How to disable Serilog to log one exception? - c#

(Asp.Net core 3.1/.Net 6)
I need to disable the ERR logging of exception TaskCanceledException in an Asp.Net application (built by others). I've used the following middleware to suppress the TaskCanceledException
app.UseMiddleware<TaskCanceledMiddleware>();
public class TaskCanceledMiddleware
{
private readonly RequestDelegate _next;
public TaskCanceledMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (TaskCanceledException)
{
// Set StatusCode 499 Client Closed Request
logger.WARN("...")
context.Response.StatusCode = 499;
}
}
}
I can see the WARN log in the logs. However, I can still find the following EROR messages by Serilog.AspNetCore.RequestLoggingMiddleware? (Note the error level is EROR)
2022-07-06 07:30:40.6636|116344477|EROR|Serilog.AspNetCore.RequestLoggingMiddleware|HTTP "GET" "/api/v1/myurl" responded 500 in 23213.3233 ms
System.Threading.Tasks.TaskCanceledException: A task was canceled.
at System.Net.Http.DiagnosticsHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at ....
Why there is still errors of TaskCanceledException after using app.UseMiddleware<TaskCanceledMiddleware>()? (BTW, what's the level of EROR? Shouldn't it be ERR?)

you can try excluding logging for certain exception like this.
new LoggerConfiguration()
....
.Filter.ByExcluding(logEvent => logEvent.Exception != null && logEvent.Exception.GetType() == typeof(TaskCanceledException))
...CreateLogger();

do you know where the exception is thrown?
Maybe it throws not inside asp.net core action execution pipeline (where Middleware works).
You can register a filter to intercept application wide exceptions inside .AddControllers() registration like this:
_ = services
.AddControllers(options =>
{
//Global filters
_ = options.Filters.Add<ApiGlobalExceptionFilterAttribute>();
///...omissis...
})
Here a simple exception filter attribute implementation:
public sealed class ApiGlobalExceptionFilterAttribute : ExceptionFilterAttribute
{
private readonly ILogger<ApiGlobalExceptionFilterAttribute> _logger;
public ApiGlobalExceptionFilterAttribute(ILogger<ApiGlobalExceptionFilterAttribute> logger)
{
_logger = logger;
}
public override void OnException(ExceptionContext context)
{
var request = context.HttpContext?.Request;
if (context.Exception is TaskCanceledException tcExc)
{
// Set StatusCode 499 Client Closed Request
_logger.WARN("...");
context.Result = new ErrorActionResult(499);
}
else
{
//TODO: manage errors
}
context.ExceptionHandled = true;
base.OnException(context);
}
}
internal class ErrorActionResult : IActionResult
{
[JsonIgnore]
public int StatusCode { get; private set; }
public ErrorActionResult(int statusCode)
{
StatusCode = statusCode;
}
public async Task ExecuteResultAsync(ActionContext context)
{
var error = new
{
Code = StatusCode,
Message = "Internal server error"
};
var objectResult = new ObjectResult(error)
{
StatusCode = StatusCode
};
await objectResult.ExecuteResultAsync(context);
}
}
The context.ExceptionHandled = true; stops exception to be propagated inside pipeline.
Hope it helps!

Related

Catching Exceptions in a RESTful API

