'Best practicies global result - error handling in Asp.Net core 3 and Angular 9

According to Microsoft's recommendation, throwing and catching should not be used for the normal logic of the program. Minimize exceptions

As part of a ASP.Net core clean architecture project (with 3 Layers Generic Repositories - BL Services - Controllers), how should the error handling and the results be designed and implemented? Should a struct or a global result class be used for all Api Controllers and BL services? Is it enough if the errors and the results are encapsulated in a struct?

Example of result class in the WebApi project:

public class ExampleResult<T> 
{
      public ExampleResult(T value, string message, bool success)
      {
         (...)
      } 
}

Controller:

public ActionResult<ExampleResult<NewResourceDto>> Post([FromBody] NewResourceDto myNewResource)
{
    try
    {
         if(!Validate(myNewResource))
             return new ExampleResult(null, "some business logic validate failed", true);

        ExampleResult result = _service.TrySaveMyNewResource(myNewResource);
        return result;
    }
    catch (Exception ex)
    {
        // Log the exception here...
        return new ExampleResult(null, "some message" + ex, false);
    }
}

The Angular Client then validates if the value is null and/or whether the success is true or false. The message contains the error messages. The http status will be 200 (no matter if success or not).

How are the exceptions minimized elegantly?



Solution 1:[1]

  • targeting the best practice in .Net Core or any other framework you need to return a common model of all of your apis that holds all the date returned from your api in case if it's a result or an error then in your angular service you should check on your returned object keys which is your base model.

     public class ErrorModel
      {
        public ErrorModel()
         {
           ErrorMessages = new List<string>();
         }
       public List<string> ErrorMessages { get; set; }
       public Exception Exception { get; set; }
     }
    
    public class BaseModel
     {
       public BaseModel()
        {
           Error = new ErrorModel();
        }
       public ErrorModel Error { get; set; }
    }
    
    
    public class BaseModel<T>: BaseModel
     {
      public BaseModel()
       {
          Error = new ErrorModel();
       }
    
     public bool HasError => Error.ErrorMessages.Count > 0 || Error.Exception != null;
    
      public T Result { get; set; }
    }
    
  • then your api should look like that

     public ActionResult<BaseModel<dynamic>> Post([FromBody] NewResourceDto myNewResource)
         {
    
          try
           {
              ExampleResult result = _service.TrySaveMyNewResource(myNewResource);
              return OK( new BaseModel<dynamic>()
              {
                  Result=result
              });
           }
         catch (Exception ex)
         {
            return StatusCode(StatusCodes.Status500InternalServerError, new BaseModel<dynamic>()
             {
                Error = new ErrorModel()
                {
                    ErrorMessages = new List<string>()
                    {
                        ex.Message,
                        "your message 2",
                         "your message 3"
                    },
                    Exception = ex
                }
            });
           }
       }
    
    • then in your angluar service you shold check on your response.hasError and displays your data according to it.

Solution 2:[2]

I agree that throwing Exceptions should not be used as signaling in the system. Maybe I don't understand your question about the returning a struct or a global result class. Seems like a bad idea. Especially, don't return HTTP OK 200 if something goes south.

  • Keep your Web API controllers as thin and dumb as possible
  • Wrap your Web API controller method methods in a try-catch so you always return HTTP Internal Server Error 500 on an unexpected error

Example of a controller method:

public IActionResult Post([FromBody] NewResourceDto myNewResource)
{
    try
    {
        _service.TrySaveMyNewResource(myNewResource);

        return StatusCode(201);
    }
    catch (Exception ex)
    {
        // Log the exception here...
        return StatusCode(500);
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Jonas Sackerud Fahlén