[水煮ASP.NETWebAPI2方法论](1-6)ModelValidation

问题

创新互联公司是一家集网站建设,玉树企业网站建设,玉树品牌网站建设,网站定制,玉树网站建设报价,网络营销,网络优化,玉树网站推广为一体的创新建站企业,帮助传统企业提升企业形象加强企业竞争力。可充分满足这一群体相比中小企业更为丰富、高端、多元的互联网需求。同时我们时刻保持专业、时尚、前沿,时刻以成就客户成长自我,坚持不断学习、思考、沉淀、净化自己,让我们为更多的企业打造出实用型网站。

  想要 ASP.NET Web API执行模型验证,同时可以和 ASP.NET MVC共享一些验证逻辑。

解决方案

  ASP.NET Web API与 ASP.NET MVC支持一样的验证机制,都是通过 System.ComponentModel.DataAnnoataions的属性验证。使用框架提供的相关验证属性,已足够来用来验证模型。

想要更细粒度的验证,我们可以选择在我们的模型中实现 IValudateObject(来自于 System.ComponentModel.DataAnnotations)。如果所有的属性都验证通过,ASP.NET Web API将会调用接口的 Validate方法,在这里我们可以进行更进一步的进行实体验证。这是和 MVC里面的行为一样,并且,我们甚至可以在 Web API和 MVC中使用同一个 DTO。

  还有另一种方法,就是可以使用一个叫做 FluentValidation(NuGet中可以下载FluentValidation)的第三方程序库,他可以构建更强大的验证场景。在这样的情况下,我们仍然需在我们的模型中实现 IValidateObject接口,同时需要依赖于FluentValidation验证器,而不是内嵌的验证逻辑。

小提示 ASP.NET Web API的验证行为在跨宿主机上是相同的。

工作原理

  为了从HTTP请求 Body中读取的模型并执行验证,ASP.NET Web API依赖于一个 IBodyModelValidator的服务。接口的大致描述如清单1-17所示,然而,他是一个可替代的服务,正常情况下,默认实现(DefaultBodyModelValidator)足够我们使用,在HttpConfiguration被设置为自启动。

清单 1-17. IBodyModelValidator 接口

public interface IBodyModelValidator
{
    bool Validate(object model, Type type, ModelMetadataProvider metadataProvider,
    HttpActionContext actionContext, string keyPrefix);
}

 

 

  有一个叫做FormatrtParameterBinding的服务,在 HTTP请求 Body绑定到 Action参数的处理请求时, DefaultBodyModelValidator的Validate方法会被调用。对于验证程序,他会递归验证整个对象图谱,验证每一个属性以及嵌套属性。Web API通过使用DataAnnotationModelValidatorProviderr来支持声明。如果我们的模型使用WCF方式的 DataMemberAttribute声明,那么,我们需要使用框架的 DataMemberValidatorProvider。

  最后,我们的模型可以实现IValidatableObject接口,这个接口只暴露了一个简单的方法如清单1-18所示。如果实现了接口,那就需要我们自己提供额外的验证逻辑。只要所有的属性验证通过,ASP.NET Wwb API就会调用IValidateableObject接口的 Validate方法,

 

清单1-18. IValidateableObject 接口的定义

public interface IValidateableObject
{
    IEnumerable Validate(ValidationContext validationContext);
}

 

 

  验证结果是通过ASP.NET Web API的 ModelStateDictionary形式表示,在这里 ModelState也是可以用的。这个和ASP.NET MVC中的概念是完全一样的,但是使用的对象是不同的,因为Web API使用自己版本的 System.Web.Http.Modelbinding。ModelStateDictionary暴露了IsValid属性,这个属性可以用来检查 Action内 Model验证的状态。

  声明的验证机制也很好的整合到了ASP.NET Web API Help Page,可以提供对API语义上的描述。我们将会在7-11的时候详细讨论他。

  小提示在API中最好的做法是使用不同的模型作为 Request和Response实体。例如,实体 ID一般仅仅是 Response模型需要的,如果 Request中需要的话,是可以从 URI中拿到的。

 

代码

  清单 1-19展示了一个模型有多种验证的情况:

RequiredAttribute,MaxLengthAttribute和