Should catching exceptions be part of the business logic such as the Service layer, or should they be caught in the controllers' methods?
For example:
Controller UpdateUser method
[HttpPut]
[Route("{id}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<ActionResult<UserDto>> UpdateUserInfo(int id, UserDto userRequest)
{
try
{
var user = _userMapper.ConvertToEntity(userRequest);
var updatedUser = await _userService.UpdateAsync(user, id);
var result = _userMapper.ConvertToUserDto(updatedUser);
return Ok(result);
}
catch (Exception ex)
{
_logger.LogError("Exception caught attempting to update user - Type: {ex}", ex.GetType());
_logger.LogError("Message: {ex}", ex.Message);
return StatusCode(500, ex.Message);
}
}
The Service Layer
public async Task<User> UpdateAsync(User user, int id)
{
await _repository.UpdateAsync(user, id);
return user;
}
So, should the exceptions be caught in the service layer or the controller? Or is it subjective?
It's dependent on the business of your application. maybe in your service you should use a try/catch block to adding a log or do anything when exception occurred. but usually I use a global try/catch in a middleware to get exception and send correct response to the client.
public class AdvancedExceptionHandler
{
private readonly RequestDelegate _next;
private readonly ILogger<AdvancedExceptionHandler> _logger;
private readonly IWebHostEnvironment _env;
public AdvancedExceptionHandler(RequestDelegate next, ILogger<AdvancedExceptionHandler> logger, IWebHostEnvironment env)
{
_next = next;
_logger = logger;
_env = env;
}
public async Task Invoke(HttpContext context)
{
string message = null;
HttpStatusCode httpStatusCode = HttpStatusCode.InternalServerError;
try
{
await _next(context);
}
catch (Exception ex)
{
_logger.LogError(ex.Message, ex);
if (_env.IsDevelopment())
{
var dic = new Dictionary<string, string>
{
["StackTrace"] = ex.StackTrace,
["Exception"] = ex.Message
};
message = JsonConvert.SerializeObject(dic);
}
else
{
message = "an error has occurred";
}
await WriteToReponseAsync();
}
async Task WriteToReponseAsync()
{
if (context.Response.HasStarted)
throw new InvalidOperationException("The response has already started");
var exceptionResult = new ExceptionResult(message, (int)httpStatusCode);
var result = JsonConvert.SerializeObject(exceptionResult);
context.Response.StatusCode = (int)httpStatusCode;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(result);
}
}
}
ExceptionResutl class:
public class ExceptionResult
{
public ExceptionResult(string message, int statusCode)
{
this.Message = message;
this.StatusCode = statusCode;
}
public string Message { get; set; }
public int StatusCode { get; set; }
}
public static class ExceptionHandlerMiddlewareExtension
{
public static void UseAdvancedExceptionHandler(this IApplicationBuilder app)
{
app.UseMiddleware<AdvancedExceptionHandler>();
}
}
Then adding middleware in Configure method
public void Configure(IApplicationBuilder app)
{
app.UseAdvancedExceptionHandler();//<--NOTE THIS
}
I don't use try/catch block in controllers. (my opinion)
Catching exceptions in your controller will quickly start to violate some clean code principles, like DRY.
If I understand correctly, the example you have written is that you want to log some errors in case any exceptions are thrown in your code. This is reasonable, but if you begin to add more endpoints, you'll notice you have the same try/catch in all your controller methods. The best way to refactor this is to use a middleware that will catch the exception and map it to a response that you want.
Over time as you begin to update your application to have more features you may have a situation where multiple endpoints are throwing similar errors and you want it to be handled in a similar way. For example, in your example, if the user doesn't exist, the application (in your service layer) may throw an UserNotFoundException, and you may have some other endpoints which can throw the same error, too.
You could create another middleware to handle this or even extend your existing middleware.
One of the better approaches I've seen over the years is to use this library https://github.com/khellang/Middleware/tree/master/src/ProblemDetails to handle the boiler plate for you.

Is it possible to intercept errors thrown by middleware after the controller action is executed?

I'm calling UseExceptionHandler in order to handle errors. This works fine, but not for errors thrown by other (subsequently registered) middleware.
The middleware which exceptions I need to handle is TransactionMiddleware. What it does is to save any changes to the database after a successfully completed call to an action. To be clear - it doesn't just commit a transaction, but also runs all the insert:s/update:s etc. This might fail, for example due to database constraints. (There are also other reasons not to complete the transaction, but they are not included here. Just mentioning that to explain that making the database calls earlier and reducing the TransactionMiddleware to simply commiting won't do the trick.)
Is there a way to NOT start the response before this middleware has run its full course?
My Program.cs
var builder = WebApplication.CreateBuilder(args);
new Startup(builder.Configuration).ConfigureServices(builder.Services);
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
// await ExcludedCode()...
});
});
app.UseSwagger()
.UseSwaggerUI();
app.UseRouting()
.UseCors()
.UseAuthentication()
.UseAuthorization()
.UseMiddleware<LanguageMiddleWare>()
.UseMiddleware<TransactionMiddleWare>()
.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.Run();
My (simplified) TransactionMiddleWare-class
public class TransactionMiddleWare
{
private readonly RequestDelegate next;
public TransactionMiddleWare(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context, IDataContext dataContext)
{
try
{
await next(context);
}
catch (PartialExecutionException)
{
this.Commit(context, dataContext);
throw;
}
this.Commit(context, dataContext);
}
private void Commit(HttpContext context, IDataContext dataContext)
{
if (this.ShouldTransactionCommited(context))
dataContext.SaveChanges();
else
throw new Exception("Exception example for clarity.");
}
private bool ShouldTransactionBeCommited(HttpContext context)
{
return true; // Actual code omitted for brevity.
}
}
Example of how my controllers return data (no special stuff):
[ApiController]
[Route("advertisment")]
public class AdvertismentController : ControllerBase
{
private readonly IAdvertismentService advertismentService;
private NLog.ILogger log;
public AdvertismentController(
IAdvertismentService advertismentService)
{
this.log = LogManager.GetCurrentClassLogger();
this.advertismentService = advertismentService;
}
[HttpPost]
public Result<Guid> Create([FromForm] CreateAdvertismentMultipartFormModel request)
{
var id = this.advertismentService.Create(request);
return new Result<Guid> { Data = id };
}
}
Here is what I ended up with. A change in the TransactionMiddleWare class:
public async Task Invoke(HttpContext context, IDataContext dataContext)
{
context.Response.OnStarting(state => {
this.Commit(context, dataContext);
return Task.CompletedTask;
}, context);
try
{
await next(context);
this.Commit(context, dataContext);
}
catch (PartialExecutionException)
{
this.Commit(context, dataContext);
throw;
}
}
That way it will be run and any exception will occur while it's still possible to modify the output and produce an error message.

