.NET 8 Web API自定义异常处理器在Swagger与Postman中表现不一致的问题求助
.NET 8 Web API自定义异常处理器在Swagger与Postman中表现不一致的问题求助
大家好,我最近在搞.NET 8 Web API的错误管理,想找个优雅的方案确保出错时始终返回ProblemDetails格式的响应。我用的是默认的WeatherForecast API模板,已经实现了自定义异常处理器,但发现它的表现不太稳定:
- 用Postman调用触发错误时,能正常返回正确的
ProblemDetails、状态码和头部信息; - 但用Swagger的“Try it out”功能调用时,却返回500内部错误,还带了异常详情而非
ProblemDetails,头部信息也不对。
排查后发现,我的GlobalExceptionHandler类(实现了IExceptionHandler接口)里,ProblemDetailsService.TryWriteAsync(...)方法在Swagger调用时返回false,但Postman调用时返回true。于是我加了失败分支,尝试直接把ProblemDetailsContext写入httpContext.Response,结果遇到了这个仅在Swagger调用时出现的异常:
Serialization and deserialization of 'System.Type' instances is not supported. Path: $.HttpContext.Features.Key."
我完全搞不懂为什么会这样,想请教各位怎么才能让错误管理在不同调用方式下表现一致?
以下是我的相关代码:
GlobalExceptionHandler 实现
public class GlobalExceptionHandler : IExceptionHandler { private readonly IProblemDetailsService _problemDetailsService; public GlobalExceptionHandler(IProblemDetailsService problemDetailsService) { if (problemDetailsService == null) throw new ArgumentException(nameof(problemDetailsService)); _problemDetailsService = problemDetailsService; } public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken) { var message = exception switch { ArgumentException => ((ArgumentException)exception).Message, ValidationException => ((ValidationException)exception).Message, _ => $"Internal Server Error ({exception.GetType().Name})" }; int status = exception switch { ArgumentException => (int)HttpStatusCode.BadRequest, ValidationException => (int)HttpStatusCode.BadRequest, _ => (int)HttpStatusCode.InternalServerError }; ProblemDetails problemDetails = new() { Title = $"Bad Request: {exception.GetType().Name}", // human-readable summary of the problem type Detail = message, //detailed explanation specific to the problem Status = status, Instance = httpContext.Request.GetEncodedUrl(), Type = exception.HelpLink }; var errorcontext = new ProblemDetailsContext() { HttpContext = httpContext, ProblemDetails = problemDetails, //Exception = exception, AdditionalMetadata = null }; httpContext.Response.Clear(); httpContext.Response.StatusCode = status; var written = await _problemDetailsService.TryWriteAsync(errorcontext); if (!written) { try { await httpContext.Response.WriteAsJsonAsync<ProblemDetailsContext>(errorcontext); written = true; } catch (Exception ex) { Console.WriteLine(ex.ToString()); written = false; } } return written; } }
Program.cs 配置
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System.Net; using System; using System.ComponentModel.DataAnnotations; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Mvc.ModelBinding; using System.Net.Http; using Microsoft.AspNetCore.Diagnostics; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); // Add services to the container. builder.Services.AddExceptionHandler<ProblemDetailsScratchPad.GlobalExceptionHandler>(); // Using a custom exception handler that converts the exception into a Problem Details builder.Services.AddProblemDetails(); // required for using the exception handler with the custom problem details exception handler code because it adds the services required for Problem Details // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseExceptionHandler(opt => { }); //adding the custom GlobalExceptionHandler to the app's pipeline app.UseAuthorization(); app.MapControllers(); app.Run();
WeatherForecastController 测试代码
[ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { private readonly ILogger<WeatherForecastController> _logger; public WeatherForecastController(ILogger<WeatherForecastController> logger) { _logger = logger; } [HttpGet("GetWeatherForecast")] public ActionResult<IEnumerable<object>> GetWeatherForecast() { throw new ArgumentException("Please provide a valid value for a week day: between 0 and 6"); } [HttpPost("PostWeatherForecastDTO")] public ActionResult<IEnumerable<object>> PostWeatherForecastDTO() { throw new Exception("Please provide a valid value for a week day: between 0 and 6"); } }
备注:内容来源于stack exchange,提问作者Frinavale
相关产品推荐
相关产品推荐

