RFC 9457错误响应标准
现代HTTP API错误处理的最佳实践遵循RFC 9457协议标准,该标准于2023年7月发布并取代了旧版RFC 7807。该规范定义了机器可读的错误响应格式,要求错误响应必须包含type(问题分类标识符)、title(人类可读的错误摘要)、status(HTTP状态码)和detail(具体错误描述)四个核心字段。以下是符合RFC 9457标准的典型错误响应结构:
{
"type": "https://example.com/errors/invalid-unit",
"title": "无效单位",
"status": 400,
"detail": "单位参数仅接受C(摄氏度)、F(华氏度)或K(开尔文)",
"instance": "/weather/London"
}
基础错误响应实现
在ASP.NET Core中,可通过ProblemDetails类直接构建标准错误响应。以下示例演示在天气API端点中处理无效温度单位的场景:
app.MapGet("/weather/{city}", async (string city, string? units) =>
{
var validUnits = new[] { "C", "F", "K" };
if (units != null && !validUnits.Contains(units.ToUpper()))
{
return Results.Problem(
title: "无效单位",
detail: #34;接受单位: {string.Join(", ", validUnits)}",
statusCode: StatusCodes.Status400BadRequest
);
}
// 正常业务逻辑
});
增强错误信息上下文
通过配置ProblemDetails服务可自动注入上下文信息。在Program.cs中添加以下配置:
builder.Services.AddProblemDetails(options =>
{
options.CustomizeProblemDetails = context =>
{
var httpContext = context.HttpContext;
context.ProblemDetails.Instance = #34;{httpContext.Request.Method} {httpContext.Request.Path}";
context.ProblemDetails.Extensions["requestId"] = httpContext.TraceIdentifier;
var activity = httpContext.Features.Get<IHttpActivityFeature>()?.Activity;
if (activity != null)
{
context.ProblemDetails.Extensions["traceId"] = activity.Id;
}
};
});
此配置将自动为所有错误响应添加请求方法、路径、唯一请求ID和分布式追踪ID,显著提升错误排查效率。
全局异常处理策略
对于企业级应用,推荐实现全局异常处理机制。首先创建自定义异常类型:
public class ProblemException : Exception
{
public string ErrorCode { get; }
public int StatusCode { get; }
public ProblemException(string errorCode, string message, int statusCode = 400)
: base(message)
{
ErrorCode = errorCode;
StatusCode = statusCode;
}
}
随后创建异常处理器:
internal sealed class ProblemExceptionHandler(
IProblemDetailsService problemDetailsService) : IExceptionHandler
{
public async ValueTask<bool> TryHandleAsync(
HttpContext context,
Exception exception,
CancellationToken cancellationToken)
{
if (exception is not ProblemException problemException)
return false;
var problemDetails = new ProblemDetails
{
Title = problemException.ErrorCode,
Detail = problemException.Message,
Status = problemException.StatusCode,
Instance = #34;{context.Request.Method} {context.Request.Path}"
};
context.Response.StatusCode = problemException.StatusCode;
return await problemDetailsService.TryWriteAsync(
new ProblemDetailsContext {
HttpContext = context,
ProblemDetails = problemDetails
});
}
}
最后注册中间件和服务:
// Program.cs
builder.Services.AddExceptionHandler<ProblemExceptionHandler>();
app.UseExceptionHandler();
实践建议与总结
两种实现方式各有适用场景:直接返回Problem结果适合简单端点,全局异常处理更适合复杂企业系统。无论选择何种方式,应确保:
- 始终返回符合RFC 9457的结构化响应
- 包含足够调试信息但排除敏感数据
- 保持HTTP状态码语义准确性
- 为不同类型错误定义唯一类型标识符
通过采用标准化的错误处理机制,不仅可以提升API的易用性和可维护性,还能显著改善客户端错误处理体验,为构建健壮的分布式系统奠定基础。