proudmonkey / AutoWrapper

A simple, yet customizable global exception handler and Http response wrapper for ASP.NET Core APIs.
MIT License
679 stars 82 forks source link

How can I wrap UnAuthorized and Forbid exceptions? #81

Closed BlazorPlate closed 3 years ago

BlazorPlate commented 3 years ago

How can I wrap UnAuthorized and Forbid exceptions?

proudmonkey commented 3 years ago

The ApiException and ApiProblemDetailsException has an overload constructor for you to pass the status code. Just set the corresponding status code for UnAuthorized and Forbidden. You can also if use the Unauthorized or Forbid object if your API returns an IActionResult.

Here's a quick example for your reference:

//Configure AutoWrapper to use ProblemDetails Exception
app.UseApiResponseAndExceptionWrapper(new AutoWrapperOptions
{
   UseApiProblemDetailsException = true
});

Then in your controller, you can do something like this:

[Route("unauth")]
[HttpGet]
public IActionResult TestUnAuthorized()
{
    return Unauthorized();
}

[Route("forbid")]
[HttpGet]
public IActionResult TestForbidden()
{
    return Forbid();
}

Here's an example using ApiException :

//Configure AutoWrapper 
app.UseApiResponseAndExceptionWrapper();
[Route("unauth")]
[HttpGet]
public IActionResult TestUnAuthorized()
{

    throw new ApiException("Access is not authorized", Status401Unauthorized);
}

[Route("forbid")]
[HttpGet]
public IActionResult TestForbidden()
{
    throw new ApiException("Access is forbidden", Status403Forbidden);
}
BlazorPlate commented 3 years ago

Thank you so much for your quick response, I really appreciate it.

This is the use case:

    **[Authorize(Roles = "Admin")]**
    public class UsersController : ApiController
    {

     }

How can I wrap the error message using Autowrapper in case the user is not authorized to access users controller?

One more question please, Can I use Autowrapper inside a service class instead of Controller class to wrap the class exceptions or it's just used only inside controllers?

proudmonkey commented 3 years ago

AutoWrapper should automatically handle unauthorized access when using the Authorize attribute.

For example, using the default settings will result to something like this:

{
  "isError": true,
  "responseException": {
    "exceptionMessage": "Request denied. Unauthorized access."
  }
}

Using Problem Details with UseApiProblemDetailsException = true will result to something like this:

{
   "isError":true,
   "type":"https://httpstatuses.com/401",
   "title":"Unauthorized",
   "status":401,
   "detail":"",
   "instance":"/api/v1/persons",
   "extensions":{}
}

Can I use Autowrapper inside a service class instead of Controller class to wrap the class exceptions

No. It's meant for ASP.NET Core APIs only. However, you can use AutoWrapper.Server to unwrap the result from AutoWrapper from any .NET clients.

BlazorPlate commented 3 years ago

Thank you for the clarification.

I tried your example with Authorize Attribute and it works as expected.

Regarding exception handling from a class library project. I'm already using AutoWrapper in a service class to catch and wrap some errors like the example below:

var result = await _roleManager.CreateAsync(role);

if (!result.Succeeded)
{
      throw new ApiProblemDetailsException(result.Errors.ToApplicationError());
}

Notice result.Errors.ToApplicationError() which returns ModelStateDictionary that can be sent as a parameter to ApiProblemDetailsException.

Without adding AutoWrapper inside a class library project, how could we handle ModelStateDictionary errors?

proudmonkey commented 3 years ago

Generally, Model state validation should be handled in the Controller level. To reference ModelStateDictionary , you have to install Microsoft.AspNetCore.Mvc package.

BlazorPlate commented 3 years ago

Thank you for your assistance. You are right, I ended up with looping ModelStateDictionary errors and wrapping them using stringBuilder as multiple lines string and then send it as regular exception.