【问题标题】:Using [JsonProperty("name")] in ModelState.Errors在 ModelState.Errors 中使用 [JsonProperty("name")]
【发布时间】:2017-03-31 03:19:36
【问题描述】:

我们有几个模型通过 JsonProperty 覆盖名称,但是当我们通过 ModelState 收到验证错误时,这会导致问题。例如:

class MyModel
{
    [JsonProperty("id")]
    [Required]
    public string MyModelId {get;set;}
}

class MyModelController
{
    public IHttpActionResult Post([FromBody] MyModel model)
    {
        if (!ModelState.IsValid)
        {
            return HttpBadRequest(ModelState);
        }

        /* etc... */
    }
}

上面的帖子将返回错误The MyModelId field is required.,这是不准确的。我们想说The id field is required.。我们尝试使用[DataMember(Name="id")],但得到了相同的结果。

问题 1:除了在每个 [Required] 属性上提供我们自己的错误消息之外,我们还有什么方法可以让 ModelState 错误显示 JSON 属性名称而不是 C# 属性名称? p>

-- 更新--

我一直在玩这个,发现了一种“自己动手”的方法,可以使用自定义属性名称重新创建错误消息。我真的希望有一种内置的方法可以做到这一点,但这似乎可以完成这项工作......

https://gist.github.com/Blackbaud-JasonTremper/b64dc6ddb460afa1698daa6d075857e4

问题 2:是否可以假定 ModelState.Key 与 <parameterName>.<reflectedProperty> 语法匹配,或者是否存在不正确的情况?

问题 3:有没有比通过[DataMember][JsonProperty] 属性上的反射进行搜索更简单的方法来确定 JSON 参数名称应该是什么?

【问题讨论】:

    标签: c# asp.net-web-api2


    【解决方案1】:

    您是否尝试过使用DisplayName 属性?

    displayname attribute vs display attribute

    此外,您可以将错误消息分配给[Required] 属性。

    [Required(ErrorMessage = "Name is required")]

    【讨论】:

    • 我确实尝试了 [DisplayName],但没有成功。但是,根据您的链接, [Display] 属性完成了这项工作(也许这就是您的意思?)。谢谢!
    【解决方案2】:

    我也遇到了这个问题,我修改了您链接中的一些代码以适应我的 WebAPI。 modelState 还将存储旧键,即模型的变量名称,加上 Json 属性名称。

    1. 首先,创建过滤器ValidateModelStateFilter
    2. 在控制器方法上方添加[ValidateModelStateFilter]

    过滤器源码:

    public class ValidateModelStateFilter : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            var descriptor = actionContext.ActionDescriptor;
            var modelState = actionContext.ModelState;
    
            if (descriptor != null)
            {
                var parameters = descriptor.GetParameters();
    
                var subParameterIssues = modelState.Keys
                                                   .Where(s => s.Contains("."))
                                                   .Where(s => modelState[s].Errors.Any())
                                                   .GroupBy(s => s.Substring(0, s.IndexOf('.')))
                                                   .ToDictionary(g => g.Key, g => g.ToArray());
    
                foreach (var parameter in parameters)
                {
                    var argument = actionContext.ActionArguments[parameter.ParameterName];
    
                    if (subParameterIssues.ContainsKey(parameter.ParameterName))
                    {
                        var subProperties = subParameterIssues[parameter.ParameterName];
                        foreach (var subProperty in subProperties)
                        {
                            var propName = subProperty.Substring(subProperty.IndexOf('.') + 1);
                            var property = parameter.ParameterType.GetProperty(propName);
                            var validationAttributes = property.GetCustomAttributes(typeof(ValidationAttribute), true);
    
                            var value = property.GetValue(argument);
    
                            modelState[subProperty].Errors.Clear();
                            foreach (var validationAttribute in validationAttributes)
                            {
                                var attr = (ValidationAttribute)validationAttribute;
                                if (!attr.IsValid(value))
                                {
                                    var parameterName = GetParameterName(property);
                                    // modelState.AddModelError(subProperty, attr.FormatErrorMessage(parameterName));
                                    modelState.AddModelError(parameterName, attr.FormatErrorMessage(parameterName));
                                }
                            }
                        }
                    }
    
    
                }
            }
    
        }
    
        private string GetParameterName(PropertyInfo property)
        {
            var dataMemberAttribute = property.GetCustomAttributes<DataMemberAttribute>().FirstOrDefault();
            if (dataMemberAttribute?.Name != null)
            {
                return dataMemberAttribute.Name;
            }
    
            var jsonProperty = property.GetCustomAttributes<JsonPropertyAttribute>().FirstOrDefault();
            if (jsonProperty?.PropertyName != null)
            {
                return jsonProperty.PropertyName;
            }
    
            return property.Name;
        }
    }
    

    【讨论】:

    • 是否有适用于 AspNetCore 的版本?谢谢
    【解决方案3】:

    可以访问参数类型,获取json名称,然后将属性名称替换为json名称。像这样的:

    var invalidParameters = (from m in actionContext.ModelState
                             where m.Value.Errors.Count > 0
                             select new InvalidParameter
                             {
                                  ParameterName = m.Key,
                                  ConstraintViolations = (from msg in m.Value.Errors select msg.ErrorMessage).ToArray()
                             }).AsEnumerable().ToArray();
    
    if (actionContext.ActionDescriptor.Parameters.Count == 1)
    {
        var nameMapper = new Dictionary<string, string>();
    
        foreach (var property in actionContext.ActionDescriptor.Parameters[0].ParameterType.GetProperties())
        {
            object[] attributes = property.GetCustomAttributes(typeof(JsonPropertyNameAttribute), false);
    
            if (attributes.Length != 1) continue;
    
            nameMapper.Add(property.Name, ((JsonPropertyNameAttribute) attributes[0]).Name);
        }
    
        var modifiedInvalidParameters = new List<InvalidParameter>();
    
    
        foreach (var invalidParameter in invalidParameters)
        {
            if(invalidParameter.ParameterName != null && nameMapper.TryGetValue(invalidParameter.ParameterName, out var mappedName))
            {
                var modifiedConstraintViolations = new List<string>();
    
                foreach (var constraintViolation in invalidParameter.ConstraintViolations ?? Enumerable.Empty<string>())
                {
                    modifiedConstraintViolations.Add(constraintViolation.Replace(invalidParameter.ParameterName, mappedName));
                }
    
                modifiedInvalidParameters.Add(new InvalidParameter
                {
                    ParameterName = mappedName,
                    ConstraintViolations = modifiedConstraintViolations.ToArray()
                });
            }
            else
            {
                modifiedInvalidParameters.Add(invalidParameter);
            }
        }
    
        invalidParameters = modifiedInvalidParameters.ToArray();
    }
    
    public struct InvalidParameter
    {
        [JsonPropertyName("parameter_name")]
        public string? ParameterName { get; set; }
    
        [JsonPropertyName("constraint_violations")]
        public string[]? ConstraintViolations { get; set; }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-11-08
      • 1970-01-01
      • 2018-03-21
      • 1970-01-01
      • 2016-10-18
      • 1970-01-01
      • 1970-01-01
      • 2020-06-22
      相关资源
      最近更新 更多