RangeAttribute。接下来,我们就可以利用 ModelState来验证Controller中的验证状态,同时响应适当的提示信息给调用端。

清单 1-19. 简单的 Web API 模型验证

public class Album
{
    public int Id { get; set; }
    [Required(ErrorMessage = "Artist is required")]
    [MaxLength(30)]
    public string Artist { get; set; }
    [Required(ErrorMessage = "Title is required")]
    [MaxLength(40)]
    public string Title { get; set; }
    [Range(0, 10, ErrorMessage = "Rating in the range of 0-10 is required.")]
    public int Rating { get; set; }
}
public class AlbumController : ApiController
{
    public HttpResponseMessage Post(Album album)
    {
        if (!ModelState.IsValid)
        {
            throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest,
            ModelState));
        }
        //omitted for brevity
    }
}

 

 

  负责处理ModelState代码的一般验证可以很容易从 Controller提取到成公共的部分,使其可以被很好的重用,不过这一部分我们将在 5-4的时候再详细介绍。

  现在,我们考虑一下这个场景,如果我们要在模型上增加增加两个额外的属性 Rating和Starred,同时扩展模型验证,验证的要求是这两个属性至少有一个是必填的。虽然,在两个属性之间纠缠的验证很难使用声明的方式来表示,但是,不要忘记 IValidateableObject可以帮我们。我们可以使用接口中的 Validata的方法去检查整个模型的状态,同时返回相应的 ValidationResult。我们要做的修改如清单 1-20所示的代码。

 

清单 1-20. 修改 ASP.NET Web API 依赖于 IValidateableObject 的验证

public class Album : IValidatableObject
{
    public int Id { get; set; }
 
    [Required(ErrorMessage = "Artist is required")]
    [MaxLength(30)]
    public string Artist { get; set; }
 
    [Required(ErrorMessage = "Title is required")]
    [MaxLength(40)]
    public string Title { get; set; }
 
    public int? Rating { get; set; }
    public bool? Starred { get; set; }
 
    public IEnumerable Validate(ValidationContext validationContext)
    {
        if (!(Rating.HasValue && Rating > 0 && Rating < 10) || (Starred.HasValue && Starred.Value))
        {
            yield return new ValidationResult("You must set either the Rating in the 0-9 range orStarred flag.");
        }
    }
}

 

 

  正如我们看到的,我们可以很容的在Validate方法中使用自己的逻辑来合并声明方式的验证。这是一个特例,如果我们的请求中没有包含这两个属性中的任何一个,我们验证期望结果就是失败,同时返回结果给调用端,如清单 1-21所示。

清单 1-21 非法请求,同时通过 IValidateableObject 执行 Validation 返回相应结果给调用端。

POST /api/album HTTP 1.1

Content-Type: application/json

{"artist":"Rancid", "title":"And Out Come The Wolves"}

Status Code: 400 Bad Request

Content-Length: 130

Content-Type: application/json; charset=utf-8

Date: Tue, 13 May 2014 19:06:31 GMT

{

"Message": "The request is invalid.",

"ModelState": {

"album": ["You must set either the Rating in the 0-9 range or Starred flag."]

}

}

 

  我们可以更进一步的验证,接下来将介绍 FluentValidation来为Web API做模型验证。手下,我们要从NuGet上安装 FluentValidation。Install-package FluentValidation

就像开始那样,不把验证逻辑嵌套到实体中,我们应用 FluentValidation验证器,调用它里面的 Validate的方法。这样,所有的验证可以放到实体外部。因此,我们不仅可以从模型中移除之前的验证逻辑,还可以清掉 DataAnnotation属性。修改之前模型验证的例子,使用 FluentValidation,如清单 1-22所示。

 

清单 1-22 FluentValidation

 

  在功能方面,上面的例子等同于清单1-20所示的代码。FluentValidation验证规则是可以配置。上面代码的前一部分展示了配置相应 FluentValidation的验证规则和相应响应的结果,而,后一部分代码是 IValidateableObject接口的实现,确保了指定的验证可以被执行。


当前标题:[水煮ASP.NETWebAPI2方法论](1-6)ModelValidation
文章分享:http://scjbc.cn/article/jjchoh.html

其他资讯