You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ExceptionMiddleware.cs 5.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. using Diligent.WebAPI.Contracts.Exceptions;
  2. using Diligent.WebAPI.Contracts.Models;
  3. using Microsoft.AspNetCore.Mvc;
  4. using Microsoft.Extensions.Options;
  5. using Newtonsoft.Json;
  6. using Newtonsoft.Json.Serialization;
  7. using System.Diagnostics;
  8. using System.Net;
  9. using System.Runtime.Serialization;
  10. namespace Diligent.WebAPI.Host.Middlewares
  11. {
  12. [ExcludeFromCodeCoverage]
  13. public class DiligBadRequestException : Exception
  14. {
  15. public const string ExceptionMessage = "Bad request from custom middleware";
  16. public const string ExceptionCode = "DiligBadRequestException";
  17. public const HttpStatusCode HttpResponseCode = HttpStatusCode.BadRequest;
  18. public DiligBadRequestException() : base(ExceptionMessage) { }
  19. public DiligBadRequestException(string message) : base(message) { }
  20. public DiligBadRequestException(string message, Exception innerException) : base(message, innerException) { }
  21. public DiligBadRequestException(SerializationInfo info, StreamingContext context) : base(info, context) { }
  22. }
  23. public class DiligExceptionMiddleware
  24. {
  25. private readonly RequestDelegate _next;
  26. private readonly ILogger<DiligExceptionMiddleware> _logger;
  27. private readonly IWebHostEnvironment _env;
  28. private readonly ApiBehaviorOptions _options;
  29. public DiligExceptionMiddleware(RequestDelegate next, ILogger<DiligExceptionMiddleware> logger, IWebHostEnvironment env, IOptions<ApiBehaviorOptions> options)
  30. => (_next, _logger, _env, _options) = (next, logger, env, options.Value);
  31. public async Task InvokeAsync(HttpContext httpContext)
  32. {
  33. try
  34. {
  35. await _next(httpContext);
  36. }
  37. // Some custom Exceptions
  38. catch (EntityNotFoundException ex)
  39. {
  40. _logger.LogError(ex, DiligBadRequestException.ExceptionMessage);
  41. await HandleExceptionAsync(httpContext, ex, DiligBadRequestException.ExceptionMessage, HttpStatusCode.NotFound);
  42. }
  43. catch (Exception ex)
  44. {
  45. // Better solution is to use switch statement
  46. if(HasInternalExceptionOfType<DiligBadRequestException>(ex))
  47. {
  48. _logger.LogError(ex, DiligBadRequestException.ExceptionMessage);
  49. await HandleExceptionAsync(httpContext, ex, DiligBadRequestException.ExceptionMessage, DiligBadRequestException.HttpResponseCode);
  50. }
  51. else
  52. {
  53. await HandleSomethingWentWrongAsync(httpContext, ex);
  54. }
  55. }
  56. }
  57. private async Task HandleSomethingWentWrongAsync(HttpContext httpContext, Exception ex)
  58. {
  59. _logger.LogError(ex, "Something went wrong");
  60. await HandleExceptionAsync(httpContext, ex);
  61. }
  62. private bool HasInternalExceptionOfType<T>(Exception e)
  63. {
  64. if(e.InnerException != null)
  65. {
  66. return e is T || HasInternalExceptionOfType<T>(e.InnerException);
  67. }
  68. return e is T;
  69. }
  70. private Task HandleExceptionAsync(HttpContext context, Exception exception, string? message = null, HttpStatusCode? responseCode = null)
  71. {
  72. context.Response.ContentType = "application/json";
  73. context.Response.StatusCode = (int)(responseCode ?? HttpStatusCode.InternalServerError);
  74. string type = null;
  75. if (_options.ClientErrorMapping.TryGetValue(context.Response.StatusCode, out var clientErrorData))
  76. {
  77. type = clientErrorData.Link;
  78. }
  79. var errorTitle = message ?? (_env.IsDevelopment() ? exception.Message : "Internal server error");
  80. var errorMessage = _env.IsDevelopment() ? exception.StackTrace : "Something went wrong" ;
  81. var serializerSettings = new JsonSerializerSettings
  82. {
  83. ContractResolver = new CamelCasePropertyNamesContractResolver()
  84. };
  85. var resultError = new ResultError(errorTitle);
  86. var httpResultError = new HttpResultError()
  87. {
  88. ErrorMessage = errorMessage,
  89. ErrorCode = responseCode ?? HttpStatusCode.InternalServerError
  90. };
  91. resultError.AddCustomError(httpResultError);
  92. var responseString = JsonConvert.SerializeObject(
  93. new BaseResult<Exception>
  94. {
  95. // Type = type,
  96. // TraceId = Activity.Current?.Id ?? context?.TraceIdentifier,
  97. IsSuccess = false,
  98. Errors = { resultError }
  99. }, serializerSettings);
  100. return context.Response.WriteAsync(responseString);
  101. }
  102. }
  103. public static class DiligExceptionMiddlewareExtensions
  104. {
  105. public static IApplicationBuilder UseDiligExceptionHandler(this IApplicationBuilder app)
  106. {
  107. return app.UseMiddleware<DiligExceptionMiddleware>();
  108. }
  109. }
  110. }