The well-accepted answer helped me a lot but I wanted to pass HttpStatusCode in my middleware to manage error status code at runtime.
According to this link I got some idea to do the same. So I merged the Andrei Answer with this. So my final code is below:
1. Base class
public class ErrorDetails { public int StatusCode { get; set; } public string Message { get; set; } public override string ToString() { return JsonConvert.SerializeObject(this); } }
2. Custom Exception Class Type
public class HttpStatusCodeException : Exception { public HttpStatusCode StatusCode { get; set; } public string ContentType { get; set; } = @"text/plain"; public HttpStatusCodeException(HttpStatusCode statusCode) { this.StatusCode = statusCode; } public HttpStatusCodeException(HttpStatusCode statusCode, string message) : base(message) { this.StatusCode = statusCode; } public HttpStatusCodeException(HttpStatusCode statusCode, Exception inner) : this(statusCode, inner.ToString()) { } public HttpStatusCodeException(HttpStatusCode statusCode, JObject errorObject) : this(statusCode, errorObject.ToString()) { this.ContentType = @"application/json"; } }
3. Custom Exception Middleware
public class CustomExceptionMiddleware { private readonly RequestDelegate next; public CustomExceptionMiddleware(RequestDelegate next) { this.next = next; } public async Task Invoke(HttpContext context /* other dependencies */) { try { await next(context); } catch (HttpStatusCodeException ex) { await HandleExceptionAsync(context, ex); } catch (Exception exceptionObj) { await HandleExceptionAsync(context, exceptionObj); } } private Task HandleExceptionAsync(HttpContext context, HttpStatusCodeException exception) { string result = null; context.Response.ContentType = "application/json"; if (exception is HttpStatusCodeException) { result = new ErrorDetails() { Message = exception.Message, StatusCode = (int)exception.StatusCode }.ToString(); context.Response.StatusCode = (int)exception.StatusCode; } else { result = new ErrorDetails() { Message = "Runtime Error", StatusCode = (int)HttpStatusCode.BadRequest }.ToString(); context.Response.StatusCode = (int)HttpStatusCode.BadRequest; } return context.Response.WriteAsync(result); } private Task HandleExceptionAsync(HttpContext context, Exception exception) { string result = new ErrorDetails() { Message = exception.Message, StatusCode = (int)HttpStatusCode.InternalServerError }.ToString(); context.Response.StatusCode = (int)HttpStatusCode.BadRequest; return context.Response.WriteAsync(result); } }
4. Extension Method
public static void ConfigureCustomExceptionMiddleware(this IApplicationBuilder app) { app.UseMiddleware<CustomExceptionMiddleware>(); }
5. Configure Method in startup.cs
app.ConfigureCustomExceptionMiddleware(); app.UseMvc();
Now my login method in Account controller :
try { IRepository<UserMaster> obj = new Repository<UserMaster>(_objHeaderCapture, Constants.Tables.UserMaster); var result = obj.Get() .AsQueryable() .Where(sb => sb.EmailId.ToLower() == objData.UserName.ToLower() && sb.Password == objData.Password.ToEncrypt() && sb.Status == (int)StatusType.Active) .FirstOrDefault(); if (result != null)//User Found return result; else // Not Found throw new HttpStatusCodeException(HttpStatusCode.NotFound, "Please check username or password"); } catch (Exception ex) { throw ex; }
Above you can see if i have not found the user then raising the HttpStatusCodeException in which i have passed HttpStatusCode.NotFound status and a custom message
In middleware
catch (HttpStatusCodeException ex)
blocked will be called which will pass control to
private Task HandleExceptionAsync(HttpContext context, HttpStatusCodeException exception) method
But what if i got runtime error before? For that i have used try catch block which throw exception and will be catched in catch (Exception exceptionObj) block and will pass control to
Task HandleExceptionAsync(HttpContext context, Exception exception)
method.
I have used a single ErrorDetails class for uniformity.
UseExceptionHandler
middleware?NotFound
without throwing exceptions. NuGet package like github.com/AKlaus/DomainResult would help here.