更流畅的asp.net api的错误返回

文摘   2024-11-12 07:34   日本  

Results.Problem 是 ASP.NET Core 提供的一种标准化错误响应生成方法,用于创建符合 RFC 7807 标准的错误响应(Problem Details)。它可用于 Web API 中返回结构化的错误信息,包含 type、title、status 和 detail 等字段,帮助客户端理解并处理错误。通过参数定制,Results.Problem 能清晰地描述错误类型、状态码、详细信息和实例路径,提升了 API 的一致性和可维护性,使客户端处理错误更加高效,特别适合构建现代化 RESTful API。

var builder = WebApplication.CreateBuilder(args);var app = builder.Build();app.MapGet("/test", () =>{    return Results.Problem(type: "Bad Request",         title: "无效的编号",         detail: "编号格式为:N00000,N+5位数字",         statusCode: StatusCodes.Status400BadRequest);});
app.Run();

运行结果:


扩展ProblemDetails

下面的代码展示了在 ASP.NET Core 中通过 Results.Problem 扩展来生成自定义的标准化错误响应。此扩展使用了 Problem Details 格式,并自定义了错误细节的处理方式,增强了错误信息的结构化和调试性。

首先,通过 builder.Services.AddProblemDetails 注册了 Problem Details 服务,并在 CustomizeProblemDetails 配置中自定义了 Problem Details 的内容。在此配置中,指定了错误实例 (Instance) 的详细格式,包括请求协议、方法和路径。同时,添加了客户端请求的跟踪信息,如 requestId(请求唯一标识符)、traceId(跟踪标识符)和 clientIP(客户端 IP 地址),使错误响应更具上下文信息。

为捕获和处理自定义异常类型,代码创建了一个名为 ProblemException 的自定义异常类,该类包含 Error 和 Message 属性,用于存储特定的错误信息。接着,实现了 IExceptionHandler 接口的 ProblemExceptionHandler 类,该类负责拦截异常,并通过 ProblemDetailsService 生成标准的 Problem Details 响应。在此过程中,TryHandleAsync 方法捕获并检测是否为 ProblemException 异常,如果是,则创建一个自定义的 ProblemDetails 对象,将 Status 设置为 400(Bad Request),并根据 ProblemException 实例的 Error 和 Message 属性填充 Title 和 Detail 字段。最后,通过 ProblemDetailsService.TryWriteAsync 方法将 Problem Details 写入响应,以确保客户端接收到结构化的错误信息。

在应用程序中,定义了一个触发 ProblemException 异常的路由 /test,当客户端请求该路由时,抛出一个 ProblemException 异常,返回包含自定义错误信息的 Problem Details 响应。通过这种方式,客户端可以接收到详细的错误上下文和结构化的错误信息,有助于快速识别和调试问题。

具体代码:
using Microsoft.AspNetCore.Diagnostics;using Microsoft.AspNetCore.Http.Features;using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);builder.Services.AddProblemDetails(opt =>{ opt.CustomizeProblemDetails = context => { context.ProblemDetails.Instance = $"{context.HttpContext.Request.Protocol} {context.HttpContext.Request.Method} {context.HttpContext.Request.Path}"; context.ProblemDetails.Extensions.TryAdd("requestId", context.HttpContext.TraceIdentifier);
var activity = context.HttpContext.Features.Get<IHttpActivityFeature>()?.Activity; context.ProblemDetails.Extensions.TryAdd("traceId", activity?.Id);
if (context.HttpContext.Connection != null && context.HttpContext.Connection.RemoteIpAddress != null) { context.ProblemDetails.Extensions.TryAdd("clientIP", context.HttpContext.Connection.RemoteIpAddress?.ToString()); } };});builder.Services.AddExceptionHandler<ProblemExceptionHandler>();
var app = builder.Build();
app.MapGet("/test", () =>{ throw new ProblemException(error: "无效的编号", message: "编号格式为:N00000,N+5位数字");
});app.UseExceptionHandler();app.Run();
public class ProblemException : Exception{ public string Error { get; } public ProblemException(string error, string message) : base(message) { Error = error; }}public class ProblemExceptionHandler : IExceptionHandler{ private readonly IProblemDetailsService _problemDetailsService; public ProblemExceptionHandler(IProblemDetailsService problemDetailsService) { _problemDetailsService = problemDetailsService; } public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken) { if (exception is not ProblemException problemException) { return true; } var problemDetails = new ProblemDetails { Status = StatusCodes.Status400BadRequest, Title = problemException.Error, Detail = problemException.Message, Type = "Bad Request" }; httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; return await _problemDetailsService.TryWriteAsync(new ProblemDetailsContext { HttpContext = httpContext, ProblemDetails = problemDetails }); }}

实现效果:

美中不足就是在抛出异常时,平台会输出fail日志。

桂迹
分享原创,记录痕迹!
 最新文章