ASP.Net Core exception handling middleware
Asked Answered
F

3

9

I am trying to use middleware for exception handling in my ASP.Net Core 3.0 Web API project:

public class ErrorHandlingMiddleware
{
    private readonly RequestDelegate next;

    public ErrorHandlingMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await next(context);
        }
        catch (Exception ex)
        {
            await HandleException(context, ex);
        }
    }

    private static Task HandleException(HttpContext context, Exception ex)
    {
        HttpStatusCode code = HttpStatusCode.InternalServerError; // 500 if unexpected

        // Specify different custom exceptions here
        if (ex is CustomException) code = HttpStatusCode.BadRequest;

        string result = JsonConvert.SerializeObject(new { error = ex.Message });

        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)code;

        return context.Response.WriteAsync(result);
    }
}

startup.cs

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        // Add controllers with a route prefix
        services.AddControllers(x => { x.UseGeneralRoutePrefix($"api/v{Configuration["APIVersion"]}"); });
        services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v0.1", new OpenApiInfo { Title = "My API", Version = "v0.1" });
        });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();

        app.UseSwagger();
        app.UseSwaggerUI(c =>
        {
            c.SwaggerEndpoint("/swagger/v0.1/swagger.json", "My API V1");
        });

        app.UseMiddleware(typeof(ErrorHandlingMiddleware));

        app.UseRouting();

        app.UseAuthorization();

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

Controller

[HttpPost]
public ActionResult Method()
{
    if (condition)
        throw new CustomException();
    else
        return Ok();
}

but the exception thrown in the controller doesn't get handled by the middleware. What would be the correct way to use the middleware?

Feuchtwanger answered 12/11, 2019 at 15:2 Comment(5)
So what does happen? What do you see?Fahy
@KirkLarkin If the condition is not satisfied, the exception is thrown and the app just crashed. The HandleException() method from the middleware is never called.Feuchtwanger
Do you see the result of UseDeveloperExceptionPage kicking in? A nice HTML response showing the error, its stack-trace, etc?Fahy
Error handling middleware should be registered as early in the pipeline as possible.Chaldea
Reference Handle errors in ASP.NET CoreChaldea
F
12

It seems that

app.UseDeveloperExceptionPage();

was preventing the exception handling middleware from catching exceptions. Removing it solves the problem.

Feuchtwanger answered 12/11, 2019 at 15:30 Comment(2)
That makes sense, guessing you were trying to debug this locally with Environment Variable set to 'Development'. Probably best to wrap this in a conditional. I think it's actually like that OOTB.Nazario
This dug me out of a whole was trying to hit brake points but couldnt cause the databasse page was getting in the wayLarrylars
S
1
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseCors(ApiConstantVars.AZURE_POLICY);
        app.UseCors(ApiConstantVars.CORS_POLICY_NAME);
        app.UseAuthentication();
        app.UseMvc();

        app.Run(
               async context =>
               {
                   var handler = context.Features.Get<Microsoft.AspNetCore.Diagnostics.IExceptionHandlerFeature>();
                   if (handler != null && handler.Error != null)
                   {
                       var errorModel = new OperationResult
                       {
                           IsSuccess = false,
                       };
                        //Handle the JSON request to respond in its origin format
                       if (context.Request.ContentType.ToUpper().IndexOf("JSON") != -1)
                       {
                           context.Response.ContentType = "application/json";
                           context.Response.StatusCode = 400; //Set the Status Code to responde JSON Failed requests
                           await context.Response
                           .WriteAsync(Newtonsoft.Json.JsonConvert.SerializeObject(new { error = "Unhandled internal error", success = false, origin = context.Request.Path }))
                           .ConfigureAwait(false);
                       }
                       else
                       {
                           context.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
                           await context.Response.WriteAsync(Newtonsoft.Json.JsonConvert.SerializeObject(handler.Error.Message))
                               .ConfigureAwait(false);
                       }
                   }
                   else
                   {
                       context.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
                       await context.Response
                           .WriteAsync(Newtonsoft.Json.JsonConvert.SerializeObject(new { message = "Unhandled internal error" }))
                           .ConfigureAwait(false);
                   }
               });
    }
Sanches answered 12/11, 2019 at 15:59 Comment(0)
M
1
app.UseMiddleware<ErrorHandlingMiddleware>();

I think you should use it this way. Add this line to the Configure method

Margit answered 21/3, 2020 at 22:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.