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.
Related
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));
})
Using .net core web api here.
I have a endpoint in my api as:
[HttpPost("data")]
public async Task<IActionResult> PostData(List<string> udata)
{
JArray sets = new JArray();
try
{
sets = Helper.GetData(udata);
}
catch (Exception e)
{
return StatusCode(500, e.Message);
}
}
In the above I am calling an custom method in my helper "GetData" which does all the processing and calculations. To make the Api controller clean I moved all the processing to this helper method.
Below is my helper class:
public static class Helper
{
public static BadRequestObjectResult GetMessage(string message)
{
return new BadRequestObjectResult(message);
}
public static JArray GetData(List<string> udata)
{
if(udata == null)
return GetMessage("Data is null");
//do some processing and calclulations here
//return BadRequest if some issue
}
}
If there is some processing issue or some of the data is not as intended or some other issue I want to throw BadRequest. For this I made a custom method to do so "BadRequestObjectResult".
Now my issue is, if there is an issue in GetData it doesnt throws back to my api or exits from my loop. It just continues to next statement.
I know there is some issue by the way I am returning this but not able to find the issue.
Can anyone point out the correct way to handle this?
My suggestion is to throw an exception from your Helper class and and handle it from your PostData method. For example...
You could throw an ArgumentException and explicitly catch that from your API method.
public static class Helper
{
public static JArray GetData(List<string> udata)
{
if(udata == null)
throw new ArgumentException("Data is null");
//do some processing and calclulations here
//throw ArgumentException if there is an issue
}
}
[HttpPost("data")]
public async Task<IActionResult> PostData(List<string> udata)
{
JArray sets = new JArray();
try
{
sets = Helper.GetData(udata);
return Ok(sets);
}
catch (ArgumentException e)
{
return BadRequest(e.Message);
}
}
This way you can only worry about return codes from your controller while your Helper methods are only concerned with the input and aren't returning something specialized for a controller. It's a bit more flexible that way if you ever want to use your Helper class elsewhere.
This will also meet your requirement of stopping processing upon hitting a bad result, so as soon as a bad result is encountered the result set is thrown away and a BadRequest response is issued.
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!
Forgive me for my ignorance but at the moment I'm struggling to figure out the best approach to catch an exception and display a message to the client based on an exception type.
My architecture:
Repository
Page IPageRepository.FindDefault()
{
try
{
return MapPageFromCategory.MapFromEntity(_context.tbl_Category.
FirstOrDefault(p =>
p.IsLandingPage == true
)
);
}
catch (Exception ex)
{
throw new ApplicationException("Error getting values from database :", ex);
}
}
With the above as you can see it just fetches data from the DB, but in some scenarios I could get a object not set to an instance of an object exception due to physically no data in the table or a Null exception depending on the data passed in.
Now in both scenario I would like to generate a 404 exception which passes to my controller.
Controller
public class PageController : ApiController
{
private IPageRepository _pageRepository;
public PageController(IPageRepository pageRepository)
{
this._pageRepository = pageRepository;
}
// GET
[HttpGet]
[Route("")]
public Page Get()
{
return this._pageRepository.FindDefault();
}
}
My Controller hit's the method and if any of those exceptions are hit, what would be the best approach to intercept those exceptions and pass to the end client (Calling app) ?
Again apologies for the question, really trying to create some sort of systematic approach to exception handling.
Thanks for your time!
You can either have a separate try/catch in the Controller class and handle the exception thrown from repository there or do something different.
You can create a class similiar to this:
public class Result
{
public bool Succeded { get; private set; }
protected Result(bool succeeded)
{
Succeded = succeeded;
}
}
public class Result<TData> : Result
{
public TData Data { get; private set; }
protected Result(bool succeeded, TData data) : base(succeeded)
{
Data = data;
}
}
Then, you can return this from your repository and check in the controller if the call has succeeded. If not, throw a desired exception, which will set StatusCode accordingly.
I'm having troubles handling all types of errors in ASP.NET WebAPI.
I've successfully handled exceptions thrown inside my action methods using an ExceptionFilter and 404 errors for invalid routes, invalid controller or action name. However, I'm struggling to handle the error where the controller and action are both found, but the parameters for model binding are incorrect types.
Here's my action, which is routed to /api/users/{id}.
[HttpGet]
public virtual TPoco Get(long id)
{
...
}
If I request the URL /api/users/notinteger, I get a 400 Bad Request error that is handled outside of my code:
{
Message: "The request is invalid.",
MessageDetail: "The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int64' for method '___ Get(Int64)' in '___'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter."
}
How can I intercept this error and respond with my own error message? Preferably not in the controller itself as I'd like to handle several controllers in the same way.
I've tried using global.asax.cs's Application_Error event as per this question, but that doesn't seem to be called in this case.
It appears that these errors are added to the ModelState as model binding errors. The action selector selects the correct action and the action invoker invokes it without throwing any errors.
The workaround I came up with is to create an action invoker that checks the ModelState for errors. If it finds any, it passes the first one to the exception handling method used by my ExceptionFilter and ErrorController.
internal class ThrowModelStateErrorsActionInvoker : ApiControllerActionInvoker
{
public override Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
foreach (var error in actionContext.ModelState.SelectMany(kvp => kvp.Value.Errors))
{
var exception = error.Exception ?? new ArgumentException(error.ErrorMessage);
//invoke global exception handling
}
return base.InvokeActionAsync(actionContext, cancellationToken);
}
}
It's nasty, but it works. This has taken up most of my day and I'm just glad to have finally got somewhere.
I'd be interested to know what the consequences are to this. What else uses the ModelState errors in Web API? Could anyone add some possible flaws in this solution?
It will more better if you use the new WebApi 2.1 Global error handling as discussed here,
http://aspnetwebstack.codeplex.com/wikipage?title=Global%20Error%20Handling&referringTitle=Specs
http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-21#global-error
If you are not willing to use WebApi 2.1 for a valid reason then you can try. (Note I have not tested but you can try). Create a custom action descriptor by inheriting with ReflectedHttpActionDescriptor and handle ExecuteAsync. This is what I mean,
public class HttpNotFoundActionDescriptor : ReflectedHttpActionDescriptor
{
ReflectedHttpActionDescriptor _descriptor;
public HttpNotFoundActionDescriptor(ReflectedHttpActionDescriptor descriptor)
{
_descriptor = descriptor;
}
public override Task<object> ExecuteAsync(HttpControllerContext controllerContext, IDictionary<string, object> arguments, CancellationToken cancellationToken)
{
try
{
return descriptor.ExecuteAsync(controllerContext, arguments, cancellationToken);
}
catch (HttpResponseException ex)
{
//..........................
}
}
}
public class HttpNotFoundAwareControllerActionSelector : ApiControllerActionSelector
{
public HttpNotFoundAwareControllerActionSelector()
{
}
public override HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
{
HttpActionDescriptor decriptor = null;
try
{
decriptor = base.SelectAction(controllerContext);
}
catch (HttpResponseException ex)
{
var code = ex.Response.StatusCode;
if (code != HttpStatusCode.NotFound && code != HttpStatusCode.MethodNotAllowed)
throw;
var routeData = controllerContext.RouteData;
routeData.Values["action"] = "Handle404";
IHttpController httpController = new ErrorController();
controllerContext.Controller = httpController;
controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "Error", httpController.GetType());
decriptor = base.SelectAction(controllerContext);
}
return new HttpNotFoundActionDescriptor(decriptor);
}
}
Note you need to override all the virtual methods.