【问题标题】:Extending ModelState to output my own message - how do I get model state value扩展 ModelState 以输出我自己的消息 - 如何获取模型状态值
【发布时间】:2020-01-13 11:01:47
【问题描述】:

我为 ModelStateDictionary 编写了自己的扩展,但遇到了显示输入值的问题。

这是我的尝试:

public static string ToLoggingFormat(this ModelStateDictionary modelState)
{
    var sb = new StringBuilder();
    sb.AppendLine("Input validation error occurred:");

    foreach (var ms in modelState.Values)
    {
        foreach (ModelError error in ms.Errors)
        {
            sb.AppendLine(error.ErrorMessage);
        }

        if (!string.IsNullOrEmpty(ms.AttemptedValue)) { 
            sb.AppendLine($"Attempted Value: {ms.AttemptedValue}");
        }
    }

    return sb.ToString();
}

我的问题是 ms.AttemptedValue 始终为空。请问有什么想法吗?我也尝试过 ms.RawValue ,但这也是空的。我传入的值无效,因为它超过了限制,因此不为空。


我正在按照建议添加自定义 XML 输入格式化程序!

public class ModelStateXmlInputFormatter : XmlSerializerInputFormatter
{
    public ModelStateXmlInputFormatter(MvcOptions options) : base(options)
    {
    }
    public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
    {
        var result = await base.ReadRequestBodyAsync(context);
        foreach (var property in context.ModelType.GetProperties())
        {
            var propValue = property.GetValue(result.Model, null);
            var propAttemptValue = property.GetValue(result.Model, null)?.ToString();
            context.ModelState.SetModelValue(property.Name, propValue, propAttemptValue);
        }

        return result;
    }

我是这样添加的,

services.AddMvc(options =>
{
    // Remove JSON input
    options.OutputFormatters.RemoveType(typeof(JsonOutputFormatter));
    options.InputFormatters.RemoveType(typeof(JsonInputFormatter));
    options.ReturnHttpNotAcceptable = true;

    // Add our customer XmlInputFormatter
        options.InputFormatters.Add(new ModelStateXmlInputFormatter(options));
        options.OutputFormatters.Add(new XmlSerializerOutputFormatter());

})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

但扩展仍然为 ms.AttemptedValue 返回 null。有任何想法吗? :))

这就是我使用扩展的方式:

[HttpPost]
[Route("myrequest")]
public async Task<IActionResult> MyRequest([FromBody] MyRequestType request)
{
    if (!ModelState.IsValid) {
          Logger.LogThis(ModelState.ToLoggingFormat());
    }
}

【问题讨论】:

  • 您的值不会出现在模型错误中吗?
  • ErrorMessage 是设置消息,而不是输入值。例如“姓氏字段超过了允许的最大字符数。”我试图以“'Smithhhhhhhh' 的值太长”为例。
  • 不可能,除非您从模型中删除 MaxLength 数据注释,并在代码中手动验证(例如,如果 model.MyLongProperty.Length > 100){ return "The value" + model.MyLongProperty + "太长了"; } 你明白了
  • 太疯狂了。那么 AttemptedValue 属性有什么意义呢?! >:(
  • 这将是微软天才们的问题

标签: c# asp.net-mvc asp.net-core modelstate


【解决方案1】:

对于这个问题,它发生在JsonInputFormatter。在 Asp.Net Core 中,有两种方法可以将请求绑定到模型,FromForm 与模型绑定,FromBodyJsonInputFormatter。对于模型绑定,它调用context.ModelState.SetModelValue 来设置RawValueAttemptedValue 的值。但是,JsonInputFormatter 没有实现这个功能。

你需要通过自定义JsonInputFormatter来实现这个功能

  1. ModelStateJsonInputFormatter

    public class ModelStateJsonInputFormatter : JsonInputFormatter
    {
        public ModelStateJsonInputFormatter(ILogger<ModelStateJsonInputFormatter> 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 property in context.ModelType.GetProperties())
            {
                var propValue = property.GetValue(result.Model, null);
                var propAttemptValue = property.GetValue(result.Model, null)?.ToString();
                context.ModelState.SetModelValue(property.Name, propValue, propAttemptValue);
            }
            return result;
        }
    }
    
  2. 注册ModelStateJsonInputFormatter

    services.AddMvc(opt => {
        var serviceProvider = services.BuildServiceProvider();
        var modelStateJsonInputFormatter = new ModelStateJsonInputFormatter(
                    serviceProvider.GetRequiredService<ILoggerFactory>().CreateLogger<ModelStateJsonInputFormatter>(),
                    serviceProvider.GetRequiredService<IOptions<MvcJsonOptions>>().Value.SerializerSettings,
                    serviceProvider.GetRequiredService<ArrayPool<char>>(),
                    serviceProvider.GetRequiredService<ObjectPoolProvider>(),
                    opt,
                    serviceProvider.GetRequiredService<IOptions<MvcJsonOptions>>().Value
            );
        opt.InputFormatters.Insert(0, modelStateJsonInputFormatter);
    }).SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Latest);
    

【讨论】:

  • 嗨!谢谢,这看起来很有希望,但我是为 XML 做的,无法让它与我的扩展一起工作。请看我的编辑!再次感谢
【解决方案2】:

试试这个:

foreach (var ms in modelState.Keys)
    {
        var value = modelState[ms];
        foreach (ModelError error in value.Errors)
        {
            sb.AppendLine(error.ErrorMessage);
        }

        if (!string.IsNullOrEmpty(value.Value.AttemptedValue)) { 
            sb.AppendLine($"Attempted Value: {value.Value.AttemptedValue}");
        }
    }

未测试...

【讨论】:

  • 这不是和OP写的一模一样吗?
  • 这不起作用,因为 ModelState 不包含 value.Value,只有 value.AttemptedValue 也会产生 null。 :( 感谢您的尝试。
猜你喜欢
  • 2023-03-18
  • 2020-11-23
  • 1970-01-01
  • 1970-01-01
  • 2020-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多