webapi - 模型驗證

来源:http://www.cnblogs.com/wangrudong003/archive/2016/12/26/6223942.html
-Advertisement-
Play Games

本次要和大家分享的是webapi的模型驗證,講解的內容可能不單單是做驗證,但都是圍繞模型來說明的;首先來吐槽下,今天下午老闆為自己買了套新辦公傢具,看起來挺好說明老闆有錢,不好的是我們乾技術的又成了搬運工(誰叫技術部男的多呢哈哈),話說讓我們搬點兒什麼小座椅板凳就夠了吧,為什麼4大箱的傢具都讓我們動 ...


本次要和大家分享的是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方式)

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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 驗證碼類 使用方法 HTML 視圖JS ...
  • 1 自定義控制項與用戶控制項區別 WinForm中, 用戶控制項(User Control):繼承自 UserControl,主要用於開發 Container 控制項,Container控制項可以添加其他Controls控制項 自定義控制項(Custom Control):繼承自 Control,主要用於開發wi ...
  • 年關將近,整個人已經沒有了工作和寫作的激情,估計這個時候很多人跟我差不多,該相親的相親,該聚會喝酒的聚會喝酒,總之就是沒有了幹活的心思(我有很多想法,但就是叫不動我的手腳,所以我只能看著別人在做我想做的事,吃我想吃的東西。)。本人由上個月的每周四五篇,到現在的文章縮短到每周一篇,說個實話,現在的一篇 ...
  • .NET Core和 .NET 4.6中 的C# 6/7 中的編譯器Roslyn 一個重要的特性就是"Compiler as a Service",簡單的講,就是就是將編譯器開放為一種可在代碼中調用的服務, 通常在工作流引擎 或是規則引擎中都需要一項功能是計算表達式,這樣的任務很容易就用Roslyn... ...
  • 當某個請求能夠被成功路由的前提是它滿足某個Route對象設置的路由規則,具體來說,當前請求的URL不僅需要滿足路由模板體現的路徑模式,請求還需要滿足Route對象的所有約束。路由系統採用IRouteConstraint介面來表示路由約束,所以我們在接下來的內容中將路由約束統稱為RouteConstr... ...
  • 上次實現了依賴註入,但是web項目必須要引用業務邏輯層和數據存儲層的實現,項目解耦並不完全;另一方面,要同時註入業務邏輯層和數據訪問層,註入的服務直接寫在Startup中顯得非常臃腫。理想的方式是,web項目近引用介面而不引用實現,在配置文件中進行配置實現程式集合類,註入業務邏輯層而不必註入數據訪問 ...
  • 為了實現這個功能,可算是折騰不少時間,網上搜素出來的結果基本都是如何屏蔽警告對話框。後來請教一個技術大牛(程式員之窗的主要作者Starts_2000),他用C++實現了,他嘗試了下C#也沒有解決,就忙其他的去,大牛時間一般都比較寶貴,後來我就將就用那個C++動態庫,那個時候我測試環境是 win7 6 ...
  • 模型瀏覽器: 在之前的章節中,我們創建了第一個關於學校的實體數據模型。但是EDM設計器並沒有將他所創建的所有對象完全顯示出來。它只將資料庫中的被選擇的表與視圖顯示出來了。 模型瀏覽器可以將EDM所創建的所有對象和函數的信息都顯示出來。右鍵EDM設計器併在菜單中選擇模型瀏覽器即可打開。 模型瀏覽器包含 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...