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

如何自定义ASP.NET REST端点错误请求(400)的响应体内容?

问题描述

我有一个REST API,端点代码如下:

[Route("create")]
[HttpPost]
public object CreateStuff ([FromBody] MyParams args)
{
    //...
}

为简化说明,假设MyParams是仅包含一个枚举类型字段的对象:

public enum SomeType {
    One = 1,
    Two = 2,
}

public class MyParams {
    public SomeType Test {get; set;}
}

使用如下请求体调用该端点可正常工作:

{
    "Test": "Two"
}

但如果传入无效值(例如"Test": "Invented value"),接口会返回400 Bad Request响应,响应体如下:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "00-c44995191a9c99604f9944c2b1542245-caa753444c07f89e-00",
    "errors": {
        "args": [
            "The args field is required."
        ],
        "$.Test": [
            "The JSON value could not be converted to System.Nullable`1[SomeType]. Path: $.Test| LineNumber: 2 | BytePositionInLine: 27."
        ]
    }
}

请问是否可以将该响应体修改为自定义内容?


解决方案

当然可以自定义这个响应体,以下是几种实用的实现方式:

1. 自定义枚举模型绑定器

针对枚举类型编写专属绑定逻辑,捕获转换失败场景并返回自定义错误信息:

public class EnumModelBinder<TEnum> : IModelBinder where TEnum : struct, Enum
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if (valueProviderResult == ValueProviderResult.None)
        {
            return Task.CompletedTask;
        }

        var value = valueProviderResult.FirstValue;
        if (!Enum.TryParse<TEnum>(value, ignoreCase: true, out var result))
        {
            var validValues = string.Join(", ", Enum.GetNames<TEnum>());
            bindingContext.ModelState.TryAddModelError(
                bindingContext.ModelName,
                $"字段 {bindingContext.ModelName} 传入无效值,允许的值为:{validValues}"
            );
            return Task.CompletedTask;
        }

        bindingContext.Result = ModelBindingResult.Success(result);
        return Task.CompletedTask;
    }
}

在MyParams的枚举字段上绑定该实现:

public class MyParams {
    [ModelBinder(typeof(EnumModelBinder<SomeType>))]
    public SomeType Test {get; set;}
}

2. 全局自定义验证过滤器

创建全局过滤器统一拦截模型验证错误,替换为自定义响应格式:

public class CustomValidationFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            var customErrors = new Dictionary<string, List<string>>();
            foreach (var key in context.ModelState.Keys)
            {
                var errors = context.ModelState[key].Errors
                    .Select(e => 
                    {
                        if (e.ErrorMessage.Contains("JSON value could not be converted"))
                        {
                            var targetProperty = context.ActionArguments.Values
                                .FirstOrDefault()?.GetType()
                                .GetProperty(key);
                            if (targetProperty != null && targetProperty.PropertyType.IsEnum)
                            {
                                var validValues = string.Join(", ", Enum.GetNames(targetProperty.PropertyType));
                                return $"字段 {key} 无效,允许的值:{validValues}";
                            }
                        }
                        return e.ErrorMessage;
                    })
                    .ToList();
                customErrors[key] = errors;
            }

            var customResponse = new
            {
                code = "PARAM_VALID_FAILED",
                message = "请求参数验证不通过",
                errorDetails = customErrors
            };

            context.Result = new JsonResult(customResponse)
            {
                StatusCode = StatusCodes.Status400BadRequest
            };
        }
    }

    public void OnActionExecuted(ActionExecutedContext context) { }
}

在Program.cs中注册全局过滤器:

builder.Services.AddControllers(options =>
{
    options.Filters.Add<CustomValidationFilter>();
});

3. 配置API行为自定义响应

通过ConfigureApiBehaviorOptions直接覆盖默认的无效模型响应逻辑:

builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
    })
    .ConfigureApiBehaviorOptions(options =>
    {
        options.InvalidModelStateResponseFactory = context =>
        {
            var customErrors = context.ModelState
                .Where(kv => kv.Value.Errors.Any())
                .ToDictionary(
                    kv => kv.Key,
                    kv => kv.Value.Errors.Select(e => 
                    {
                        if (e.Exception is JsonException jsonEx && jsonEx.Message.Contains("converted to"))
                        {
                            var enumTypeName = jsonEx.Path.Split('.').Last();
                            var enumType = Type.GetType($"你的命名空间.{enumTypeName}");
                            if (enumType != null && enumType.IsEnum)
                            {
                                var validValues = string.Join(", ", Enum.GetNames(enumType));
                                return $"无效枚举值,允许的值:{validValues}";
                            }
                        }
                        return e.ErrorMessage;
                    }).ToList()
                );

            var customResponse = new
            {
                status = 400,
                message = "参数错误",
                errors = customErrors
            };

            return new JsonResult(customResponse) { StatusCode = 400 };
        };
    });

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 22:10:30