1

I'm having a strange problem with ASP.Net Core custom middleware.

I've got an exception handler that I'm trying to log exceptions and then send a generic message to the caller without the exception details that seem to be sent by default.

The problem is that the response is being sent without a body.

My code:

public async Task Invoke(HttpContext context)
{
    try
    {
        await next.Invoke(context);
    }
    catch (Exception e)
    {
        logger.LogError(e, "Request threw an exception");

        context.Response.StatusCode = 500;

        using (var writer = new StreamWriter(context.Response.Body))
        {
            await writer.WriteAsync($"Error on server processing request");
            await writer.FlushAsync();
        }
    }
}

I'm using .Net 5. This project is a Web API project, in case that makes any difference.

I'm invoking my API via swagger UI and I'm also checking the responses using the dev tools in Edge (Chromium version) to make sure that swagger isn't hiding the body.

It appears that there is no body to the response, it's just an empty response with a 500 code.

Running in the debugger shows that it is executing the code and if I change the response code to 566 that response code is received by SwaggerUI so it's doing something.

Note: before I added the FlushAsync() call, I was getting an exception sent to Swagger saying that the dispose was using a synchronous write when flushing so that seems to be necessary.

Update: Pipeline configuration, as requested:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment() || env.IsTesting())
    {
        app.UseDeveloperExceptionPage();
        app.UseSwagger();
        app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Clients v1"));
    }

    app.UseRequestLoggingMiddleware(); // My custom middleware

    app.UseHttpsRedirection();

    app.UseRouting();

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

Behaviour is the same regardless of the environment (development, testing, staging or production)

2 Answers 2

2

I'm uncertain why using context.Response.Body is not working, but what I've done instead is use the methods directly on context.Response:

context.Response.StatusCode = 500;
await context.Response.WriteAsync($"Error on server processing request");

Update: Given your middleware pipeline starts with app.UseDeveloperExceptionPage() I suspect that's what's overwriting the response - at least if you're running with dev or test as environment. This is the middleware that actually exposes the full exception details you are saying you're trying to avoid. In the ASP.NET Core project boilerplate/template this is intentionally only added when not running on a production environment.

So perhaps your problem will be solved by changing the ASPNETCORE_ENVIRONMENT environment variable to something other than Development or Test. Or if you still want your own middleware, you should probably remove app.UseDeveloperExceptionPage() and perhaps even move your own app.UseRequestLoggingMiddleware() up as the first line in Configure (although I don't think the Swagger stuff should interfere - but I make no promises :) )

Sign up to request clarification or add additional context in comments.

5 Comments

I'd read on a website that StreamWriter was the way to do it and didn't realise there was a built-in method 🤦‍♂️ However, this is also not sending the message to the caller...
@Mog0 Can you show where in your pipeline you've added the middleware? Sounds like something is overwriting your response
I've updated the question with my pipeline configuration code.
@Mog0 I updated my answer as well :) I think it's the UseDeveloperExceptionPage middleware that's messing with the response.
Thanks for your help @Xerillo - Turns out it wasn't middleware messing with the response but instead was ASP.Net itself dropping content that didn't match one of the types in the accept request header (see my answer).
1

I found the problem. The code I needed was

context.Response.StatusCode = 500;
context.Response.ContentType = "text/plain";
await context.Response.WriteAsync($"Error on server processing request");

I noticed that if I requested the API by typing the url into the web browser, I was getting the text back but when I requested via Swagger UI, it was not sending the text.

SwaggerUI was setting an accept: text/plain header with the request so ASP.Net was ignoring any content that wasn't set as this type (the ContentType is null by default).

The browser had */* in its accept header so the content was being sent regardless of type.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.