using CleanArchitecture.Application.Common.Exceptions;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
namespace CleanArchitecture.WebUI.Filters
{
public class ApiExceptionFilterAttribute : ExceptionFilterAttribute
{
private readonly IDictionary<Type, Action<ExceptionContext>> _exceptionHandlers;
public ApiExceptionFilterAttribute()
{
// Register known exception types and handlers.
_exceptionHandlers = new Dictionary<Type, Action<ExceptionContext>>
{
{ typeof(ValidationException), HandleValidationException },
{ typeof(NotFoundException), HandleNotFoundException },
{ typeof(UnauthorizedAccessException), HandleUnauthorizedAccessException },
{ typeof(ForbiddenAccessException), HandleForbiddenAccessException },
};
}
public override void OnException(ExceptionContext context)
{
HandleException(context);
base.OnException(context);
}
private void HandleException(ExceptionContext context)
{
Type type = context.Exception.GetType();
if (_exceptionHandlers.ContainsKey(type))
{
_exceptionHandlers[type].Invoke(context);
return;
}
if (!context.ModelState.IsValid)
{
HandleInvalidModelStateException(context);
return;
}
HandleUnknownException(context);
}
private void HandleValidationException(ExceptionContext context)
{
var exception = context.Exception as ValidationException;
var details = new ValidationProblemDetails(exception.Errors)
{
Type = "https://tools.ietf.org/html/rfc7231#section-6.5.1"
};
context.Result = new BadRequestObjectResult(details);
context.ExceptionHandled = true;
}
private void HandleInvalidModelStateException(ExceptionContext context)
{
var details = new ValidationProblemDetails(context.ModelState)
{
Type = "https://tools.ietf.org/html/rfc7231#section-6.5.1"
};
context.Result = new BadRequestObjectResult(details);
context.ExceptionHandled = true;
}
private void HandleNotFoundException(ExceptionContext context)
{
var exception = context.Exception as NotFoundException;
var details = new ProblemDetails()
{
Type = "https://tools.ietf.org/html/rfc7231#section-6.5.4",
Title = "The specified resource was not found.",
Detail = exception.Message
};
context.Result = new NotFoundObjectResult(details);
context.ExceptionHandled = true;
}
private void HandleUnauthorizedAccessException(ExceptionContext context)
{
var details = new ProblemDetails
{
Status = StatusCodes.Status401Unauthorized,
Title = "Unauthorized",
Type = "https://tools.ietf.org/html/rfc7235#section-3.1"
};
context.Result = new ObjectResult(details)
{
StatusCode = StatusCodes.Status401Unauthorized
};
context.ExceptionHandled = true;
}
private void HandleForbiddenAccessException(ExceptionContext context)
{
var details = new ProblemDetails
{
Status = StatusCodes.Status403Forbidden,
Title = "Forbidden",
Type = "https://tools.ietf.org/html/rfc7231#section-6.5.3"
};
context.Result = new ObjectResult(details)
{
StatusCode = StatusCodes.Status403Forbidden
};
context.ExceptionHandled = true;
}
private void HandleUnknownException(ExceptionContext context)
{
var details = new ProblemDetails
{
Status = StatusCodes.Status500InternalServerError,
Title = "An error occurred while processing your request.",
Type = "https://tools.ietf.org/html/rfc7231#section-6.6.1"
};
context.Result = new ObjectResult(details)
{
StatusCode = StatusCodes.Status500InternalServerError
};
context.ExceptionHandled = true;
}
}
}