Map custom exceptions to HTTP exceptions at one place - c#

In my .NET Core Web API project many controller endpoints have code like this example
public async Task<ActionResult<User>> UpdateUserUsernameAsync(/* DTOs */)
{
try
{
User user = null; // Update user here
return Ok(user);
}
catch (EntityNotFoundException entityNotFoundException) // map not found to 404
{
return NotFound(entityNotFoundException.Message);
}
catch (EntityAlreadyExistsException entityAlreadyExistsException) // map duplicate name to 409
{
return Conflict(entityAlreadyExistsException.Message);
}
catch (Exception exception) // map any other errors to 500
{
return new StatusCodeResult(StatusCodes.Status500InternalServerError);
}
}
I would like to create a mapping for the controllers that catches exceptions and maps them to HTTP responses before sending them back to the client.
A similiar question has been asked 4 years ago
ASP.NET Core Web API exception handling
In NestJs it's possible to define your own mappings by extending a base class e.g.
export class MyCustomException extends HttpException {
constructor() {
super('My custom error message', HttpStatus.FORBIDDEN);
}
}
Taken from here https://docs.nestjs.com/exception-filters#custom-exceptions
So basically I want to define mapping classes that could look like this sample code (this just shows my pseudo implementation)
// Map MyCustomException to NotFound
public class MyCustomExceptionMapping<TCustomException> : IExceptionMapping<TCustomException>
{
public ActionResult Map(TCustomException exception)
{
return NotFound(exception.Message);
}
}
Next I can cleanup the controller endpoint method to
public async Task<ActionResult<User>> UpdateUserUsernameAsync(/* DTOs */)
{
User user = null; // Update user here
return Ok(user);
}
Whenever an exception gets thrown the API would try to find the correct mapping interface. Otherwise it sends back a 500.
It would be nice to define such mappings and avoid a huge switch case for every exception in your project.
Does something like this exists? Is the accepted answer from the previous question still up to date?

Use Exception Filters it will be called when the controller throws an Exception and you can define the custom response. Microsoft Docs
public class MyExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
HttpStatusCode status;
var message = "";
var exceptionType = context.Exception.GetType();
if (exceptionType is EntityNotFoundException)
{
status = HttpStatusCode.NotFound;
message = context.Exception.Message;
}
else if (exceptionType is EntityAlreadyExistsException)
{
status = HttpStatusCode.Conflict;
message = context.Exception.Message;
}
else
{
status = HttpStatusCode.InternalServerError;
message = "Internal Server Error.";
}
//You can enable logging error
context.ExceptionHandled = true;
HttpResponse response = context.HttpContext.Response;
response.StatusCode = (int)status;
response.ContentType = "application/json";
context.Result = new ObjectResult(new ApiResponse { Message = message, Data = null });
}
}
To use the filter on all controllers you must register it in the ConfigureServices method in the Startup.cs
services.AddMvc(config =>
{
config.Filters.Add(typeof(MyExceptionFilter));
})

Related

Return specific error based on exception in ASP.NET Core?

Consider a controller endpoint like this:
[HttpGet]
public IActionResult Get()
{
var resource = _myService.GetSomeResource();
return Ok(resource);
}
The GetSomeResource() method performs validation on the identity of the caller, like this:
public SomeResourceModel GetSomeResource()
{
_securityVerifier.VerifyCallerHasAccess();
// do stuff to get resource
return resource;
}
Within the VerifyCallerHasAccess() method, let's pretend that the caller does NOT have access to the resource they requested. So, we throw an exception. Something like:
throw new SecurityException($"User does not have access to this resource");
With all that said, what I want is for the ASP.NET controller to automatically return a 403 when an exception of that type is encountered. By default, it's just returning a 500. Is it possible to register http response codes to specific types of exceptions? If so, how?
(Note, I know I can wrap my endpoint code in a try/catch, but I don't want to have to manually do this)
Edit for some clarity: My endpoint is already using .NET's authorization system. The security verification in question deals with getting the user's identity and checking against an external security service that they have the appropriate access rights to a given security resource.
You could use some middleware in the request pipeline:
public class ErrorHandlingMiddleware
{
readonly RequestDelegate _next;
public ErrorHandlingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (SecurityException ex)
{
context.Response.StatusCode = 403;
}
catch (Exception ex)
{
context.Response.StatusCode = 500;
await context.Response.WriteAsync(ex.Message);
}
}
}
and then register that in Program.cs:
app.UseMiddleware<ErrorHandlingMiddleware>();
If you could modify VerifyCallerHasAccess() method,you could return (or set some property) true or false instead of throw an exception
in controller,you could try as below:
if (result)            
{                
return Ok();            
}            
else            
{                
return Problem(detail: "User does not have access to the resource", statusCode: 403);                
// you could also try return Forbind() to return a 403 with out  the detail
//check the controllerbase class and you would find more methods related            
}
if you insist on throw exception, then put
var resource = _myService.GetSomeResource();
return Ok(resource)
into try catch block ,return 403 with Forbind() method or other methods you would find in controllerbase class
You can write an exception filter (see https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-7.0) or if you want to standardize your responses as well, take a look at Hellang.Middleware.ProblemDetails

