如何为本地化目的覆盖 Json.Net 模型绑定异常消息?

How to override Json.Net model binding exception messages for localization purposes?

我已经使用 ModelBindingMessageProvider.SetValueIsInvalidAccessor 和其他 ModelBindingMessageProvider 值将所有模型绑定消息覆盖为 return 我的自定义资源字符串。

然后我发现了一个可悲的事实。如果我的 API 控制器接收到的数据为 JSON,则不会使用 ModelBindingMessageProvider 验证消息。相反,Json.Net 启动,我得到类似这样的响应:

  "errors": {
    "countryId": [
      "Input string '111a' is not a valid number. Path 'countryId', line 3, position 23."
    ]
  },

我查看了 Json.Net 的 GitHub 来源 - 事实上,它似乎有用行号等定义的确切错误消息。

因此,ModelState 设法将它们拉入,而不是使用自己的 ModelBindingMessageProvider 消息。

我试图禁用 Json.Net 错误处理:

.AddJsonOptions(options =>
                {
                 ...
                    options.SerializerSettings.Error = delegate (object sender, Newtonsoft.Json.Serialization.ErrorEventArgs args)
                    {
                        // ignore them all
                        args.ErrorContext.Handled = true;
                    };
                })

但这没什么区别。

有什么方法可以捕获这些 Json 反序列化错误并将它们重定向到 ModelBindingMessageProvider,以便我的本地化消息能够正常工作?

Is there any way to catch these Json deserialization errors and redirect them to ModelBindingMessageProvider, so that my localized messages would work?

不,模型绑定和json 输入不同,模型绑定是针对FromForm,而JsonInputFormatter 是针对FromBody。他们遵循不同的方式。您无法自定义来自 ModelBindingMessageProvider 的错误消息。

对于 JSON,您可以实现自己的 JsonInputFormatter 并将错误消息更改为

  1. CustomJsonInputFormatter

    public class CustomJsonInputFormatter : JsonInputFormatter
    {
        public CustomJsonInputFormatter(ILogger<CustomJsonInputFormatter> logger
            , JsonSerializerSettings serializerSettings
            , ArrayPool<char> charPool
            , ObjectPoolProvider objectPoolProvider
            , MvcOptions options
            , MvcJsonOptions jsonOptions) 
            : base(logger, serializerSettings, charPool, objectPoolProvider, options, jsonOptions)
        {
        }
        public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
        {           
            var result = await base.ReadRequestBodyAsync(context);
    
            foreach (var key in context.ModelState.Keys)
            {
                for (int i = 0; i < context.ModelState[key].Errors.Count; i++)
                {
                    var error = context.ModelState[key].Errors[i];
                    context.ModelState[key].Errors.Add($"This is translated error { error.ErrorMessage }");
                    context.ModelState[key].Errors.Remove(error);
                }
            }
            return result;
        }
    }
    
  2. 注册CustomJsonInputFormatter

        services.AddMvc(options =>
        {                
            var serviceProvider = services.BuildServiceProvider();
            var customJsonInputFormatter = new CustomJsonInputFormatter(
                     serviceProvider.GetRequiredService<ILoggerFactory>().CreateLogger<CustomJsonInputFormatter>(),
                     serviceProvider.GetRequiredService<IOptions<MvcJsonOptions>>().Value.SerializerSettings,
                     serviceProvider.GetRequiredService<ArrayPool<char>>(),
                     serviceProvider.GetRequiredService<ObjectPoolProvider>(),
                     options,
                     serviceProvider.GetRequiredService<IOptions<MvcJsonOptions>>().Value
                );
            options.InputFormatters.Insert(0, customJsonInputFormatter);
            }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }
    
  3. 将本地化服务注册到 CustomJsonInputFormatter 以自定义错误消息。