You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 18:42:40