ASP.NET Core Web API Exception Handling

ASP.NET Core Web API exception handling

Quick and Easy Exception Handling

Simply add this middleware before ASP.NET routing into your middleware registrations.

app.UseExceptionHandler(c => c.Run(async context =>
{
var exception = context.Features
.Get<IExceptionHandlerPathFeature>()
.Error;
var response = new { error = exception.Message };
await context.Response.WriteAsJsonAsync(response);
}));
app.UseMvc(); // or .UseRouting() or .UseEndpoints()

Done!



Enable Dependency Injection for logging and other purposes

Step 1. In your startup, register your exception handling route:

// It should be one of your very first registrations
app.UseExceptionHandler("/error"); // Add this
app.UseEndpoints(endpoints => endpoints.MapControllers());

Step 2. Create controller that will handle all exceptions and produce error response:

[AllowAnonymous]
[ApiExplorerSettings(IgnoreApi = true)]
public class ErrorsController : ControllerBase
{
[Route("error")]
public MyErrorResponse Error()
{
var context = HttpContext.Features.Get<IExceptionHandlerFeature>();
var exception = context.Error; // Your exception
var code = 500; // Internal Server Error by default

if (exception is MyNotFoundException) code = 404; // Not Found
else if (exception is MyUnauthException) code = 401; // Unauthorized
else if (exception is MyException) code = 400; // Bad Request

Response.StatusCode = code; // You can use HttpStatusCode enum instead

return new MyErrorResponse(exception); // Your error model
}
}

A few important notes and observations:

  • You can inject your dependencies into the Controller's constructor.
  • [ApiExplorerSettings(IgnoreApi = true)] is needed. Otherwise, it may break your Swashbuckle swagger
  • Again, app.UseExceptionHandler("/error"); has to be one of the very top registrations in your Startup Configure(...) method. It's probably safe to place it at the top of the method.
  • The path in app.UseExceptionHandler("/error") and in controller [Route("error")] should be the same, to allow the controller handle exceptions redirected from exception handler middleware.

Here is the link to official Microsoft documentation.



Response model ideas.

Implement your own response model and exceptions.
This example is just a good starting point. Every service would need to handle exceptions in its own way. With the described approach you have full flexibility and control over handling exceptions and returning the right response from your service.

An example of error response model (just to give you some ideas):

public class MyErrorResponse
{
public string Type { get; set; }
public string Message { get; set; }
public string StackTrace { get; set; }

public MyErrorResponse(Exception ex)
{
Type = ex.GetType().Name;
Message = ex.Message;
StackTrace = ex.ToString();
}
}

For simpler services, you might want to implement http status code exception that would look like this:

public class HttpStatusException : Exception
{
public HttpStatusCode Status { get; private set; }

public HttpStatusException(HttpStatusCode status, string msg) : base(msg)
{
Status = status;
}
}

This can be thrown from anywhere this way:

throw new HttpStatusCodeException(HttpStatusCode.NotFound, "User not found");

Then your handling code could be simplified to just this:

if (exception is HttpStatusException httpException)
{
code = (int) httpException.Status;
}

HttpContext.Features.Get<IExceptionHandlerFeature>() WAT?

ASP.NET Core developers embraced the concept of middlewares where different aspects of functionality such as Auth, MVC, Swagger etc. are separated and executed sequentially in the request processing pipeline. Each middleware has access to request context and can write into the response if needed. Taking exception handling out of MVC makes sense if it's important to handle errors from non-MVC middlewares the same way as MVC exceptions, which I find is very common in real world apps. So because built-in exception handling middleware is not a part of MVC, MVC itself knows nothing about it and vice versa, exception handling middleware doesn't really know where the exception is coming from, besides of course it knows that it happened somewhere down the pipe of request execution. But both may needed to be "connected" with one another. So when exception is not caught anywhere, exception handling middleware catches it and re-runs the pipeline for a route, registered in it. This is how you can "pass" exception handling back to MVC with consistent content negotiation or some other middleware if you wish. The exception itself is extracted from the common middleware context. Looks funny but gets the job done :).

ASP.NET Core Web API try catch exception question

IF

you plan to handle each exception separately - your approach is the way to go. I suggest to use this "ugly" code simply because it is more readable. If all your exceptions have common handling (for example logging) - you can use only catch (Exception e) and call your logging methods. This will work for all types of exceptions.

