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".
Related
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
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));
})
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;
}
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!
How do I catch all unhandled exceptions that occur in ASP.NET Web Api so that I can log them?
So far I have tried:
Create and register an ExceptionHandlingAttribute
Implement an Application_Error method in Global.asax.cs
Subscribe to AppDomain.CurrentDomain.UnhandledException
Subscribe to TaskScheduler.UnobservedTaskException
The ExceptionHandlingAttribute successfully handles exceptions that are thrown within controller action methods and action filters, but other exceptions are not handled, for example:
Exceptions thrown when an IQueryable returned by an action method fails to execute
Exceptions thrown by a message handler (i.e. HttpConfiguration.MessageHandlers)
Exceptions thrown when creating a controller instance
Basically, if an exception is going to cause a 500 Internal Server Error to be returned to the client, I want it logged. Implementing Application_Error did this job well in Web Forms and MVC - what can I use in Web Api?
This is now possible with WebAPI 2.1 (see the What's New):
Create one or more implementations of IExceptionLogger. For example:
public class TraceExceptionLogger : ExceptionLogger
{
public override void Log(ExceptionLoggerContext context)
{
Trace.TraceError(context.ExceptionContext.Exception.ToString());
}
}
Then register with your application's HttpConfiguration, inside a config callback like so:
config.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());
or directly:
GlobalConfiguration.Configuration.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());
To answer my own question, this isn't possible!
Handling all exceptions that cause internal server errors seems like a basic capability Web API should have, so I have put in a request with Microsoft for a Global error handler for Web API:
https://aspnetwebstack.codeplex.com/workitem/1001
If you agree, go to that link and vote for it!
In the meantime, the excellent article ASP.NET Web API Exception Handling shows a few different ways to catch a few different categories of error. It's more complicated than it should be, and it doesn't catch all interal server errors, but it's the best approach available today.
Update: Global error handling is now implemented and available in the nightly builds! It will be released in ASP.NET MVC v5.1. Here's how it will work: https://aspnetwebstack.codeplex.com/wikipage?title=Global%20Error%20Handling
The Yuval's answer is for customizing responses to unhandled exceptions caught by Web API, not for logging, as noted on the linked page. Refer to the When to Use section on the page for details. The logger is always called but the handler is called only when a response can be sent. In short, use the logger to log and the handler to customize the response.
By the way, I am using assembly v5.2.3 and the ExceptionHandler class does not have the HandleCore method. The equivalent, I think, is Handle. However, simply subclassing ExceptionHandler (as in Yuval's answer) does not work. In my case, I have to implement IExceptionHandler as follows.
internal class OopsExceptionHandler : IExceptionHandler
{
private readonly IExceptionHandler _innerHandler;
public OopsExceptionHandler (IExceptionHandler innerHandler)
{
if (innerHandler == null)
throw new ArgumentNullException(nameof(innerHandler));
_innerHandler = innerHandler;
}
public IExceptionHandler InnerHandler
{
get { return _innerHandler; }
}
public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
{
Handle(context);
return Task.FromResult<object>(null);
}
public void Handle(ExceptionHandlerContext context)
{
// Create your own custom result here...
// In dev, you might want to null out the result
// to display the YSOD.
// context.Result = null;
context.Result = new InternalServerErrorResult(context.Request);
}
}
Note that, unlike the logger, you register your handler by replacing the default handler, not adding.
config.Services.Replace(typeof(IExceptionHandler),
new OopsExceptionHandler(config.Services.GetExceptionHandler()));
You can also create a global exception handler by implementing the IExceptionHandler interface (or inherit the ExceptionHandler base class). It will be the last to be called in the execution chain, after all registered IExceptionLogger:
The IExceptionHandler handles all unhandled exceptions from all
controllers. This is the last in the list. If an exception occurs, the
IExceptionLogger will be called first, then the controller
ExceptionFilters and if still unhandled, the IExceptionHandler
implementation.
public class OopsExceptionHandler : ExceptionHandler
{
public override void HandleCore(ExceptionHandlerContext context)
{
context.Result = new TextPlainErrorResult
{
Request = context.ExceptionContext.Request,
Content = "Oops! Sorry! Something went wrong."
};
}
private class TextPlainErrorResult : IHttpActionResult
{
public HttpRequestMessage Request { get; set; }
public string Content { get; set; }
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpResponseMessage response =
new HttpResponseMessage(HttpStatusCode.InternalServerError);
response.Content = new StringContent(Content);
response.RequestMessage = Request;
return Task.FromResult(response);
}
}
}
More on that here.
You may have existing try-catch blocks that you're not aware of.
I thought my new global.asax.Application_Error method wasn't being consistently called for unhandled exceptions in our legacy code.
Then I found a few try-catch blocks in the middle of the call stack that called Response.Write on the Exception text. That was it. Dumped the text on the screen then killed the exception stone dead.
So the exceptions were being handled, but the handling was doing nothing useful. Once I removed those try-catch blocks the exceptions propagated to the Application_Error method as expected.