validation模型属性WCF Web APi

我有一组托管WCF Web Api的服务,我需要做的是validation应用程序模型中的属性。

例如,在MVC 3中,我在模型中修饰属性,如下所示:

[StringLength(30)] public string UserName { get; set; } 

然后在控制器中我这样继续validation模型是否满足validation参数:

  [HttpPost] ActionResult Create(Model myModel) { if(ModelState.IsValid(){ Post the model } else { Don't post the model } } 

有没有办法在WCF Web Api中做类似的事情?

首先,我应该说出很棒的问题+回答丹尼尔

但是,我已经采取了一些进一步的改进,并加入其中。

ValidationHander

我把它改进了一点。 它现在基于一个通用的HttpOperationHandler因此可以使用HttpRequestMessage 。 这样做的原因是我可以返回使用正确媒体类型(来自接受标头)格式化的错误消息。

 public class ValidationHandler : HttpOperationHandler { public ValidationHandler() : base("response") { } protected override HttpRequestMessage OnHandle(TResource model, HttpRequestMessage requestMessage) { var results = new List(); var context = new ValidationContext(model, null, null); Validator.TryValidateObject(model, context, results, true); if (results.Count == 0) { return requestMessage; } var errorMessages = results.Select(x => x.ErrorMessage).ToArray(); var mediaType = requestMessage.Headers.Accept.FirstOrDefault(); var response = new RestValidationFailure(errorMessages); if (mediaType != null) { response.Content = new ObjectContent(typeof (string[]), errorMessages, mediaType); } throw new HttpResponseException(response); } } 

扩展方法

ModelValidationFor方法中添加ValidationHandler时,您提供的2与desc参数几乎保持不变

我添加了一个额外的扩展方法。 这是为了确保所有“资源”类都经过validation。 这主要是我懒惰和健忘。 我永远忘记在某个地方添加一些类到列表。 (这就是我写通用windsor安装程序的原因!)

 public static void ValidateAllResourceTypes(this WebApiConfiguration config, string assemblyFilter = "MyCompany*.dll") { var path = Path.GetDirectoryName((new Uri(Assembly.GetExecutingAssembly().CodeBase)).AbsolutePath); var dc = new DirectoryCatalog(path, assemblyFilter); var assemblies = dc.LoadedFiles.Select(Assembly.LoadFrom).ToList(); assemblies.ForEach(assembly => { var resourceTypes = assembly.GetTypes() .Where(t => t.Namespace != null && t.Namespace.EndsWith("Resources")); foreach (var resourceType in resourceTypes) { var configType = typeof(Extensions); var mi = configType.GetMethod("ModelValidationFor"); var mi2 = mi.MakeGenericMethod(resourceType); mi2.Invoke(null, new object[] { config }); } }); } 

我使用了DirectoryCatalog类的System.ComponentModel.Composition.Hosting命名空间(以前称为MEF)。 在这种情况下,我刚刚使用以“Resources”结尾的命名空间来查找我的“Resource”类。 将其更改为使用自定义属性或您可能更喜欢的其他任何方式来识别哪些类是您的“资源”并不需要太多工作。

RestValidationFailure

这是我做的一个小助手类,允许validation失败响应的一致行为。

 public class RestValidationFailure : HttpResponseMessage { public RestValidationFailure(string[] messages) { StatusCode = HttpStatusCode.BadRequest; foreach (var errorMessage in messages) { Headers.Add("X-Validation-Error", errorMessage); } } } 

所以,现在我得到了一个很好的列表(在我首选的mediatype中)所有的validation错误。

请享用! 🙂

好的,我终于设法为我的模型工作获得validation。 我写了一个validation处理程序和几个扩展方法。 validation处理程序的第一件事:

  public class ValidationHandler : HttpOperationHandler { private readonly HttpOperationDescription _httpOperationDescription; public ValidationHandler(HttpOperationDescription httpOperationDescription) { _httpOperationDescription = httpOperationDescription; } protected override IEnumerable OnGetInputParameters() { return _httpOperationDescription.InputParameters .Where(prm => prm.ParameterType == typeof(T)); } protected override IEnumerable OnGetOutputParameters() { return _httpOperationDescription.InputParameters .Where(prm => prm.ParameterType == typeof(T)); } protected override object[] OnHandle(object[] input) { var model = input[0]; var validationResults = new List(); var context = new ValidationContext(model, null, null); Validator.TryValidateObject(model, context, validationResults,true); if (validationResults.Count == 0) { return input; } else { var response = new HttpResponseMessage() { Content = new StringContent("Model Error"), StatusCode = HttpStatusCode.BadRequest }; throw new HttpResponseException(response); } } } 

注意Handler如何接收T对象,这主要是因为我想validationAPI中的所有模型类型。 因此OnGetInputParameters指定处理程序需要接收T类型对象,并且OnGetOutputParameters指定处理程序需要返回具有相同T类型的对象,以防满足validation策略,否则,请参阅on handle方法如何抛出允许客户端知道存在validation问题的例外情况。

现在我需要注册处理程序,为此我写了几个扩展方法,跟随Pedro Felix博客的一个例子http://pfelix.wordpress.com/2011/09/24/wcf-web-apicustom-parameter-转换/ (这个博客给了我很多帮助,对整个处理程序操作有一些很好的解释)。 所以这些是扩展方法:

 public static WebApiConfiguration ModelValidationFor(this WebApiConfiguration conf) { conf.AddRequestHandlers((coll, ep, desc) => { if (desc.InputParameters.Any(p => p.ParameterType == typeof(T))) { coll.Add(new ValidationHandler(desc)); } }); return conf; } 

所以这个方法检查操作中是否有T类型参数,如果是,则将处理程序添加到该特定操作。

这个调用另一个扩展方法AddRequestHandler,并且该方法添加新处理程序而不删除以前注册的处理程序(如果存在)。

 public static WebApiConfiguration AddRequestHandlers( this WebApiConfiguration conf, Action,ServiceEndpoint,HttpOperationDescription> requestHandlerDelegate) { var old = conf.RequestHandlers; conf.RequestHandlers = old == null ? requestHandlerDelegate : (coll, ep, desc) => { old(coll, ep, desc); }; return conf; } 

最后一件事是注册处理程序:

  var config = new WebApiConfiguration(); config.ModelValidationFor(); //Instead of passing a T object pass the object you want to validate routes.SetDefaultHttpConfiguration(config); routes.MapServiceRoute("SomeRoute"); 

所以就是这样..希望它能帮助别人!

我目前正在开发一个HttpOperationHandler,它可以完全满足您的需求。 它现在还没有完成,但是这个伪代码可能会让你知道如何做到这一点。

 public class ValidationHandler : HttpOperationHandler { private readonly HttpOperationDescription _httpOperationDescription; private readonly Uri _baseAddress; public ValidationHandler(HttpOperationDescription httpOperationDescription, Uri baseAddress) { _httpOperationDescription = httpOperationDescription; _baseAddress = baseAddress; } protected override IEnumerable OnGetInputParameters() { return new[] { HttpParameter.RequestMessage }; } protected override IEnumerable OnGetOutputParameters() { var types = _httpOperationDescription.InputParameters.Select(x => x.ParameterType); return types.Select(type => new HttpParameter(type.Name, type)); } protected override object[] OnHandle(object[] input) { var request = (HttpRequestMessage)input[0]; var uriTemplate = _httpOperationDescription.GetUriTemplate(); var uriTemplateMatch = uriTemplate.Match(_baseAddress, request.RequestUri); var validationResults = new List(); //Bind the values from uriTemplateMatch.BoundVariables to a model //Do the validation with Validator.TryValidateObject and add the results to validationResults //Throw a exception with BadRequest http status code and add the validationResults to the message //Return an object array with instances of the types returned from the OnGetOutputParmeters with the bounded values } } 

OnGetInputParameters值告诉OnHandle方法的预期内容,OnGetOutputParameters告诉OnHandle方法的预期输出(稍后将注入到服务中的方法中)。

然后,您可以使用HttpConfiguration将处理程序添加到路由中,如下所示:

  var httpConfiguration = new HttpConfiguration { RequestHandlers = (collection, endpoint, operation) => collection.Add(new ValidationHandler(operation, endpoint.Address.Uri)) }; RouteTable.Routes.MapServiceRoute("MyResource", httpConfiguration); 

在MSDN上发布了一个这样的示例,即为此创建应该有效的行为。 您还可以使用Validator.ValidateObject手动调用validation器 (或将其作为扩展方法包装)并返回validation错误,这实质上就是该行为的作用。