Skip to content

Instantly share code, notes, and snippets.

@seangwright
Last active June 26, 2019 05:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seangwright/139868f800503c118be9675cc8aa02c3 to your computer and use it in GitHub Desktop.
Save seangwright/139868f800503c118be9675cc8aa02c3 to your computer and use it in GitHub Desktop.
Error Handling in Web Api 2
public class ApiExceptionResult : IHttpActionResult
{
private static readonly MediaTypeHeaderValue mediaType = new MediaTypeHeaderValue("application/json");
private readonly ApiError errorResponse;
private readonly JsonMediaTypeFormatter jsonMediaTypeFormatter;
private readonly HttpRequestMessage requestMessage;
private readonly HttpStatusCode statusCode;
public ApiExceptionResult(HttpRequestMessage requestMessage, HttpStatusCode statusCode, ApiError errorResponse, JsonMediaTypeFormatter jsonMediaTypeFormatter)
{
Guard.Against.Null(requestMessage, nameof(requestMessage));
Guard.Against.Null(statusCode, nameof(statusCode));
Guard.Against.Null(errorResponse, nameof(errorResponse));
Guard.Against.Null(jsonMediaTypeFormatter, nameof(jsonMediaTypeFormatter));
this.requestMessage = requestMessage;
this.statusCode = statusCode;
this.errorResponse = errorResponse;
this.jsonMediaTypeFormatter = jsonMediaTypeFormatter;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = requestMessage.CreateResponse(statusCode, errorResponse, jsonMediaTypeFormatter, mediaType);
return Task.FromResult(response);
}
}
public class GlobalApiExceptionHandler : IExceptionHandler
{
private readonly JsonMediaTypeFormatter jsonMediaTypeFormatter;
public GlobalApiExceptionHandler(JsonMediaTypeFormatter jsonMediaTypeFormatter)
{
Guard.Against.Null(jsonMediaTypeFormatter, nameof(jsonMediaTypeFormatter));
this.jsonMediaTypeFormatter = jsonMediaTypeFormatter;
}
public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
{
var exception = context.Exception;
var (code, error) = BuildError(exception);
// Use your logging framework of choice or Kentico's EventLogProvider.LogException()
// to log your error before sending the response
context.Result = new ApiExceptionResult(context.Request, code, error, jsonMediaTypeFormatter);
return Task.CompletedTask;
}
private (HttpStatusCode code, ApiError error) BuildError(Exception exception)
{
var buildApiError = CaptureErrorId(Guid.NewGuid());
switch (exception)
{
case HttpException httpEx:
return ((HttpStatusCode)httpEx.GetHttpCode(), buildApiError("HTTP_EXCEPTION", "Server error"));
case NotAuthenticatedException notAuthNEx:
return (HttpStatusCode.Unauthorized, buildApiError("NOT_AUTHENTICATED", "This endpoint reuires authentication"));
case NotAuthorizedException notAuthZEx:
return (HttpStatusCode.Forbidden, buildApiError("NOT_AUTHORIZED", "The request was not authorized to perform that action"));
case BadRequestException badRequestEx:
return (HttpStatusCode.BadRequest, buildApiError("BAD_REQUEST", "The request was malformed"));
case NotFoundException notFoundEx:
return (HttpStatusCode.NotFound, buildApiError("NOT_FOUND", "The requested data could not be found"));
default:
return (HttpStatusCode.InternalServerError, buildApiError("APPLICATION_EXCEPTION", "Server error"));
}
}
private Func<string, string, ApiError> CaptureErrorId(Guid errorId) =>
(string errorCode, string errorMessage) =>
new ApiError(errorId, errorCode, errorMessage);
}
public static class WebApiConfig
{
public static HttpConfiguration ConfigureWebApi(IContainer container)
{
// ...
config.Services.Replace(typeof(IExceptionHandler), container.Resolve<GlobalApiExceptionHandler>());
// ...
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment