2

I have an Asp.Net core 3.1 Web Api project in witch I have a lot of controllers.

almost in every controller I have a template like this:

public class SomeController:ControllerBase

{
    //injecting my service

    [HttpGet]
    [Route("")]
    public async Task<ActionResult> SomeMethod()

        {
            try
            {
               //Do some work
               return Ok(somevalue);
            }
            
            catch(ServiceException ex)
            {
              return BadRequest(ex.Message);
            }
            catch
            {
              return BadRequest();
            }
        }
}

this template is implemented in almost all the controllers.

I want to delete try...catch statements and write a base class or something to handle this try...catch statements outside my controller.

something like this I reckon:

public async Task<ActionResult> SomeMethod()

    {
       //Do some work
       
       return Ok(somevalue);
       //if any exception of ServiceException type occured i want to 
       //return BadRequest with message and if not a BadRequest without message
    }

I have heard something about Cross-Cutting Concerns but I don't know how to use them.

Do you have any Idea how can I do it?

Ali.Rashidi
  • 1,284
  • 4
  • 22
  • 51
  • 1
    Bad Request is an input validation response. Returning it in response to caught exceptions is telling the caller the wrong thing (unless your input validation throws exceptions). – madreflection Apr 29 '22 at 16:41
  • 1
    It sounds more like this would be suitable to move this into middleware, where you could also specify controllers that do not follow this pattern eg. through attributes. – ekke Apr 29 '22 at 16:43
  • Yes, move it to middleware, but return a 500 Internal Server Error instead of 400 Bad Request. – madreflection Apr 29 '22 at 16:49
  • @madreflection How? – Ali.Rashidi Apr 29 '22 at 17:32

2 Answers2

4

Absolutely agree, anything that has try/catch implemented everywhere is a code smell and there's a better way of doing things.

If you search for 'Global Exception Handling netcore' you'll find a lot of hits on this subject.

In general, let the exception propagate up the stack, preserve all the exception detail and let it be caught and handled by a Middleware. Probably a bit too much code to snippet into this answer, but here's decent article with a couple of different versions/examples available

https://jasonwatmore.com/post/2022/01/17/net-6-global-error-handler-tutorial-with-example

You'll notice returning responses from the middleware, and this is your opportunity to perform logging centrally as well - a single place to capture and log all your exception details.

Dylan Morley
  • 1,656
  • 12
  • 21
  • Agree with the approach suggested in this blog post. Just remember to log the exception too. Catching an exception like this and overriding the response will cause other exception-logging middleware to not get notified – ThomasArdal Apr 30 '22 at 07:53
1

A simple approach:

public static ActionResult Try(Func<ActionResult> action)
{
    try
    {
        return action();
    }
    catch (ServiceException ex)
    {
        return BadRequest(ex.Message);
    }
    catch
    {
        return BadRequest();
    }
}

And you use like:

return Try(() => Ok(someValue));

Or

return Try(() => 
{
    // Do something...
    return new EmptyResult();
});
Victor
  • 2,313
  • 2
  • 5
  • 13