.NET Core WebApi Get HttpClient Error Message

I am trying to display the relevant error message returned from a Web API when using HttpClient.PostJsonAsync<T>().
In my Web API, for example on a failed login I would return a 401 unauthorized exception with the message in the body with an error handling middleware;
public class HttpErrorHandlerMiddleware
{
private readonly RequestDelegate _next;
public HttpErrorHandlerMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (HttpStatusCodeException exception)
{
if (!context.Response.HasStarted)
{
context.Response.StatusCode = (int)exception.StatusCode;
context.Response.Headers.Clear();
await context.Response.WriteAsync(exception.Message);
}
}
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class HttpErrorHandlerMiddlewareExtensions
{
public static IApplicationBuilder UseHttpErrorHandlerMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<HttpErrorHandlerMiddleware>();
}
}
Then in Postman this is displayed with the message e.g. 'Invalid UserName or Password', 'User is Locked out.', etc.
However, when I try to catch the error on the client side, e.g.
try
{
var result = await _httpClient.PostJsonAsync<LoginResponse>("api/login", loginModel);
/* Removed for brevity */
}
catch(Exception ex)
{
return new LoginResponse { Success = false, Error = ex.Message };
}
The error returned is always 'Response status code does not indicate success: 401 (Unauthorized).'
How would I get the detail of the error from the return of the PostJsonAsync<T> please?
401 is a validate response, and it would not be captured in HttpErrorHandlerMiddleware.
I am not sure how you implement 401 and PostJsonAsync. Here is a working demo for you:
Controller Action
[HttpPost]
public IActionResult Login(LoginModel loginModel)
{
return StatusCode(401, new LoginResponse {
Success = false,
Error = "Invalid User Name and Password"
});
}
HttpClient Request
public async Task<LoginResponse> Test(LoginModel loginModel)
{
var _httpClient = new HttpClient();
var result = await _httpClient.PostAsJsonAsync<LoginModel>("https://localhost:44322/api/values/login", loginModel);
return await result.Content.ReadAsAsync<LoginResponse>();
}

DelegatingHandler not getting invoked if exception caught by ExceptionHandler