Returning meaningful error messages from a .NET Core API

Scenario
I have a .NET Core 2.2 web API with an exception handling middleware. Whenever an exception occurs in the application (inside the MVC layer) it gets caught by the exception middleware and returned as an internal server error back to the frontend and logged to kibana.
The problem
This is all fine and well when things go wrong, but sometimes I want to notify the calling application of specifically what went wrong. I.e., "Could not find record in database!" or "Failed to convert this to that!"
My Solution
I've used application Exceptions (not great - I know) to piggy back off the error middleware to return this to the frontend. This has been working fine, but has created a lot of noise around the code by having to throw a whole bunch of exceptions. I'm not satisfied with this approach and convinced that there must be a better solution.
My application architecture: I'm following a traditional n-tier application layout being services (business logic) and repositories (DAL) all speaking to each other. I would preferably like to elegantly bubble up any issues back to the user in any of these layers.
I've been thinking about this for a while now and am not sure what the best way to go about it is. Any advice would be appreciated.
I use a kind of the operation result pattern (non-official pattern).
The principle is to return a new Type containing:
Whether the operation was a success.
The result of the operation if was successful.
Details about the Exception that caused the failure.
Consider the following class:
public class OperationResult
{
protected OperationResult()
{
this.Success = true;
}
protected OperationResult(string message)
{
this.Success = false;
this.FailureMessage = message;
}
protected OperationResult(Exception ex)
{
this.Success = false;
this.Exception = ex;
}
public bool Success { get; protected set; }
public string FailureMessage { get; protected set; }
public Exception Exception { get; protected set; }
public static OperationResult SuccessResult()
{
return new OperationResult();
}
public static OperationResult FailureResult(string message)
{
return new OperationResult(message);
}
public static OperationResult ExceptionResult(Exception ex)
{
return new OperationResult(ex);
}
public bool IsException()
{
return this.Exception != null;
}
}
Then you could easily adapt OperationResult or create a class that inherits from OperationResult, but uses a generic type parameter.
Some examples:
The Operation Result Pattern — A Simple Guide
Error Handling in SOLID C# .NET – The Operation Result Approach
As per the Microsoft's standards, it is ideal to use ProblemDetails object in case of 4xx/5xx exceptions -
Following is the customised RequestDelegate method which you can use in ApiExceptionHandler to handle exceptions.
public async Task RequestDelegate(HttpContext context)
{
var exception = context.Features.Get<IExceptionHandlerFeature>().Error;
var problemDetails = new ProblemDetails
{
Title = "An unexpected error occurred!",
Status = GetStatusCode(exception),
Detail = _env.IsDevelopment() ? exception.Message : "An unexpected error occurred!",
Instance = $"{Environment.MachineName}:{context.TraceIdentifier}:{Guid.NewGuid()}"
};
_logger.LogError($"Exception thrown. StatusCode: {problemDetails.Status}. Instance: {problemDetails.Instance}", exception);
context.Response.StatusCode = problemDetails.Status.Value;
context.Response.WriteJson(problemDetails, "application/problem + json");
await Task.CompletedTask;
}

Web API 2 services - how to return an Exception message in the Status

Is it possible, in Web API 2 to directly return the Exception message in the response's Status ?
For example, if I was writing a WCF Service (rather than Webi API), I could follow this tutorial to directly return an Exception message as part of the response status:
Here, the web service doesn't return any data in the Response, and the error message gets returned directly in the Status Description.
This is exactly what I'd like my Web API services to do when an exception occurs, but I can't work out how to do it.
Most suggestions suggest using code like below, but then the error message will then always get returned in a separate response string, rather than being part of the Status.
For example, if I were to use this code:
public IHttpActionResult GetAllProducts()
{
try
{
// Let's get our service to throw an Exception
throw new Exception("Something went wrong");
return Ok(products);
}
catch (Exception ex)
{
return new System.Web.Http.Results.ResponseMessageResult(
Request.CreateErrorResponse((HttpStatusCode)500,
new HttpError("Something went wrong")));
}
}
... then it returns a generic 500 message, and the exception is returned in a JSON string.
Does anyone know how to modify a Web API function (which returns an IHttpActionResult object) to do this ?
You could register a custom global filter that will handle all Exceptions. Something like:
public class CatchAllExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
context.Response = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(context.Exception.Message)
};
}
}
You will need to register it in WebApiConfig.cs, with:
config.Filters.Add(new CatchAllExceptionFilterAttribute());
This filter will be hit everytime there is an unhandled exception in the system and set the http response to the exception message. You could also check the different types of exception and alter your response accordingly, for example:
public override void OnException(HttpActionExecutedContext context)
{
if(context.Exception is NotImplementedException)
{
context.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented)
{
Content = new StringContent("Method not implemented.")
};
}
else
{
context.Response = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(context.Exception.Message)
};
}
}
https://www.asp.net/web-api/overview/error-handling/web-api-global-error-handling
Please refer above link, it will help you!

