例如,有一个Web Api操作方法:
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 dto)替换了Post(UserDto userDto))签名时,会重新编码此前缀名称,并且所有客户端应用程序都停止工作。
这就是为什么你需要确保这个前缀在服务器端被删除。问题是如何正确地做,没有使事情复杂化。例如,您可以创建一个自定义序列化程序,并在序列化期间删除这些前缀。但是为了做到这一点,你需要知道模型参数的名称,调用代码可能看起来像这样:
public HttpMessageResponse Post(UserDto userDto) { if (!this.ModelState.IsValid) { return this.Request.CreateCustomErrorResponse( HttpStatusCode.BadRequest,this.ModelState,modelName: "userDto"); } // ... }
解决方法
第一部分:
Also error messages returned to a client shouldn’t contain those prefixes
我同意将参数名称作为所有模型状态错误的前缀都不是很大的行为。幸运的是,具有此行为的服务是可替换的。你只需要一个自定义IBodyModelValidator。这是它的样子(使用Decorator模式让默认服务做大部分工作):
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()));对于第二部分:
elso replace “modelState” with “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的组合给出了您请求的输出。