I am trying to understand how Web API Http pipeline works!
In my Web API project, I am using the following technique to log/handle exceptions:
ExceptionHandler -> Handle exceptions at the global level
ExceptionFilterAttribute -> Handle custom exception thrown by user
DelegatingHandler -> log request and response data
Sample code for each implementation:
ExceptionFilter:
public class CustomExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
var request = context.ActionContext.Request;
if (context.Exception is ItemNotFoundException)
{
context.Response = request.CreateResponse(HttpStatusCode.NotFound, context.Exception.Message);
}
else if (context.Exception is InvalidRequestException)
{
context.Response = request.CreateResponse(HttpStatusCode.BadRequest, context.Exception.Message);
}
}
}
Exception Handler:
public class GlobalExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
var result = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(Constant.ErrorMessage.InternalServerError)
};
context.Result = new ErrorMessageResult(context.Request, result);
}
}
public class ErrorMessageResult : IHttpActionResult
{
private readonly HttpRequestMessage _request;
private readonly HttpResponseMessage _httpResponseMessage;
public ErrorMessageResult(HttpRequestMessage request, HttpResponseMessage httpResponseMessage)
{
_request = request;
_httpResponseMessage = httpResponseMessage;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return Task.FromResult(_httpResponseMessage);
}
}
DelegatingHandler:
public class LogRequestAndResponseHandler : DelegatingHandler
{
private readonly ILoggingService _loggingService;
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
string requestBody = await request.Content.ReadAsStringAsync();
_loggingService.FirstLevelServiceLog(requestBody);
var result = await base.SendAsync(request, cancellationToken);
if (result.Content != null)
{
var responseBody = await result.Content.ReadAsStringAsync();
_loggingService.FirstLevelServiceLog(responseBody);
}
return result;
}
}
Observation:
When there is an custom exception CustomExceptionFilter is getting invoked and later the response is logged in LogRequestAndResponseHandler.
However, if the exception is not handled, it goes in GlobalExceptionHandler then the response DOES NOT come to LogRequestAndResponseHandler for logging.
Could anyone let me know, what code change have to be done in CustomExceptionFilter/GlobalExceptionHandler in order to receive the response in DelegatingHandler?
Solution: (Updated 10/09/2018)
Okay, so i found the solution here
By modifying ExceptionHandler code, i am able to catch the response in DelegatingHandler
Key was to inherit from IExceptionHandler rather than ExceptionHandler
Code:
public class GlobalExceptionHandler : IExceptionHandler
{
public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
{
var httpResponse = context.Request.CreateResponse(HttpStatusCode.InternalServerError, Constant.ErrorMessage.InternalServerError);
context.Result = new ResponseMessageResult(httpResponse);
return Task.FromResult(0);
}
}
Question:
I am still not able to understand how it's working? What is the difference between IExceptionHandler & ExceptionHandler?
Could anyone shed some light on this?
ExceptionHandler implements IExceptionHandler like this:
Task IExceptionHandler.HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
{
if (context == null)
throw new ArgumentNullException(nameof (context));
ExceptionContext exceptionContext = context.ExceptionContext;
if (!this.ShouldHandle(context))
return TaskHelpers.Completed();
return this.HandleAsync(context, cancellationToken);
}
Where I suspect you're seeing the difference is in that ShouldHandle check, which is implemented like this:
public virtual bool ShouldHandle(ExceptionHandlerContext context)
{
if (context == null)
throw new ArgumentNullException(nameof (context));
return context.ExceptionContext.CatchBlock.IsTopLevel;
}
I'm not intimately familiar with the pipeline, but from what I've seen it appears that exceptions can be handled at various points, and the ExceptionHandler base class assumes you probably only want to handle exceptions at the top level of the execution stack. I've seen cases where other handlers like CORS get in the way of this, and the catch block never ends up being at the top level.
If this is what you're seeing, you can still extend ExceptionHandler, and override the ShouldHandle method to just always return true. Or you could be more surgical and specifically detect whether CORS is likely to get in the way of the top-level check as suggested in this comment.

ASP.NET Core equivalent of ASP.NET MVC 5's HttpException

