文章出處

本次要和大家分享的是webapi的模型驗證,講解的內容可能不單單是做驗證,但都是圍繞模型來說明的;首先來吐槽下,今天下午老板為自己買了套新辦公家具,看起來挺好說明老板有錢,不好的是我們干技術的又成了搬運工(誰叫技術部男的多呢哈哈),話說讓我們搬點兒什么小座椅板凳就夠了吧,為什么4大箱的家具都讓我們動手,每箱東西拆分出來每件幾乎需要至少4個人才能挪到的東西,而且不少呢,這是讓我們搬完后不用上班的節奏吧;我很想問的是買這么貴的東西,難道不給包送和組裝?行政部門就不能請點搬運工,非要節約這點錢(技術可不是兼職的搬運工啊)?東西少那是鍛煉身體沒關系,東西多了那就是折磨人,這里說的想必也是大眾技術朋友們的心聲吧呵呵;好了不多說了,本章內容希望大家喜歡,也希望各位多多掃碼支持和點贊謝謝:

 

» 增加模型驗證

» 自定義過濾器,輸出模型驗證信息

» FromUri和FromBody用途

 

下面一步一個腳印的來分享:

» 增加模型驗證

首先,我們測試用例使用上一篇的 MoStudent 學生類,模型驗證需要在對應提交類中的需要驗證格式的屬性增加一些注解標記,常用的標記有:

. Required:必須滿足不為空

. RegularExpression:正則表達式驗證

. StringLength:指定字符允許的范圍

. DataType:數據類型,常用于密碼類型,如: DataType.Password 

. Range:指定數字允許的范圍

這里我們實例的 MoStudent  類用到的注解,如下代碼:

 public class MoStudent
    {

        public int Id { get; set; }

        [Required(ErrorMessage = "名稱必須填寫")]
        [RegularExpression(@"\w{2,15}", ErrorMessage = "名稱應為2-15長度的字母組合")]
        public string Name { get; set; }

        public bool Sex { get; set; }
        public DateTime Birthday { get; set; }
    }

然后,我們把保存學生的方法內容定義成這樣,iis訪問地址為 http://localhost:1001/s/add ,代碼對應:

        [Route("add")]
        [HttpPost]
        public HttpResponseMessage AddStudent(MoStudent moStudent)
        {

            if (ModelState.IsValid)
            {

                return Request.CreateResponse(HttpStatusCode.OK, moStudent);
            }
            return Request.CreateResponse(HttpStatusCode.BadRequest, ModelState);
         }

這里的關鍵代碼是 ModelState.IsValid ,通過這個來判斷用戶錄入的信息是否滿足我們定義在實體類中的注解規則,不滿足我這里直接輸出訪問狀態為 HttpStatusCode.BadRequest ,再來咋們用ajax提交添加學生表單到這個接口,但不錄入任何學生信息,會得到console輸出的返回的信息:

這里能看到404,明顯從上面的代碼來看走到了 Request.CreateResponse(HttpStatusCode.BadRequest, ModelState); 這段代碼,也就是說 ModelState.IsValid 的結果是false,驗證沒有通過,因為我們在錄入學生信息頁面的文本框內,沒有輸入任何信息就直接提交的表單,所以這里驗證沒有通過;但是作為一個webapi接口而言,這種如果提交格式不正確,就直接返回400錯誤,這樣明顯不友好,那么我們需要做一下變動,首先需要定義一個公共接口返回類 MoResult ,該類主要用來作為webapi接口的統一返回數據格式標準(這也是通常接口需要的一種響應數據規則格式),我們定義如下格式代碼:

 1 /// <summary>
 2     /// 結果輸出類
 3     /// </summary>
 4     public class MoResult
 5     {
 6 
 7         /// <summary>
 8         /// 0:失敗,1:成功 其他
 9         /// </summary>
10         public EnResultStatus Status { get; set; }
11 
12         /// <summary>
13         /// 錯誤信息
14         /// </summary>
15         public object Msg { get; set; }
16 
17         /// <summary>
18         /// 對象信息
19         /// </summary>
20         public object Data { get; set; }
21     }
22 
23     /// <summary>
24     /// 枚舉類型
25     /// </summary>
26     public enum EnResultStatus
27     {
28         失敗 = 0,
29         成功 = 1
30     }

然后,調整下apiController對應保存學生信息的Action方法代碼如:

 1 [Route("add")]
 2         [HttpPost]
 3         public HttpResponseMessage AddStudent(MoStudent moStudent)
 4         {
 5 
 6             var moResult = new MoResult() { Msg = EnResultStatus.失敗 };
 7             try
 8             {
 9                 if (ModelState.IsValid)
10                 {
11                     var isfalse = db.Save(moStudent);
12                     moResult.Status = isfalse ? EnResultStatus.成功 : EnResultStatus.失敗;
13                     moResult.Data = moStudent;
14                 }
15                 else
16                 {
17                     //如果驗證是吧,只取第一個錯誤信息返回
18                     var item = ModelState.Values.Take(1).SingleOrDefault();
19                     moResult.Msg = item.Errors.Where(b => !string.IsNullOrWhiteSpace(b.ErrorMessage)).Take(1).SingleOrDefault().ErrorMessage;
20                 }
21             }
22             catch (Exception ex)
23             {
24                 moResult.Msg = ex.Message;
25             }
26             return Request.CreateResponse(HttpStatusCode.OK, moResult);
27         }

