問題
創(chuàng)新互聯(lián)公司是一家集網(wǎng)站建設(shè),玉樹企業(yè)網(wǎng)站建設(shè),玉樹品牌網(wǎng)站建設(shè),網(wǎng)站定制,玉樹網(wǎng)站建設(shè)報價,網(wǎng)絡(luò)營銷,網(wǎng)絡(luò)優(yōu)化,玉樹網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強企業(yè)競爭力。可充分滿足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時我們時刻保持專業(yè)、時尚、前沿,時刻以成就客戶成長自我,堅持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實用型網(wǎng)站。
想要 ASP.NET Web API執(zhí)行模型驗證,同時可以和 ASP.NET MVC共享一些驗證邏輯。
解決方案
ASP.NET Web API與 ASP.NET MVC支持一樣的驗證機制,都是通過 System.ComponentModel.DataAnnoataions的屬性驗證。使用框架提供的相關(guān)驗證屬性,已足夠來用來驗證模型。
想要更細(xì)粒度的驗證,我們可以選擇在我們的模型中實現(xiàn) IValudateObject(來自于 System.ComponentModel.DataAnnotations)。如果所有的屬性都驗證通過,ASP.NET Web API將會調(diào)用接口的 Validate方法,在這里我們可以進行更進一步的進行實體驗證。這是和 MVC里面的行為一樣,并且,我們甚至可以在 Web API和 MVC中使用同一個 DTO。
還有另一種方法,就是可以使用一個叫做 FluentValidation(NuGet中可以下載FluentValidation)的第三方程序庫,他可以構(gòu)建更強大的驗證場景。在這樣的情況下,我們?nèi)匀恍柙谖覀兊哪P椭袑崿F(xiàn) IValidateObject接口,同時需要依賴于FluentValidation驗證器,而不是內(nèi)嵌的驗證邏輯。
小提示 ASP.NET Web API的驗證行為在跨宿主機上是相同的。
工作原理
為了從HTTP請求 Body中讀取的模型并執(zhí)行驗證,ASP.NET Web API依賴于一個 IBodyModelValidator的服務(wù)。接口的大致描述如清單1-17所示,然而,他是一個可替代的服務(wù),正常情況下,默認(rèn)實現(xiàn)(DefaultBodyModelValidator)足夠我們使用,在HttpConfiguration被設(shè)置為自啟動。
清單 1-17. IBodyModelValidator 接口
public interface IBodyModelValidator { bool Validate(object model, Type type, ModelMetadataProvider metadataProvider, HttpActionContext actionContext, string keyPrefix); }
有一個叫做FormatrtParameterBinding的服務(wù),在 HTTP請求 Body綁定到 Action參數(shù)的處理請求時, DefaultBodyModelValidator的Validate方法會被調(diào)用。對于驗證程序,他會遞歸驗證整個對象圖譜,驗證每一個屬性以及嵌套屬性。Web API通過使用DataAnnotationModelValidatorProviderr來支持聲明。如果我們的模型使用WCF方式的 DataMemberAttribute聲明,那么,我們需要使用框架的 DataMemberValidatorProvider。
最后,我們的模型可以實現(xiàn)IValidatableObject接口,這個接口只暴露了一個簡單的方法如清單1-18所示。如果實現(xiàn)了接口,那就需要我們自己提供額外的驗證邏輯。只要所有的屬性驗證通過,ASP.NET Wwb API就會調(diào)用IValidateableObject接口的 Validate方法,
清單1-18. IValidateableObject 接口的定義
public interface IValidateableObject { IEnumerableValidate(ValidationContext validationContext); }
驗證結(jié)果是通過ASP.NET Web API的 ModelStateDictionary形式表示,在這里 ModelState也是可以用的。這個和ASP.NET MVC中的概念是完全一樣的,但是使用的對象是不同的,因為Web API使用自己版本的 System.Web.Http.Modelbinding。ModelStateDictionary暴露了IsValid屬性,這個屬性可以用來檢查 Action內(nèi) Model驗證的狀態(tài)。
聲明的驗證機制也很好的整合到了ASP.NET Web API Help Page,可以提供對API語義上的描述。我們將會在7-11的時候詳細(xì)討論他。
小提示在API中最好的做法是使用不同的模型作為 Request和Response實體。例如,實體 ID一般僅僅是 Response模型需要的,如果 Request中需要的話,是可以從 URI中拿到的。
代碼
清單 1-19展示了一個模型有多種驗證的情況:
RequiredAttribute,MaxLengthAttribute和
RangeAttribute。接下來,我們就可以利用 ModelState來驗證Controller中的驗證狀態(tài),同時響應(yīng)適當(dāng)?shù)奶崾拘畔⒔o調(diào)用端。
清單 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 } }
負(fù)責(zé)處理ModelState代碼的一般驗證可以很容易從 Controller提取到成公共的部分,使其可以被很好的重用,不過這一部分我們將在 5-4的時候再詳細(xì)介紹。
現(xiàn)在,我們考慮一下這個場景,如果我們要在模型上增加增加兩個額外的屬性 Rating和Starred,同時擴展模型驗證,驗證的要求是這兩個屬性至少有一個是必填的。雖然,在兩個屬性之間糾纏的驗證很難使用聲明的方式來表示,但是,不要忘記 IValidateableObject可以幫我們。我們可以使用接口中的 Validata的方法去檢查整個模型的狀態(tài),同時返回相應(yīng)的 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 IEnumerableValidate(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方法中使用自己的邏輯來合并聲明方式的驗證。這是一個特例,如果我們的請求中沒有包含這兩個屬性中的任何一個,我們驗證期望結(jié)果就是失敗,同時返回結(jié)果給調(diào)用端,如清單 1-21所示。
清單 1-21 非法請求,同時通過 IValidateableObject 執(zhí)行 Validation 返回相應(yīng)結(jié)果給調(diào)用端。
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
就像開始那樣,不把驗證邏輯嵌套到實體中,我們應(yīng)用 FluentValidation驗證器,調(diào)用它里面的 Validate的方法。這樣,所有的驗證可以放到實體外部。因此,我們不僅可以從模型中移除之前的驗證邏輯,還可以清掉 DataAnnotation屬性。修改之前模型驗證的例子,使用 FluentValidation,如清單 1-22所示。
清單 1-22 FluentValidation
在功能方面,上面的例子等同于清單1-20所示的代碼。FluentValidation驗證規(guī)則是可以配置。上面代碼的前一部分展示了配置相應(yīng) FluentValidation的驗證規(guī)則和相應(yīng)響應(yīng)的結(jié)果,而,后一部分代碼是 IValidateableObject接口的實現(xiàn),確保了指定的驗證可以被執(zhí)行。