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

C# JsonPatchDocument能否在ModelState无效时设置自定义错误消息

C# JsonPatchDocument自定义DateTime类型校验错误消息实现

场景说明

C#接口使用JsonPatchDocument处理补丁请求时,若传入类似77-77-7777的无效日期值,接口默认返回的400错误提示为固定格式:

{
     "errors": {
         "AccountDto": [
             "The value '77-77-7777' is invalid for target location."
          ]
    },
    "title": "One or more validation errors occurred.",
    "status": 400
}

该提示无法体现具体出错属性,需要将所有DateTime类型属性的该类校验错误调整为{propertyName} input value '{invalidValue}' is an invalid date格式。

实现方式

这个错误是JsonPatch在执行路径值类型转换阶段写入ModelState的,不属于普通数据注解校验范围,可以通过自定义ApiBehaviorOptions的无效模型状态响应逻辑实现消息替换,不需要修改原有接口业务代码。

  • 首先在Program.cs服务配置段,添加自定义的InvalidModelState处理逻辑:
builder.Services.Configure<ApiBehaviorOptions>(options =>
{
    options.InvalidModelStateResponseFactory = context =>
    {
        foreach (var key in context.ModelState.Keys)
        {
            var stateEntry = context.ModelState[key];
            if (stateEntry?.Errors == null) continue;

            for (var i = 0; i < stateEntry.Errors.Count; i++)
            {
                var error = stateEntry.Errors[i];
                // 匹配JsonPatch类型转换失败的默认错误模板
                if (!string.IsNullOrEmpty(error.ErrorMessage) 
                    && error.ErrorMessage.StartsWith("The value '") 
                    && error.ErrorMessage.EndsWith("' is invalid for target location."))
                {
                    // 提取用户传入的无效原始值
                    var invalidValue = error.ErrorMessage
                        .Replace("The value '", string.Empty)
                        .Replace("' is invalid for target location.", string.Empty);

                    // 获取当前Patch操作对应的目标属性类型
                    var targetPropType = GetPatchTargetPropType(context, key);
                    if (targetPropType == typeof(DateTime) || targetPropType == typeof(DateTime?))
                    {
                        // 替换为自定义错误消息
                        var propName = key.Split('.').Last();
                        stateEntry.Errors[i] = new ModelError($"{propName} input value '{invalidValue}' is an invalid date");
                    }
                }
            }
        }

        // 保留原有400响应结构,仅替换错误提示内容
        return new BadRequestObjectResult(new ValidationProblemDetails(context.ModelState)
        {
            Status = StatusCodes.Status400BadRequest,
            Title = "One or more validation errors occurred."
        });
    };
});
  • 添加辅助方法,用来解析Patch路径对应的目标属性类型:
static Type? GetPatchTargetPropType(ActionContext context, string modelStateKey)
{
    try
    {
        // 查找接口参数中定义的JsonPatchDocument对应的目标Dto类型
        var patchParameter = context.ActionDescriptor.Parameters
            .FirstOrDefault(p => p.ParameterType.IsGenericType 
                && p.ParameterType.GetGenericTypeDefinition() == typeof(JsonPatchDocument<>));
        if (patchParameter == null) return null;

        var dtoType = patchParameter.ParameterType.GetGenericArguments()[0];
        var propName = modelStateKey.Split('.').Last();
        // 查找属性对应类型,若使用了自定义序列化命名,可在此处增加名称匹配逻辑
        return dtoType.GetProperty(propName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance)?.PropertyType;
    }
    catch
    {
        return null;
    }
}

补充说明

  • 如果项目使用了小驼峰、下划线等自定义Json命名策略,反射查找属性时需要对应做名称映射,匹配[JsonPropertyName]等特性配置的属性名
  • 需要支持DateTimeOffset类型校验的话,只要在类型判断逻辑中追加typeof(DateTimeOffset)、typeof(DateTimeOffset?)即可
  • 该逻辑仅替换DateTime类型对应的JsonPatch转换错误,不影响其他类型校验、其他业务校验的原有提示

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 09:54:26