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

.NET5环境下Asp.Net Core API如何优雅实现集中式ProblemDetails校验错误处理

实现方案

方案1:重写InvalidModelStateResponseFactory(推荐)

ASP.NET Core 针对标记了[ApiController]特性的控制器默认会自动执行模型校验,你不需要在每个Action方法中手动判断ModelState.IsValid,只需要替换默认的无效模型响应工厂即可实现集中化处理。
在Startup.cs的ConfigureServices方法中添加如下配置:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.InvalidModelStateResponseFactory = context =>
        {
            // 构造标准ProblemDetails结构
            var problemDetails = new ProblemDetails
            {
                Type = "parameter_validation_failure",
                Title = "参数校验失败",
                Status = StatusCodes.Status400BadRequest,
                Detail = "请求提交的参数不符合预设校验规则",
                Instance = context.HttpContext.Request.Path
            };

            // 构造自定义Invalid Fields扩展字段
            var invalidFields = new List<object>();
            foreach (var (fieldKey, fieldState) in context.ModelState)
            {
                if (!fieldState.Errors.Any()) continue;
                
                // 读取属性DisplayName
                var displayName = fieldKey;
                var parameter = context.ActionDescriptor.Parameters.FirstOrDefault(p => 
                    p.BindingInfo?.BinderModelName == fieldKey || p.Name == fieldKey);
                if (parameter != null)
                {
                    var metadata = context.ModelMetadata.GetMetadataForType(parameter.ParameterType);
                    var propertyMeta = metadata.Properties.FirstOrDefault(p => p.Name == fieldKey);
                    if (propertyMeta != null)
                    {
                        displayName = propertyMeta.DisplayName ?? fieldKey;
                    }
                }

                // 收集每个字段的错误信息、错误码
                foreach (var error in fieldState.Errors)
                {
                    string errorCode = "InvalidParameter";
                    // 读取自定义ValidationAttribute中存储的错误码
                    if (error.Exception is CustomValidationException customEx)
                    {
                        errorCode = customEx.ErrorCode;
                    }
                    invalidFields.Add(new
                    {
                        FieldName = fieldKey,
                        DisplayName = displayName,
                        ErrorMessage = error.ErrorMessage,
                        ErrorCode = errorCode
                    });
                }
            }
            // 添加扩展字段
            problemDetails.Extensions.Add("Invalid Fields", invalidFields);

            return new BadRequestObjectResult(problemDetails)
            {
                ContentTypes = { "application/problem+json" }
            };
        };
    });

方案2:自定义ValidationAttribute绑定错误码

如果需要将错误码和校验逻辑绑定,不需要在响应工厂中统一匹配,可以在自定义校验特性中直接存储错误码,示例如下:

public class CustomNotNullAttribute : ValidationAttribute
{
    // 自定义错误码字段
    public string ErrorCode { get; set; }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (value == null)
        {
            // 返回携带错误码的校验结果
            return new ValidationResult(ErrorMessage, new[] { validationContext.MemberName })
            {
                CustomState = new { ErrorCode = ErrorCode }
            };
        }
        return ValidationResult.Success;
    }
}

在模型属性上使用时直接指定错误码:

public class UserCreateRequest
{
    [CustomNotNull(ErrorMessage = "用户名不能为空", ErrorCode = "USER_001")]
    [DisplayName("用户名")]
    public string UserName { get; set; }
}

在响应工厂中可以直接读取存储的错误码:

if (fieldState.Errors.FirstOrDefault()?.CustomState is dynamic state)
{
    errorCode = state.ErrorCode;
}

方案3:自定义全局Action过滤器

如果需要更灵活的扩展逻辑(比如校验失败日志上报),可以实现全局Action过滤器:

  1. 先关闭默认的模型校验过滤器:
services.Configure<ApiBehaviorOptions>(options => 
{
    options.SuppressModelStateInvalidFilter = true;
});
  1. 实现自定义校验过滤器:
public class GlobalModelValidateFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            // 此处构造ProblemDetails的逻辑和方案1完全一致
            // 最终将context.Result赋值为构造好的BadRequestObjectResult即可
        }
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
    }
}
  1. 注册全局过滤器:
services.AddControllers(options =>
{
    options.Filters.Add<GlobalModelValidateFilter>();
});

内容的提问来源于stack exchange,提问作者Vivek Nuna

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 12:15:04