In ASP.NET MVC 5 you could throw a HttpException with a HTTP code and this would set the response like so:
throw new HttpException((int)HttpStatusCode.BadRequest, "Bad Request.");
HttpException does not exist in ASP.NET Core. What is the equivalent code?
I implemented my own HttpException and supporting middleware which catches all HttpException's and turns them into the corresponding error response. A short extract can be seen below. You can also use the Boxed.AspNetCore Nuget package.
Usage Example in Startup.cs
public void Configure(IApplicationBuilder application)
{
application.UseIISPlatformHandler();
application.UseStatusCodePagesWithReExecute("/error/{0}");
application.UseHttpException();
application.UseMvc();
}
Extension Method
public static class ApplicationBuilderExtensions
{
public static IApplicationBuilder UseHttpException(this IApplicationBuilder application)
{
return application.UseMiddleware<HttpExceptionMiddleware>();
}
}
Middleware
internal class HttpExceptionMiddleware
{
private readonly RequestDelegate next;
public HttpExceptionMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await this.next.Invoke(context);
}
catch (HttpException httpException)
{
context.Response.StatusCode = httpException.StatusCode;
var responseFeature = context.Features.Get<IHttpResponseFeature>();
responseFeature.ReasonPhrase = httpException.Message;
}
}
}
HttpException
public class HttpException : Exception
{
private readonly int httpStatusCode;
public HttpException(int httpStatusCode)
{
this.httpStatusCode = httpStatusCode;
}
public HttpException(HttpStatusCode httpStatusCode)
{
this.httpStatusCode = (int)httpStatusCode;
}
public HttpException(int httpStatusCode, string message) : base(message)
{
this.httpStatusCode = httpStatusCode;
}
public HttpException(HttpStatusCode httpStatusCode, string message) : base(message)
{
this.httpStatusCode = (int)httpStatusCode;
}
public HttpException(int httpStatusCode, string message, Exception inner) : base(message, inner)
{
this.httpStatusCode = httpStatusCode;
}
public HttpException(HttpStatusCode httpStatusCode, string message, Exception inner) : base(message, inner)
{
this.httpStatusCode = (int)httpStatusCode;
}
public int StatusCode { get { return this.httpStatusCode; } }
}
In the long term, I would advise against using exceptions for returning errors. Exceptions are slower than just returning an error from a method.
After a brief chat with #davidfowl, it seems that ASP.NET 5 has no such notion of HttpException or HttpResponseException that "magically" turn to response messages.
What you can do, is hook into the ASP.NET 5 pipeline via MiddleWare, and create one that handles the exceptions for you.
Here is an example from the source code of their error handler middleware which will set the response status code to 500 in case of an exception further up the pipeline:
public class ErrorHandlerMiddleware
{
private readonly RequestDelegate _next;
private readonly ErrorHandlerOptions _options;
private readonly ILogger _logger;
public ErrorHandlerMiddleware(RequestDelegate next,
ILoggerFactory loggerFactory,
ErrorHandlerOptions options)
{
_next = next;
_options = options;
_logger = loggerFactory.CreateLogger<ErrorHandlerMiddleware>();
if (_options.ErrorHandler == null)
{
_options.ErrorHandler = _next;
}
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
_logger.LogError("An unhandled exception has occurred: " + ex.Message, ex);
if (context.Response.HasStarted)
{
_logger.LogWarning("The response has already started,
the error handler will not be executed.");
throw;
}
PathString originalPath = context.Request.Path;
if (_options.ErrorHandlingPath.HasValue)
{
context.Request.Path = _options.ErrorHandlingPath;
}
try
{
var errorHandlerFeature = new ErrorHandlerFeature()
{
Error = ex,
};
context.SetFeature<IErrorHandlerFeature>(errorHandlerFeature);
context.Response.StatusCode = 500;
context.Response.Headers.Clear();
await _options.ErrorHandler(context);
return;
}
catch (Exception ex2)
{
_logger.LogError("An exception was thrown attempting
to execute the error handler.", ex2);
}
finally
{
context.Request.Path = originalPath;
}
throw; // Re-throw the original if we couldn't handle it
}
}
}
And you need to register it with StartUp.cs:
public class Startup
{
public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerfactory)
{
app.UseMiddleWare<ExceptionHandlerMiddleware>();
}
}
Alternatively, if you just want to return an arbitrary status code and aren't concerned with the Exception-based approach, you can use
return new HttpStatusCodeResult(400);
Update: as of .NET Core RC 2, the Http prefix is dropped. It is now:
return new StatusCodeResult(400);
The Microsoft.AspNet.Mvc.Controller base class exposes a HttpBadRequest(string) overload which takes an error message to return to the client. So from within a controller action, you could call:
return HttpBadRequest("Bad Request.");
Ultimately my nose says any private methods called from within a controller action should either be fully http-context-aware and return an IActionResult, or perform some other small task completely isolated from the fact that it's inside of an http pipeline. Granted this is my personal opinion, but a class that performs some piece of business logic should not be returning HTTP status codes, and instead should be throwing its own exceptions which can be caught and translated at the controller/action level.
There is no equivalent in ASP.NET Core itself. As others have said, the way to implement this is with a middleware and your own exceptions.
The Opw.HttpExceptions.AspNetCore NuGet package does exactly this.
Middleware and extensions for returning exceptions over HTTP, e.g. as ASP.NET Core Problem Details. Problem Details are a machine-readable format for specifying errors in HTTP API responses based on https://www.rfc-editor.org/rfc/rfc7807. But you are not limited to returning exception results as Problem Details, but you can create your own mappers for your own custom formats.
It is configurable and well documented.
Here is the list of provided exceptions out of the box:
4xx
400 BadRequestException
400 InvalidModelException
400 ValidationErrorException<T>
400 InvalidFileException
401 UnauthorizedException
403 ForbiddenException
404 NotFoundException
404 NotFoundException<T>
409 ConflictException
409 ProtectedException
415 UnsupportedMediaTypeException
5xx
500 InternalServerErrorException
500 DbErrorException
500 SerializationErrorException
503 ServiceUnavailableException
Here is an extended version of #muhammad-rehan-saeed answer.
It logs exceptions conditionaly and disables http cache.
If you use this and UseDeveloperExceptionPage, you should call UseDeveloperExceptionPage before this.
Startup.cs:
app.UseMiddleware<HttpExceptionMiddleware>();
HttpExceptionMiddleware.cs
/**
* Error handling: throw HTTPException(s) in business logic, generate correct response with correct httpStatusCode + short error messages.
* If the exception is a server error (status 5XX), this exception is logged.
*/
internal class HttpExceptionMiddleware
{
private readonly RequestDelegate next;
public HttpExceptionMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await this.next.Invoke(context);
}
catch (HttpException e)
{
var response = context.Response;
if (response.HasStarted)
{
throw;
}
int statusCode = (int) e.StatusCode;
if (statusCode >= 500 && statusCode <= 599)
{
logger.LogError(e, "Server exception");
}
response.Clear();
response.StatusCode = statusCode;
response.ContentType = "application/json; charset=utf-8";
response.Headers[HeaderNames.CacheControl] = "no-cache";
response.Headers[HeaderNames.Pragma] = "no-cache";
response.Headers[HeaderNames.Expires] = "-1";
response.Headers.Remove(HeaderNames.ETag);
var bodyObj = new {
Message = e.BaseMessage,
Status = e.StatusCode.ToString()
};
var body = JsonSerializer.Serialize(bodyObj);
await context.Response.WriteAsync(body);
}
}
}
HTTPException.cs
public class HttpException : Exception
{
public HttpStatusCode StatusCode { get; }
public HttpException(HttpStatusCode statusCode)
{
this.StatusCode = statusCode;
}
public HttpException(int httpStatusCode)
: this((HttpStatusCode) httpStatusCode)
{
}
public HttpException(HttpStatusCode statusCode, string message)
: base(message)
{
this.StatusCode = statusCode;
}
public HttpException(int httpStatusCode, string message)
: this((HttpStatusCode) httpStatusCode, message)
{
}
public HttpException(HttpStatusCode statusCode, string message, Exception inner)
: base(message, inner)
{
}
public HttpException(int httpStatusCode, string message, Exception inner)
: this((HttpStatusCode) httpStatusCode, message, inner)
{
}
}
I had better results with this code than with :
UseExceptionHandler:
automatically logs every "normal" exceptions (ex 404).
disabled in dev mode (when app.UseDeveloperExceptionPage is called)
cannot catch only specific exceptions
Opw.HttpExceptions.AspNetCore: logs exception when everything works fine
See also ASP.NET Core Web API exception handling
Starting from ASP.NET Core 3 you can use ActionResult to return HTTP status code:
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<ITEMS_TYPE> GetByItemId(int id)
{
...
if (result == null)
{
return NotFound();
}
return Ok(result);
}
More details are here: https://learn.microsoft.com/en-us/aspnet/core/web-api/action-return-types?view=aspnetcore-3.1

Categories