I have an ASP.NET Core Web API and I'm using an exception middleware in order to catch my custom exceptions and return appropriate responses to the client.
However, in the case of "Unauthorized Access" thrown by the controller itself when using the [Authorize] attribute, how can I catch the error in my middleware?
Exception middleware
public class ExceptionMiddleware
{
readonly RequestDelegate _request;
public ExceptionMiddleware(RequestDelegate request)
{
_request = request;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _request(context);
}
catch (ValidationException ex)
{
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
var error = ex.ToValidationExceptionDetails();
await context.Response.WriteAsJsonAsync(error);
}
catch (Exception ex) when (ex.GetErrorCode() == (int)HttpStatusCode.Conflict)
{
context.Response.StatusCode = (int)HttpStatusCode.Conflict;
var error = ex.ToConflictExceptionDetails();
await context.Response.WriteAsJsonAsync(error);
}
catch (Exception ex) when (ex.GetErrorCode() == (int)HttpStatusCode.NotFound)
{
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
var error = ex.ToNotFoundExceptionDetails();
await context.Response.WriteAsJsonAsync(error);
}
catch(Exception ex) when (ex.GetErrorCode() == (int)HttpStatusCode.Unauthorized)
{
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
var error = ex.ToUnauthorizedRequestExceptionDetails();
await context.Response.WriteAsJsonAsync(error);
}
catch (Exception)
{
throw;
}
}
}
Custom exception example:
public class PostNotFoundException : Exception
{
public int ErrorCode { get; } = (int)System.Net.HttpStatusCode.NotFound;
readonly static string _defaultErrorMessage = "Post Not Found.";
public PostNotFoundException() : base(_defaultErrorMessage) { }
public PostNotFoundException(string message) : base(message) { }
public PostNotFoundException(string message, Exception innerException)
: base(message, innerException) { }
}
Controller method code:
[Route("api/[controller]")]
[ApiController]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class PostsController : ControllerBase
{
readonly IPostService _postService;
public PostsController(IPostService postService)
{
_postService = postService;
}
[HttpGet("{id}")]
public async Task<ActionResult<PostQueryDTO>> GetPostById(Guid id)
{
return await _postService.GetPostByIdAsync(id);
}
[HttpGet("user")]
public async Task<ActionResult<IEnumerable<PostsByUserDTO>>> GetPostsByUserID()
{
return Ok(await _postService.GetPostsByUserIDAsync(HttpContext.GetUserID()));
}
[HttpGet("user/followings")]
public async Task<ActionResult<IEnumerable<PostQueryDTO>>> GetPostsByUserFollowings()
{
return Ok(await _postService.GetPostsByUserFollowingsAsync(HttpContext.GetUserID()));
}
[HttpPost]
public async Task<ActionResult<PostPOSTCommandDTO>> PostPost(PostPOSTCommandDTO post)
{
PostQueryDTO newpost = await _postService.CreatePostAsync(post, HttpContext.GetUserID());
return CreatedAtAction("GetPostById", new { id = newpost.Id }, newpost);
}
[HttpPut("{postID}")]
public async Task<IActionResult> PutPost(PostPOSTCommandDTO post, Guid postID)
{
await _postService.EditPostAsync(post, postID, HttpContext.GetUserID());
return NoContent();
}
[HttpDelete("{postID}")]
public async Task<IActionResult> DeletePost(Guid postID)
{
await _postService.DeletePostAsync(postID, HttpContext.GetUserID());
return NoContent();
}
}
So basically if an unauthorized user tries to add a post, the controller returns and 401 response, because of the Authorize attribute.
Is there a way to stop it from doing that and let me return custom response instead?