【问题标题】:How to remove prefixes from ModelState keys?如何从 ModelState 键中删除前缀?
【发布时间】:2013-06-10 23:09:38
【问题描述】:

比如有一个Web Api action方法:

public HttpMessageResponse Post(UserDto userDto)
{
    if (!this.ModelState.IsValid)
    {
        return this.Request.CreateErrorResponse(
            HttpStatusCode.BadRequest, this.ModelState);
    }

    // ...
}

客户端发送以下请求:

HTTP POST: /api/user
{ "username": "me", "password": "Pa$sw0rd" }

并得到响应:

HTTP 201/Created:
{ "message": "Your request is invalid.",
  "modelState": { "userDto.Password": "Your password is too strong." } }

默认情况下,action 方法通过在模型错误前面加上 action 方法中使用的参数名称来公开实现细节。如果客户端应用程序在清理模型错误时硬编码此前缀名称,然后服务器端代码更改(例如,您将 Post(UserDto userDto) 签名替换为 Post(UserDto dto))并且所有客户端应用程序停止工作,该怎么办。

这就是为什么您需要确保在服务器端删除此前缀。问题是,如何正确地做到这一点,而不会使事情复杂化。例如,您可以创建自定义序列化程序并在序列化期间删除这些前缀。但为了这样做,您需要知道模型参数的名称,调用代码可能如下所示:

public HttpMessageResponse Post(UserDto userDto)
{
    if (!this.ModelState.IsValid)
    {
        return this.Request.CreateCustomErrorResponse(
            HttpStatusCode.BadRequest, this.ModelState, modelName: "userDto");
    }

    // ...
}

【问题讨论】:

    标签: .net asp.net-web-api


    【解决方案1】:

    第一部分:

    返回给客户端的错误消息也不应该包含这些前缀

    我同意将参数名称作为所有模型状态错误的前缀并不是很好的行为。幸运的是,具有这种行为的服务是可替换的。您只需要一个自定义的 IBodyModelValidator。下面是它的样子(使用装饰器模式让默认服务完成大部分工作):

    public class PrefixlessBodyModelValidator : IBodyModelValidator
    {
        private readonly IBodyModelValidator _innerValidator;
    
        public PrefixlessBodyModelValidator(IBodyModelValidator innerValidator)
        {
            if (innerValidator == null)
            {
                throw new ArgumentNullException("innerValidator");
            }
    
            _innerValidator = innerValidator;
        }
    
        public bool Validate(object model, Type type, ModelMetadataProvider metadataProvider, HttpActionContext actionContext, string keyPrefix)
        {
            // Remove the keyPrefix but otherwise let innerValidator do what it normally does.
            return _innerValidator.Validate(model, type, metadataProvider, actionContext, String.Empty);
        }
    }
    

    然后,用你的包装默认服务:

    config.Services.Replace(typeof(IBodyModelValidator), new PrefixlessBodyModelValidator(config.Services.GetBodyModelValidator()));
    

    第二部分:

    还将“modelState”替换为“errors”

    目前显示“modelState”的原因是您当前的代码:

    return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
    

    正在有效地执行以下操作:

    HttpError error = new HttpError(ModelState, false);
    return Request.CreateResponse(HttpStatusCode.BadRequest, error);
    

    由于 HttpError 正在被序列化,并且它有一个名为“ModelState”的属性,这就是您在响应中看到的内容。

    如果您想要不同的属性名称,可以使用自定义错误类:

    public class PrettyHttpError
    {
        public PrettyHttpError(ModelStateDictionary modelState)
        {
            Message = "Your request is invalid.";
            Errors = new Dictionary<string, IEnumerable<string>>();
    
            foreach (var item in modelState)
            {
                var itemErrors = new List<string>();
                foreach (var childItem in item.Value.Errors)
                {
                    itemErrors.Add(childItem.ErrorMessage);
                }
                Errors.Add(item.Key, itemErrors);
            }
        }
    
        public string Message { get; set; }
    
        public IDictionary<string, IEnumerable<string>> Errors { get; set; }
    }
    

    然后使用此错误类型而不是 HttpError 创建您的响应:

    PrettyHttpError error = new PrettyHttpError(ModelState);
    return Request.CreateResponse(HttpStatusCode.BadRequest, error);
    

    PrettyHttpError 和 PrefixlessBodyModelValidator 的组合给出了您请求的输出。

    【讨论】:

    • 只是对第二个的注释 -- HttpError 是一本字典,所以你可以直接使用 error["Errors"] = error["ModelState"]; error.Remove("ModelState");
    • 不幸的是,您的PrefixlessBodyModelValidator 不会帮助解决序列化错误。例如。当客户端为枚举或 DateTime 提供无效值时。在那种情况下,前缀仍然存在,这让我非常难过。
    • 这个配置变量在哪里? config.Services.Replace(typeof(IBodyModelValidator),..
    • 我最终将prefix 参数传递给 PrettyHttpError 构造函数。我用Substring 删除它以修复序列化错误。不理想,但它有效......
    • 这似乎不适用于嵌套视图模型结构。
    猜你喜欢
    • 1970-01-01
    • 2012-01-26
    • 2015-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-15
    • 1970-01-01
    • 2015-11-15
    相关资源
    最近更新 更多