C# using db context and exception handling in a better way (DRY)

I am writing this piece of code in the given action of WEP API controller in all my action methods in web API controllers I am making resulting in a lot of code redundancy and repeating my self (!DRY) hence i need a way this can be done with a better approach using inheritance or iterface or by using any other object oriented approach.
[Route("myApiGetRequest")]
[HttpGet]
public IHttpActionResult myApiGetRequest(long id)
{
try
{
DataTable transaction = new DataTable();
using (var context = new myDbContext())
{
//code for what I want to do
}
}
catch (Exception ex)
{
return Ok(new { success = false, message = ex.Message });
}
}
You can take advantage of exception filters which are available in WebApi. Then, move exception handling code to that filter.
For instance:
public class MyExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Exception is Exception)
{
context.Response = new HttpResponseMessage { Content = new StringContent("Exception occured", System.Text.Encoding.UTF8, "text/plain"), StatusCode = HttpStatusCode.InternalServerError};
}
}
}
And then in your WebApi controller you can do following:
[Route("myApiGetRequest")]
[HttpGet]
[MyExceptionFilter]
public IHttpActionResult myApiGetRequest(long id)
{
DataTable transaction = new DataTable();
using (var context = new myDbContext())
{
//code for what I want to do
}
}
Exceptions which then will occur in your myApiGetRequest will be handled by corresponding exception filter which this method is decorated
ExceptionHandling can be done by implementing your own ExceptionFilterAttribute. You can use this attribute to decorate all controllers which need an exception handling.
Basiclly, you override the OnException method and convert different exception types into status code and message.

Exception handling in Web API

In my Web API project, I created sub projects (class libraries) where I handle actual data handling operations. My backend database is DocumentDB.
My question is how do I tell my Web API action methods of any errors I may encounter within data methods in my class libraries? Once my Web API method knows about the error, I can just return Http status 500 or something like that but I'm not sure what I should have in the catch part (see below) and how I can notify the calling Web API method of the error encountered?
--- Web API Method ---
public async Task<IHttpActionResult> DoSomething(Employee emp)
{
var employeeRecord = await MyClassLibrary.DoSomethingWithEmployee(emp);
// Here, I want to check for errors
}
--- Class Library Code ---
public static async Task<Employee> DoSomethingWithEmployee(Employee emp)
{
try
{
// Logic here to call DocumentDB and create employee document
}
catch
{
// This is where I catch the error but how do I notify the calling Web API method that there was an error?
}
}
ASP.NET Web API 2.1 have framework support for global handling of unhandled exceptions.
It allows use to customize the HTTP response that is sent when an unhandled application exception occurs.
So, do not catch exception in Class Library. If you are required to log exception in Class Library, then re-throw those exception to Presentation.
WebApiConfig
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// ...
config.Services.Replace(typeof (IExceptionHandler),
new GlobalExceptionHandler());
}
}
GlobalExceptionHandler
public class GlobalExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
var exception = context.Exception;
var httpException = exception as HttpException;
if (httpException != null)
{
context.Result = new CustomErrorResult(context.Request,
(HttpStatusCode) httpException.GetHttpCode(),
httpException.Message);
return;
}
// Return HttpStatusCode for other types of exception.
context.Result = new CustomErrorResult(context.Request,
HttpStatusCode.InternalServerError,
exception.Message);
}
}
CustomErrorResult
public class CustomErrorResult : IHttpActionResult
{
private readonly string _errorMessage;
private readonly HttpRequestMessage _requestMessage;
private readonly HttpStatusCode _statusCode;
public CustomErrorResult(HttpRequestMessage requestMessage,
HttpStatusCode statusCode, string errorMessage)
{
_requestMessage = requestMessage;
_statusCode = statusCode;
_errorMessage = errorMessage;
}
public Task<HttpResponseMessage> ExecuteAsync(
CancellationToken cancellationToken)
{
return Task.FromResult(_requestMessage.CreateErrorResponse(
_statusCode, _errorMessage));
}
}
Credit to ASP.NET Web API 2: Building a REST Service from Start to Finish, and source code.
The error handling depends on your logic and how your API respond to its consumers.
Basically, you have to use HTTP Status Codes according to the type of error.
In your data access and business layer methods, you can depend on the return type. For example, in all methods that queries the database, if the object is not there, you can return NULL, and in your web API, if the method returns NULL, then simply return NotFound() which will respond to the client with a 404.
As for the exceptions:
You can use Error Codes in your business and data access layer and check for these codes in your web API actions. Then return a suitable status code accordingly. Ex: return a status code of 500 if there has been a connection issue to the database, or return a 400 (Bad Request) if the user didn't provide all required action parameters in the correct format.
In case of any other exception that you didn't catch, you can go with the global exception handler described by #Win
If you want to intercept and log the error in your console application but still forward the error to the caller, just use throw; at the end of your catch statement in your console application.
It will rethrow the same exception to the caller, so your application can be aware of the exception in the "callee" as well as in the "caller".

Categories