需要注意的幾點是:

1. 這里我們通過 MoResult 的Data屬性來返回我們添加成功Student輸入信息;

2. 如果 ModelState.IsValid 驗證失敗,取第一個驗證錯誤信息返回給調用方,其目的讓調用方能夠提醒用戶哪些信息錄入的不全或者格式不對

好了咋們來一起看下這個時候ajax提交的空表單數據后,獲取到webapi接口返回的數據信息如:

很明顯這個錯誤信息就是咋們在實體 MoStudent 中對Name屬性做的一個驗證錯誤信息,再這里一個前端調用webapi接口驗證的流程基本就完事了,當然一般這種空驗證也是需要前端自己驗證的,好了咋們也來看下,如果錄入完成學生信息的表單提交過后會有什么樣子的數據返回呢:

 

» 自定義過濾器,輸出模型驗證信息

首先,我們需要明確的是,任意webapi接口基本都是需要有請求參數的格式驗證的,如果按照上一節直接在實體類中增加注解驗證,那么我們就沒必要在每一個api接口對應的action中再寫代碼 ModelState.IsValid 去判斷是否驗證成功,或者是獲取驗證后的錯誤信息了,因為webapi提供一個 ActionFilterAttribute Action過濾器,這個作用主要是請求到api的Action方法時候用來執行一些東西,本實例通過繼承她,來自定義一個驗證過濾器 ValidateModelAttribute ,主要用來統一獲取驗證錯誤信息,并且輸出響應給調用方,下面先來看下代碼:

 1 /// <summary>
 2     /// 模型格式驗證
 3     /// </summary>
 4     public class ValidateModelAttribute : ActionFilterAttribute
 5     {
 6 
 7 
 8         public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
 9         {
10 
11             if (!actionContext.ModelState.IsValid)
12             {
13 
14                 var moResult = new MoResult() { Msg = "請求數據異常", Status = EnResultStatus.失敗 };
15                 //自定義錯誤信息
16                 var item = actionContext.ModelState.Values.Take(1).SingleOrDefault();
17                 moResult.Msg = item.Errors.Where(b => !string.IsNullOrWhiteSpace(b.ErrorMessage)).Take(1).SingleOrDefault().ErrorMessage;
18                 actionContext.Response = actionContext.Request.CreateResponse(System.Net.HttpStatusCode.OK, moResult);
19             }
20         }
21     }

同第一小節的代碼差不多,主要用來判斷是否通過實體類的注解驗證,如果不通過獲取第一個錯誤信息返回給調用方;這里要注意的是我們重寫了 ActionFilterAttribute 過濾器中的 OnActionExecuting 方法;好了下面我們還需要把自定義的 ValidateModelAttribute  在 App_Start/WebApiConfig.cs 加入一下 config.Filters.Add(new ValidateModelAttribute()); 這個代碼,意思吧自定義的過濾器假如到webapi中;再來咋們直接就可以在我們添加學生的Action上方使用 [ValidateModel] 標記來驗證調用方傳遞給webapi的參數了,我們把學生添加Action AddStudent 代碼改為如下所示:

 1  [Route("add")]
 2         [HttpPost]
 3         [ValidateModel]  //這里是自定義過濾
 4         public HttpResponseMessage AddStudent(MoStudent moStudent)
 5         {
 6 
 7             var moResult = new MoResult();
 8             try
 9             {
10                 var isfalse = db.Save(moStudent);
11                 moResult.Status = isfalse ? EnResultStatus.成功 : EnResultStatus.失敗;
12                 moResult.Data = moStudent;
13             }
14             catch (Exception ex)
15             {
16                 moResult.Msg = ex.Message;
17             }
18             return Request.CreateResponse(HttpStatusCode.OK, moResult);
19         }

然后,再用ajax提交一下空表單數據,會得到如圖所示的信息:

能看出自定義驗證過濾器效果和我們之前直接寫在代碼中驗證的提示信息一樣,總體來說自定義的驗證使得api中的Action代碼精簡了很多,很提倡使用;

 

» FromUri和FromBody用途

首先,我們來看一段代碼:

 1 [Route("all01_2/{id:int?}")]
 2         [AcceptVerbs("POST", "GET")]
 3         public HttpResponseMessage GetAllStudents01_2(int id = 0)
 4         {
 5             var students = db.GetAll();
 6 
 7             if (id > 0)
 8             {
 9                 students = students.Where(b => b.Id == id).ToList();
10             }
11 
12             return Request.CreateResponse(HttpStatusCode.OK, students);
13         }