OR

If you decide that some of your exceptions might have common handling - you can go with:

try 
{
// do
}
catch (Exception e)
{
if (e is BadRequestException ||
e is HttpRequestException ||
e is TimeoutRejectedException )
{
// Log exception
}
}

OR

A good approach is to use a delegate for exception handling. Since you're going to log exceptions, the delegate will handle this.

Action<Exception> HandleError = (e) => {
// Log exception
};

catch (UnauthorizedAccessException e) { HandleError(e); }
catch (BadRequestException e) { HandleError(e); }
catch (HttpRequestException e) { HandleError(e); }

OR

You can combine the first and the second approach

if (e is BadRequestException || 
e is HttpRequestException ||
e is TimeoutRejectedException )
{
HandleError(e);
}

.Net Core Handle exceptions that returned from web api

According to your description, I suggest you could use try catch on the server-side to capture the exception and return as a json response.

In the client side, you could use deserlize the response and create a new view named Error to show the response message.

More details, you could refer to below codes:

Error Class:

public class APIError
{
public string Version { get; set; }
public string StatusCode { get; set; }
public string ErrorMessage { get; set; }
}

API:

[HttpGet]
public IActionResult Get()
{
try
{
throw new Exception("UserNotFound");
}
catch (Exception e)
{

return Ok(new APIError { Version="1.0", ErrorMessage=e.Message, StatusCode="500" });
}


}

Application:

       var request = new HttpRequestMessage(HttpMethod.Get,
"https://localhost:44371/weatherforecast");


var client = _clientFactory.CreateClient();

var response = await client.SendAsync(request);

if (response.IsSuccessStatusCode)
{
var responseStream = await response.Content.ReadAsStringAsync();
APIError re = JsonSerializer.Deserialize<APIError>(responseStream, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
});

if (re.StatusCode == "500")
{

return View("Error", new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier, Version = re.Version, StatusCode = re.StatusCode, ErrorMessage = re.ErrorMessage });

}


}
else
{
// Hanlde if request failed issue
}

Notice: I created a new Error view, you could create it by yourself or modify the default error view.

Error Viewmodel:

public class ErrorViewModel
{
public string RequestId { get; set; }

public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

public string Version { get; set; }
public string StatusCode { get; set; }
public string ErrorMessage { get; set; }
}

Error view:

@model ErrorViewModel
@{
ViewData["Title"] = "Error";
}

<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>

@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}

<h3>@Model.StatusCode</h3>
<p>
@Model.ErrorMessage
</p>

Result:

Sample Image

ASP.NET Core Web API how to catch the 401 and 403 errors using the try catch method

As per Handle errors in ASP.NET Core, you can use UseStatusCodePages:

app.UseStatusCodePages(async statusCodeContext =>
{
switch (statusCodeContext.HttpContext.Response.StatusCode)
{
case 401:
statusCodeContext.HttpContext.Response.StatusCode = 400;
await statusCodeContext.HttpContext.Response.WriteAsJsonAsync(new ErrorMessage { httpStatus = 500, Message = "some message" });
break;
case 403:
statusCodeContext.HttpContext.Response.StatusCode = 400;
await statusCodeContext.HttpContext.Response.WriteAsJsonAsync(new ErrorMessage { httpStatus = 500, Message = "some message" });
break;
}
});

ASP.NET Core Web API - Exception is not showing immediately in Visual Studio

you need to set the Visual Studio like this:

Sample Image

link.

Exception Handling in ASP.NET Web API giving me errors after HttpRequestException

I think that you are looking for is HttpResponseException and not HttpRequestException. If so, you can do it as below:

throw new HttpResponseException(HttpStatusCode.NotFound);

This works, because as you can see in documentation, there is a constructor for that class that takes one parameter of type HttpStatusCode. On the other hand, as you can see here, this is not true for HttpRequestException.

Global Error Handling in ASP.NET Core MVC

It seems that you wish to redirect the browser to an error page.

To do this you'll need to replace:

context.Request.Path = $"/error/{response.StatusCode}";

With

context.Reponse.Redirect($"/error/{response.StatusCode}");

Also, since you're sending a redirect, the response content needs to be empty, so remove the response.WriteAsync bit too.

var result = JsonSerializer.Serialize(new { message = error?.Message });
await response.WriteAsync(result);


Related Topics



Leave a reply



Submit