I'm writing custom middleware which requires i attach the results as response headers to a given request. To do this, i've created simple middleware which looks like the following:
public class MyCustomMiddleware
{
private readonly RequestDelegate _next;
public MyCustomMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(Microsoft.AspNetCore.Http.HttpContext context, IConfiguration configuration)
{
debug.print("I'm before the next piece of middleware");
await _next(context);
Context.Response.Headers.Add("x-special-header-4", "super secret response header");
}
}
This of course results in the following exception:
Exception thrown: 'System.InvalidOperationException' in Microsoft.AspNetCore.Server.Kestrel.Core.dll System.InvalidOperationException: Headers are read-only, response has already started.
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ThrowHeadersReadOnlyException() at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.System.Collections.Generic.IDictionary<System.String,Microsoft.Extensions.Primitives.StringValues>.Add(String key, StringValues value)
This occurs when i'm using an ActionResult - but also when i've requested something that cannot be routed at all (and would as a result return a 404).
The App Startup order looks like:
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<MyCustomMiddleware>();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
(I have a helper to setup the middleware, which i've omitted from this post, but just adds the custom middleware)
The real aim of the middleware is to measure time taken / other factors, which would need me to 'complete' the request, and then add headers to the response - all without modifying existing user code.
Is there a way that this can be done?
You are add the header after calling await _next(context);
, which is too late because the response has already started. To fix this issue, you could try the httpContext.Response.OnStarting()
delegate:
public class MyCustomMiddleware
{
private readonly RequestDelegate _next;
public MyCustomMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(Microsoft.AspNetCore.Http.HttpContext context, IConfiguration configuration)
{
Debug.Print("I'm before the next piece of middleware");
context.Response.OnStarting(state =>
{
var ctx = (HttpContext)state;
ctx.Response.Headers.Add("x-special-header-4", "super secret response header");
return Task.FromResult(0);
}, context);
await _next(context);
}
}