這里的action傳遞進來了一個名稱id的參數,然后下面做了 id > 0 的判斷來獲取不同的數據,這種場景我們經常會遇到,并且查詢條件的參數不止一個,這樣來看我們會不斷的在action里面增加傳遞的參數,格式可能會如此: public HttpResponseMessage GetAllStudents01_2(int id = 0,參數2,參數3,參數...) ,這樣如果需要用到的參數有10個以上,通常看起來不是很方便,這個時候FromUri和FromBody的用途就來了;先來看FromUri,首先需要我們把上面的那個Action方法的入參格式改成這樣:

 1 [Route(@"all01_3/{id:int=0}/{name?}")]
 2         [AcceptVerbs("POST", "GET")]
 3         public HttpResponseMessage GetAllStudents01_3([FromUri]MoSearch moSearch)
 4         {
 5             var students = db.GetAll().AsEnumerable();
 6 
 7             if (moSearch.Id > 0)
 8             {
 9                 students = students.Where(b => b.Id == moSearch.Id);
10             }
11 
12             if (!string.IsNullOrWhiteSpace(moSearch.Name))
13             {
14                 students = students.Where(b => b.Name.Contains(moSearch.Name));
15             }
16 
17             return Request.CreateResponse(HttpStatusCode.OK, students);
18         }

通過實體類 MoSearch 來獲取調用端傳遞的參數,實體類MoSearch定義的屬性字段如:

public class MoSearch
    {

        public int Id { get; set; }

        public string Name { get; set; }
    }

然后我們改造下查詢學生列表的ajax請求方法如:

 1 $("#btnSearch").on("click", function () {
 2 
 3             var tabObj = $("#tab tbody");
 4             tabObj.html('tr><td colspan="4">加載中...</td></tr>');
 5 
 6 
 7             var url = "http://localhost:1001/s/all01_3/1";
 8             var txtName = $("input[name='txtName']").val();
 9             if (txtName.length > 0) {
10                 url = url + "/" + txtName;
11             }
12 
13             $.get(url, function (data) {
14 
15                 console.log(data);
16 
17                 var tabHtml = [];
18                 $.each(data, function (i, item) {
19 
20                     tabHtml.push('<tr>');
21                     tabHtml.push("<td>" + item.Id + "</td>");
22                     tabHtml.push("<td>" + item.Name + "</td>");
23                     tabHtml.push("<td>" + (item.Sex ? "" : "") + "</td>");
24                     tabHtml.push("<td>" + item.Birthday + "</td>");
25                     tabHtml.push('</tr>');
26                 });
27                 if (tabHtml.length <= 0) { tabHtml.push('tr><td colspan="4">暫無數據</td></tr>'); }
28 
29                 tabObj.html(tabHtml.join(''));
30             });
31         });

這里Id=1是固定了,直接查詢id=1的學生信息,并且有一個可傳遞的參數學生名稱txtName的值,如果用戶沒有輸入學生名稱,那么就不會加入到這個get請求中,下面來看下首先不輸入學生姓名查詢條件的效果:

這里能看出來只有學生編號為1的學生被查出來了,下面我們再錄入姓名查詢條件,能看到結果如:

能看到此時查不到名稱為“小2”的學生信息,咋們再改成“小1”試試,

這個時候小1的學生能被查出來,這說明咋們的api的all01_3能夠通過 FromUri]MoSearch moSearch 獲取到我們輸入的參數;下面我們使用FromBody來演示她的效果,首先需要修改js請求webapi格式如:

然后把webapi的all01_3修改為如下代碼:

 1 [Route(@"all01_3/{name?}")]
 2         [AcceptVerbs("POST", "GET")]
 3         public HttpResponseMessage GetAllStudents01_3([FromBody]MoSearch moSearch)
 4         {
 5             var students = db.GetAll().AsEnumerable();
 6 
 7             if (moSearch.Id > 0)
 8             {
 9                 students = students.Where(b => b.Id == moSearch.Id);
10             }
11 
12             if (!string.IsNullOrWhiteSpace(moSearch.Name))
13             {
14                 students = students.Where(b => b.Name.Contains(moSearch.Name));
15             }
16 
17             return Request.CreateResponse(HttpStatusCode.OK, students);
18         }

主要區別吧FromUri換成了FromBody,最后咋們來一起看下沒有錄入學生名稱查詢條件的測試效果圖:

錄入學生查詢條件后的效果圖:

由上看出能正常查詢出來數據,這說明 [FromBody]MoSearch moSearch 獲取到了調用客戶端的請求參數;好了FromBody和FromUri測試的例子就這些,這兩者的區別這里總結下:

FromBody:主要用來獲取post方式請求的參數

FromUri:主要獲取url地址的請求參數(可以看成get方式)

這兩者的效果對比我就不做了,有興趣的朋友可以自己驗證下;本次分享的內容就到這里吧,主要講解了關于數據模型的一些知識,希望各位喜歡,多多點贊。


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜

    大師兄 發表在 痞客邦 留言(0) 人氣()