Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.Net Core: Return IActionResult from a custom Exception Middleware

I have created a new Exception middleware in my .Net Core application. All the exceptions throughout the application are captured and logged here. What I want is to return a IActionResult type like InternalServerError() or NotFound() from the Exception Middleware and not do response.WriteAsync as below.

Controller Method:

   public async Task<IActionResult> Post()
    {
        //Do Something
        return Ok();
    }

Middleware:

public class ExceptionMiddleware
    {
        private readonly RequestDelegate _next;
        public ExceptionMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            try
            {
                await _next.Invoke(context);
            }
            catch (Exception ex)
            {
                await HandleExceptionAsync(context, ex);
            }
        }

        private async Task HandleExceptionAsync(HttpContext context, Exception exception)
        {
            var response = context.Response;
            var statusCode = (int)HttpStatusCode.InternalServerError;
            var message = exception.Message;
            var description = exception.Message;

            response.ContentType = "application/json";
            response.StatusCode = statusCode;

            await response.WriteAsync(JsonConvert.SerializeObject(new ErrorResponse
            {
                Message = message,
                Description = description
            }));
        }
    }
like image 934
shaikhspear Avatar asked Dec 11 '19 21:12

shaikhspear


People also ask

What does IActionResult return?

The IActionResult return type is appropriate when multiple ActionResult return types are possible in an action. The ActionResult types represent various HTTP status codes. Any non-abstract class deriving from ActionResult qualifies as a valid return type.

How does Web API handle global exception in .NET Core?

The middleware UseExceptionHandler can be used to handle exceptions globally. You can get all the details of the exception object (Stack Trace, Inner exception, message etc..) and display them on-screen. You can implement like this.


3 Answers

IActionResult is a thing from MVC, so it is only available within the MVC pipeline (including Razor Pages). Just before the MVC middleware terminates, it will execute those action results using ExecuteResultAsync. That method is then responsible of writing that response to HttpContext.Response.

So in custom middleware, you cannot just set an action result and have it executed, since you are not running within the MVC pipeline. However, with that knowledge, you can simply execute the result yourself.

Let’s say you want to execute a NotFoundResult which is what Controller.NotFound() creates. So you create that result and call ExecuteResultAsync with an . That executor will be able to execute that result object and write to the response:

var result = new NotFoundResult();
await result.ExecuteResultAsync(new ActionContext
{
    HttpContext = context
});
like image 60
poke Avatar answered Oct 22 '22 20:10

poke


That's not really possible due to where IActionResult and middleware sit in relation to one another in the architecture. Middleware sits much lower, and so it can't reach further up the stack to IActionResult. Here's an answer that talks more about it: https://stackoverflow.com/a/43111292/12431728

like image 1
Matt U Avatar answered Oct 22 '22 20:10

Matt U


What you're trying to do can be done by simply adding this line:

app.UseStatusCodePagesWithReExecute("/Public/Error", "?statusCode={0}");

to the Configure method in the Startup.cs. Then you can create your Public Controller with Error method that does the following:

[AllowAnonymous]
public IActionResult Error(int? statusCode = null)
{
    // Retrieve error information in case of internal errors.
    var error = HttpContext.Features.Get<IExceptionHandlerFeature>()?.Error;
    var path = HttpContext.Features.Get<IExceptionHandlerPathFeature>()?.Path;

    // TODO: Redirect here based on your status code or perhaps just render different views for different status codes.
}

There is also another middleware that allows you to do a similar thing:

app.UseStatusCodePages(async context =>
{
    if (context.HttpContext.Response.StatusCode == 401)
    {
        context.HttpContext.Response.Redirect("Errors/Unauthorized/");
    }
    else if (context.HttpContext.Response.StatusCode == 500)
    {
        // TODO: Redirect for 500 and so on...
    }
 });
like image 1
Marko Avatar answered Oct 22 '22 19:10

Marko