.NET 7中如何基于FluentResults实现集中式错误处理?
用FluentResults替代异常实现统一ProblemDetails错误处理
之前用异常时,能通过中间件统一返回ProblemDetails格式的错误响应,但改用FluentResults后,不想在每个控制器动作里重复写判断Result是否失败、手动构建ProblemDetails的代码,希望实现类似之前的统一错误处理逻辑。
解决方案
1. 自定义带HTTP元数据的错误类型
FluentResults的默认Error类不包含HTTP状态码、标题等信息,我们需要自定义一个扩展类来携带这些元数据:
public class HttpError : Error { public int StatusCode { get; } public string Title { get; } public string Type { get; } public HttpError(string message, int statusCode, string title, string type) : base(message) { StatusCode = statusCode; Title = title; Type = type; } // 封装常见错误的快捷创建方法 public static HttpError NotFound(string message) { return new HttpError( message, StatusCodes.Status404NotFound, "指定的资源未找到", "https://tools.ietf.org/html/rfc7231#section-6.5.4" ); } public static HttpError InternalServerError(string message) { return new HttpError( message, StatusCodes.Status500InternalServerError, "服务器内部错误", "https://tools.ietf.org/html/rfc7231#section-6.6.1" ); } }
2. 编写统一处理FluentResults的Action过滤器
创建一个Action过滤器,自动识别控制器返回的Result对象,当结果失败时,自动构建ProblemDetails并返回对应HTTP响应:
public class FluentResultFilter : IActionFilter { public void OnActionExecuting(ActionExecutingContext context) { } public void OnActionExecuted(ActionExecutedContext context) { // 检测动作返回的是否是FluentResults的Result类型 if (context.Result is ObjectResult objectResult && objectResult.Value is Result result) { if (result.IsFailed) { // 优先获取自定义的HttpError,没有则使用默认500错误 var httpError = result.Errors.OfType<HttpError>().FirstOrDefault(); var problemDetails = new ProblemDetails { Status = httpError?.StatusCode ?? StatusCodes.Status500InternalServerError, Title = httpError?.Title ?? "服务器内部错误", Type = httpError?.Type ?? "https://tools.ietf.org/html/rfc7231#section-6.6.1", Detail = result.Errors.FirstOrDefault()?.Message ?? "发生未知错误" }; // 替换原响应结果为ProblemDetails context.Result = new ObjectResult(problemDetails) { StatusCode = problemDetails.Status }; } } } }
3. 注册过滤器到ASP.NET Core管道
在Program.cs中添加过滤器,让所有控制器动作都能被统一处理:
builder.Services.AddControllers(options => { options.Filters.Add<FluentResultFilter>(); });
4. 业务逻辑中返回自定义错误
在业务层直接返回HttpError,无需在控制器中额外处理:
public Result<Resource> GetResource(int id) { var resource = _resourceRepository.GetById(id); if (resource == null) { return Result.Fail(HttpError.NotFound($"ID为{id}的资源不存在")); } return Result.Ok(resource); }
5. 控制器简化代码
控制器现在只需直接返回业务层的Result,过滤器会自动处理失败情况:
[HttpGet("{id}")] public IActionResult GetResource(int id) { var result = _resourceService.GetResource(id); return Ok(result); }
效果说明
这样就完全替代了之前的异常中间件逻辑:
- 业务层不再抛出异常,而是返回包含错误元数据的
Result - 过滤器统一处理所有失败的
Result,自动生成符合规范的ProblemDetails响应 - 彻底移除了控制器中重复的错误判断代码
内容的提问来源于stack exchange,提问作者Simon Great
相关产品推荐
相关产品推荐

