網站首頁 編程語言 正文
在開發ASP.NET Core MVC應用程序時,需要對控制器中的模型校驗數據有效性,元數據注釋(Data Annotations)是一個完美的解決方案。
元數據注釋最典型例子是確保API的調用者提供了某個屬性的值,在傳統的ASP.NET MVC中使用的是RequiredAttribute
特性類。該屬性仍然可以在ASP.NET Core MVC中使用,但也提供了一個新的特性類BindRequiredAttribute
。
今天讓我們來看看它們之間的細微差別。
RequiredAttribute的典型用法
想象一下,您的模型是下面的BookOrder
類。為了強制Author
和Title
屬性的值始終出現在傳入的請求中,并且值不能為空,我們使用RequiredAttribute
修飾它們。
public class BookOrder { [Required] public string Author { get; set; } [Required] public string Title { get; set; } }
這在傳統的ASP.NET MVC中是很常見方法。有了這個,您可以驗證ModelState
是否存在錯誤,因為對這些屬性的驗證失敗會反映在這里。示例:
[Route("bookorder")] public IActionResult PostBook([FromForm]BookOrder bookOrder) { if (!ModelState.IsValid) { return BadRequest(ModelState); } // 正常邏輯代碼 return Content("OK"); }
如果傳入的請求中缺少兩個必需的屬性(或者屬性的值為String.Empty),則響應將是一個400狀態碼,并將模型狀態錯誤序列化到響應中。
使用Postman進行測試,請求中只提供了Title
屬性的值,而未提供Author
屬性的值:
常有趣的問題,例如當我們添加類似數量的屬性時(類型為int
)。在這種情況下,RequiredAttribute
將無法正常工作,因為默認值為0
,并且該屬性不可能為空。即使客戶端在不包括屬性值的情況下提交請求,模型實例的值都將為0
。換句話說,RequiredAttribute
不起作用。在我們的例子中,我們使用的是int
,但是同樣適用于所有值的類型,例如DateTime
或Guid
。
以前,在傳統的ASP.NET MVC中,解決這個問題的方法是使用一個可為空類型,如下所示:
public class BookOrder { [Required] public string Author { get; set; } [Required] public string Title { get; set; } [Required] public int? Quantity { get; set; } }
雖然它解決了這個問題,但是這不是最優雅的方式。Quantity
屬性不能為null
,如果API的調用者未提供該屬性的值,則該值保持為null
,配合RequiredAttribute
特性可以達到我們的目的。
這種方法的缺點是,在正常邏輯代碼中,您需要直接訪問可為空的Value
屬性。
[Route("bookorder")] public IActionResult PostBook([FromForm]BookOrder bookOrder) { if (!ModelState.IsValid) { return BadRequest(ModelState); } // 正常邏輯代碼 var requestedQuantity = bookOrder.Quantity.Value; return Content("OK"); }
如果通過靜態分析工具來檢查代碼,因為它們找不不到空值檢測,所以會警告您可能出現空引用異常(即使在邏輯上,ModelState
檢測是足夠的)。所以這并不是令人賞心悅目的方案。
如果在VS中安裝了Resharper工具,會顯示如下警告:
這就是BindRequiredAttribute
產生的原因。它和RequiredAttribute
的工作方式類似,它要求請求必須提示屬性的值 。
我們將模型改為如下結構,Quantity
屬性為int
類型,并使用[BindRequired]
修飾:
public class BookOrder { [Required] public string Author { get; set; } [Required] public string Title { get; set; } [BindRequired] public int Quantity { get; set; } }
使用Postman進行測試,在請求體不包括Quantity
屬性的值:
測試結果為ModelState.IsValid
返回的false
,要求提供Quantity
屬性的值。
RequiredAttribute與BindRequiredAttribute的差別
BindRequiredAttribute
特性類只要求API的調用者提供該屬性的值,但不進行空值檢測,允許提供的值為空,例如我們將模型改為如下示例,所有屬性中都使用了BindRequiredAttribute
特性類進行注釋:
public class BookOrder { [BindRequired] public string Author { get; set; } [BindRequired] public string Title { get; set; } [BindRequired] public int Quantity { get; set; } }
使用Postman進行測試,在請求體包括這三個屬性名稱的參數,但是屬性的值均為空或默認值:
測試結果為正常,BindRequiredAttribute
特性類只要求API的調用者提供該屬性的值,但不進行空值檢測,允許提供的值為空。
RequiredAttribute 與 BindRequiredAttribute 混用問題
當然,在代碼中混合使用[Required]
和[BindRequired]
并不是一個最令人滿意的效果。值得慶幸的是,ASP.NET Core MVC為您提供了足夠的靈活性來改變RequiredAttribute
的行為,強制它包含BindRequiredAttribute
的行為。
您可以通過實現自己的IBindingMetadataProvider
并在您的應用程序全局范圍內注冊來實現這一目標。
public class RequiredBindingMetadataProvider : IBindingMetadataProvider { public void CreateBindingMetadata(BindingMetadataProviderContext context) { if (context.PropertyAttributes?.OfType().Any() ?? false) { context.BindingMetadata.IsBindingRequired = true; } } }
在全局范圍內注冊它:
public void ConfigureServices(IServiceCollection services) { services.AddMvc(o => { o.ModelMetadataDetailsProviders.Add(new RequiredBindingMetadataProvider()); }); }
相關補充內容
1、ASP.NET Core MVC還提供了BindNeverAttribute
特性類用于指定該屬性不進行模型綁定。例如:您擁有一個像IsAdmin
這樣的屬性,這需要通過服務端來指定,而不是通過客戶提交的數據來指定;
2、如果客戶端提交的是Json格式的數據,BindRequiredAttribute
與BindNeverAttribute
特性類就不會起任何作用,這是因為模型直接通過Json.Net反序列化來創建,MVC框架并不知道屬性的值從何而來。
上面補充的兩點,感興趣的同學可以自己進行測試。
總結
-
RequiredAttribute
特性類的行為在傳統的ASP.NET MVC與ASP.NET Core MVC是一致的,校驗屬性的值不能為null
; -
RequiredAttribute
特性類對于值類型的屬性,就沒有任何效果了,所以在編碼的過程將屬性的類型改為可為空類型,不過編碼的過程就比較糾結了,需要訪問可為空類型的Value
屬性,而且靜態代碼檢測工具會警告需要進行非空的校驗; -
BindRequiredAttribute
特性類可以解決值類型默認值的問題,強制要求客戶端提交請求時,必須包含屬性的值,不過屬性的值可以為空,不進行非空的校驗; - 在
RequiredAttribute
與BindRequiredAttribute
混合使用的情況下,我們也提供了優雅的方法,讓RequiredAttribute
包含BindRequiredAttribute
的行為。
原文鏈接:https://www.cnblogs.com/tdfblog/p/required-and-bindrequired-in-asp-net-core-mvc.html
相關推薦
- 2022-04-28 Go語言單元測試超詳細解析_Golang
- 2022-05-13 vscode代碼格式化 保存時候自動修改為合規代碼
- 2022-04-23 一起來了解一下C++的結構體?struct_C 語言
- 2022-11-07 一文教會你用python連接并簡單操作SQLserver數據庫_python
- 2022-05-03 C#設計模式之工廠模式_C#教程
- 2022-05-20 jmeter連接數據庫The driver has not received any packets
- 2022-07-26 Docker部署Golang+Gin和Python+Tornado后端
- 2022-03-30 詳解nginx?location指令_nginx
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支