<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          .NET之模型綁定和驗(yàn)證

          共 27769字,需瀏覽 56分鐘

           ·

          2021-07-10 07:51

          介紹

          模型綁定就是接收將來(lái)自HTTP請(qǐng)求的數(shù)據(jù)映射到模型的過(guò)程。如果找不到模型屬性的值,并不會(huì)報(bào)錯(cuò),而是給該屬性設(shè)置默認(rèn)值。

          示例:比如我們有一個(gè)接口為

          [HttpGet("{id}")]
          public ActionResult<Pet> GetById(int id, bool dogsOnly)

          這個(gè)時(shí)候你的請(qǐng)求為:http://localhost:5000/api/pets/2?DogsOnly=true

          路由系統(tǒng)選擇該Action后,模型綁定會(huì)執(zhí)行以下的步驟:

          • 查找 GetByID 的第一個(gè)參數(shù),該參數(shù)是一個(gè)名為 id 的整數(shù)。
          • 查找 HTTP 請(qǐng)求中的可用源,并在路由數(shù)據(jù)中查找 id =“2”。
          • 將字符串“2”轉(zhuǎn)換為整數(shù) 2。
          • 查找 GetByID 的下一個(gè)參數(shù),該參數(shù)是一個(gè)名為 dogsOnly 的布爾值。
          • 查找源,并在查詢字符串中查找“DogsOnly=true”。名稱匹配不區(qū)分大小寫。
          • 將字符串“true”轉(zhuǎn)換為布爾值 true。

          最后會(huì)調(diào)用GetById方法,參數(shù)Id為2,參數(shù)dogsOnly為true。

          默認(rèn)情況下,模型綁定以鍵值對(duì)的形式從HTTP請(qǐng)求中的以下源中獲取數(shù)據(jù):

          1. 表單域
          2. 請(qǐng)求正文
          3. 路由數(shù)據(jù)
          4. 查詢字符串參數(shù)
          5. 上傳的文件

          對(duì)于每個(gè)參數(shù),按照順序掃描源。也可以直接指定源

          • [FromQuery] - 從查詢字符串獲取值。
          • [FromRoute] - 從路由數(shù)據(jù)獲取值。
          • [FromForm] - 從發(fā)布表單字段中獲取值。
          • [FromBody] - 從請(qǐng)求正文獲取值。
          • [FromHeader] - 從 HTTP 標(biāo)頭獲取值。

          示例:

          [HttpGet]
          public async Task<User> GetAsync([FromQuery]string id)
              
          [HttpGet]
          public async Task<User> GetAsync([FromRoute]string id)
              
          [HttpGet]
          public async Task<User> GetAsync([FromForm]string id)
              
          [HttpPost]
          public async Task<ActionResult<string>> AddAsync([FromBody]AddUserVm dto)
              
          public void OnGet([FromHeader(Name = "Accept-Language")] string language)

          也可以編寫自定義的值提供程序,比如從cookie中獲取會(huì)話狀態(tài),參考:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/models/model-binding?view=aspnetcore-5.0#additional-sources

          模型綁定

          簡(jiǎn)單模型綁定

          例如:bool、byte、char、DateTime、DateTimeOffset、float、enum、guid、int、TimeSpan、Url、Version等

          復(fù)雜類型

          使用復(fù)雜類型必須具有要綁定的公共默認(rèn)構(gòu)造函數(shù)和公共可寫屬性。進(jìn)行模型綁定時(shí)候,將使用公共默認(rèn)構(gòu)造函數(shù)來(lái)實(shí)例化類。對(duì)于復(fù)雜類型的每個(gè)屬性,模型綁定會(huì)查找名稱模式 prefix.property_name 的源。如果未找到,它將僅查找不含前綴的 properties_name。不過(guò)一般我們使用都是進(jìn)行完全匹配,特殊需求才會(huì)做此操作。

          參考資料:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/models/model-binding?view=aspnetcore-5.0#complex-types

          內(nèi)置自定義模型綁定

          通過(guò)ByteArrayModelBinder 可以實(shí)現(xiàn)將傳輸?shù)腷ase64編碼字符串轉(zhuǎn)換為字節(jié)數(shù)組。

          比如:

                  [HttpPost]
                  public void Post([FromForm] byte[] file, string filename)
                  {
                      var trustedFileName = Path.GetRandomFileName();
                      var filePath = Path.Combine("e://", trustedFileName);

                      if (System.IO.File.Exists(filePath))
                      {
                          return;
                      }

                      System.IO.File.WriteAllBytes(filePath, file);
                  }

          請(qǐng)求示例

          image.png

          接收結(jié)果

          image.png

          自定義模型綁定

          示例場(chǎng)景:通過(guò)請(qǐng)求頭傳遞后端自定義的一種token,通過(guò)自定義模型綁定將token解析后綁定到請(qǐng)求模型。

          參考資料:https://www.cnblogs.com/jyzhu/articles/8670536.html

          請(qǐng)求接口示例

                  [HttpGet]
                  public ActionResult GetToken(TokenModel dto)
                  {
                      return Ok(dto);
                  }

          首先定義token模型類

              public class TokenModel
              {
                  public int UserID { get; set; }

                  public string UserName { get; set; }
              }

          自定義模型綁定器

              public class TokenModelBinder : IModelBinder
              {
                  /// <summary>
                  /// 請(qǐng)求里傳遞參數(shù)token
                  /// </summary>
                  /// <param name="bindingContext"></param>
                  /// <returns></returns>
                  public Task BindModelAsync(ModelBindingContext bindingContext)
                  {
                      //參數(shù)必須包含token
                      if (!(bindingContext.ActionContext.HttpContext.Request.Headers.ContainsKey("token")))
                          return Task.CompletedTask;

                      var token = bindingContext.ActionContext.HttpContext.Request.Headers["token"];

                      //TODO  解析token
                      var result = new TokenModel()
                      {
                          UserID = 111,
                          UserName = "azrng",
                      };
                      bindingContext.Result = ModelBindingResult.Success(result);
                      return Task.CompletedTask;
                  }
              }

          定義token框架綁定器

              public class TokenModelBinderProvider : IModelBinderProvider
              {
                  public IModelBinder GetBinder(ModelBinderProviderContext context)
                  {
                      if (context == null)
                      {
                          throw new ArgumentNullException(nameof(context));
                      }

                      if (context.Metadata.ModelType == typeof(TokenModel))
                          return new TokenModelBinder();

                      return null;
                  }
              }

          啟用綁定器

              services.AddControllers(options =>
              {
                  options.ModelBinderProviders.Insert(0, new TokenModelBinderProvider());
              });

          請(qǐng)求示例

          var client = new RestClient("http://localhost:5000/api/ModelVerify/GetToken");
          client.Timeout = -1;
          var request = new RestRequest(Method.GET);
          request.AddHeader("token""123456");
          IRestResponse response = client.Execute(request);
          Console.WriteLine(response.Content);

          結(jié)果就是可以在GetToken方法參數(shù)獲取到我們token的值。

          模型校驗(yàn)

          現(xiàn)在dotNetCore如果在控制器標(biāo)識(shí)[ApiController],那么就會(huì)在進(jìn)action前就會(huì)自動(dòng)校驗(yàn)?zāi)P皖惤壎ㄊ欠穹弦螅绻环弦笞詣?dòng)觸發(fā)HTTP400錯(cuò)誤響應(yīng)。原文

          [ApiController]
          [Route("[controller]")]
          public class WeatherForecastController : ControllerBase

          驗(yàn)證特性

          通過(guò)驗(yàn)證特性可為屬性增加驗(yàn)證規(guī)則。不僅僅有內(nèi)置的驗(yàn)證特性,還可以實(shí)現(xiàn)自定義驗(yàn)證特性。

          內(nèi)置驗(yàn)證特性

          常用的有:必填、長(zhǎng)度驗(yàn)證、數(shù)值范圍、手機(jī)號(hào)碼、郵箱,還可以使用正則驗(yàn)證

              public class AddModelVerify
              {
                  [Display(Name = "名稱"), Required(ErrorMessage = "{0}不能為空")]// 非空校驗(yàn) 
                  [MinLength(6, ErrorMessage = "名稱不能小于6位")] // 最小長(zhǎng)度校驗(yàn)
                  [MaxLength(10, ErrorMessage = "長(zhǎng)度不超過(guò)10個(gè)")] // 最大長(zhǎng)度校驗(yàn)
                  public string UserName { get; set; }

                  /// <summary>
                  /// 密碼
                  /// </summary>
                  [Display(Name = "密碼"), Required(ErrorMessage = "{0}不能為空")]
                  [MinLength(6, ErrorMessage = "密碼必須大于6位")]
                  public string PassWord { get; set; }

                  [Display(Name = "工號(hào)")] // 友好名稱錯(cuò)誤提示
                  [Required(ErrorMessage = "{0}不能為空")]
                  [StringLength(10, MinimumLength = 1, ErrorMessage = "{0}長(zhǎng)度是{1}")]
                  public string EmployeeNo { get; set; }
              }

              public IActionResult VerifyPhone([RegularExpression(@"^\d{3}-\d{3}-\d{4}$")] string phone)

          除了上面這些還有其他內(nèi)置特性:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/models/validation?view=aspnetcore-5.0#built-in-attributes

          請(qǐng)求地址傳入空值,輸出結(jié)果:HTTP錯(cuò)誤400

          {
            "errors": {
              "PassWord": [
                "密碼不能為空",
                "密碼必須大于6位"
              ],
              "UserName": [
                "名稱不能為空",
                "名稱不能小于6位"
              ],
              "EmployeeNo": [
                "工號(hào)不能為空",
                "工號(hào)長(zhǎng)度是10"
              ]
            },
            "type""https://tools.ietf.org/html/rfc7231#section-6.5.1",
            "title""One or more validation errors occurred.",
            "status": 400,
            "traceId""00-d16b945b3e172a42bfe5b53d08f7487b-8d87c2ca238fdc4a-00"
          }

          還有一個(gè)Remote特性感覺挺有意思,使用場(chǎng)景是比如在ID上標(biāo)注遠(yuǎn)程特性,綁定時(shí)候自定驗(yàn)證ID是否有效

          [AcceptVerbs("GET""POST")]
          public IActionResult VerifyID(string id)
          {
              if (!_userService.VerifyID(id))
              {
                  return Json($"對(duì)象未找到");
              }
              return Json(true);
          }

          模型類使用指向操作方法的[Remote]特性注釋屬性

          [Remote(action: "VerifyID", controller: "Users")]
          public string ID { get; set; }

          Remote其他用法:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/models/validation?view=aspnetcore-5.0#additional-fields

          自定義特性

          對(duì)于內(nèi)置驗(yàn)證特性無(wú)法處理的情況,我們可以創(chuàng)建自定義驗(yàn)證特性。

          模擬場(chǎng)景:添加用戶時(shí)候,設(shè)置名字和工號(hào)不能一致,出生日期必須小于當(dāng)前時(shí)間

          輸入模型類

              public class AddUserinfoVm
              {
                  [Display(Name = "名稱"), Required(ErrorMessage = "{0}不能為空")]
                  [MinLength(6, ErrorMessage = "名稱不能小于6位")]
                  [MaxLength(10, ErrorMessage = "長(zhǎng)度不超過(guò)10個(gè)")]
                  public string UserName { get; set; }

                  /// <summary>
                  /// 密碼
                  /// </summary>
                  [Display(Name = "密碼"), Required(ErrorMessage = "{0}不能為空")]
                  [MinLength(6, ErrorMessage = "密碼必須大于6位")]
                  public string PassWord { get; set; }

                  [Display(Name = "工號(hào)")]
                  [Required(ErrorMessage = "{0}不能為空")]
                  [StringLength(10, MinimumLength = 1, ErrorMessage = "{0}長(zhǎng)度是{1}")]
                  public string EmployeeNo { get; set; }

                  /// <summary>
                  /// 出生日期
                  /// </summary>
                  public DateTime Birthday { get; set; }
              }

          方案一:通過(guò)添加AddUserVerifyAttribute來(lái)實(shí)現(xiàn)

              [AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
              public class AddUserVerifyAttribute : ValidationAttribute
              {
                  protected override ValidationResult IsValid(object value, ValidationContext validationContext)
                  {
                      var user = (AddUserinfoVm)validationContext.ObjectInstance;//user 變量表示 AddUserinfoVm 對(duì)象,其中包含表單提交中的數(shù)據(jù)
                      var date = (DateTime)value;
                      if (date > DateTime.Now)
                      {
                          return new ValidationResult("出生日期不能大于當(dāng)前時(shí)間");
                      }
                      if (user.UserName == user.EmployeeNo)
                      {
                          return new ValidationResult("名稱和工號(hào)不能一樣");
                      }
                      return ValidationResult.Success;
                  }
              }

          使用方法

                  [AddUserVerify]
                  public DateTime Birthday { get; set; }

          方案二:模型類中繼承IValidatableObject,并實(shí)現(xiàn)Validate方法

                  /// <summary>
                  /// 屬性級(jí)別的自定義驗(yàn)證
                  /// </summary>
                  /// <param name="validationContext"></param>
                  /// <returns></returns>   
                  public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
                  {
                      if (Birthday > DateTime.Now)
                      {
                          yield return new ValidationResult("出生日期不能大于當(dāng)前時(shí)間", new[] { nameof(Birthday) });
                      }
                      if (UserName == EmployeeNo)
                      {
                          yield return new ValidationResult("名稱和工號(hào)不能一樣", new[] { nameof(UserName), nameof(EmployeeNo) });
                      }
                  }

          請(qǐng)求參數(shù):

          {
            "userName""string",
            "passWord""string",
            "employeeNo""string",
            "birthday""2021-06-15T14:34:52.192Z"
          }

          輸出錯(cuò)誤信息

          {
            "errors": {
              "Birthday": [
                "出生日期不能大于當(dāng)前時(shí)間"
              ],
              "UserName": [
                "名稱和工號(hào)不能一樣"
              ],
              "EmployeeNo": [
                "名稱和工號(hào)不能一樣"
              ]
            },
            "type""https://tools.ietf.org/html/rfc7231#section-6.5.1",
            "title""One or more validation errors occurred.",
            "status": 400,
            "traceId""00-18854d59f6b6fc48b5c4c6a6dbe3802c-ba23f594f351a64d-00"
          }

          ModelState.IsValid

          通過(guò)該方法可以實(shí)現(xiàn)對(duì)請(qǐng)求類驗(yàn)證是否滿足要求并做出相應(yīng)的響應(yīng)。

          如果已經(jīng)使用[ApiController]標(biāo)識(shí),那么該方法就不在需要。

                  [HttpPost]
                  public ActionResult Add([FromBody] AddModelVerify dto)
                  {
                      //對(duì)請(qǐng)求類進(jìn)行驗(yàn)證特性
                      if (ModelState.IsValid)
                      {
                          //對(duì)請(qǐng)求類的值做出修改
                          dto.UserName = "azrng";
                          if (!TryValidateModel(dto))
                          {
                              //重新運(yùn)行驗(yàn)證失敗
                              return Ok("修改值后驗(yàn)證失敗");
                          }
                          return Ok("驗(yàn)證成功");
                      }
                      else
                      {
                          ModelState.AddModelError(string.Empty, "輸入有誤");
                      }
                      return Ok("");
                  }

          禁用驗(yàn)證

              /// <summary>
              /// 創(chuàng)建不會(huì)將任何字段標(biāo)記為無(wú)效的 IObjectModelValidator 實(shí)現(xiàn)。
              /// </summary>
              public class NullObjectModelValidator : IObjectModelValidator
              {
                  public void Validate(ActionContext actionContext,
                      ValidationStateDictionary validationState, string prefix, object model)
                  {
                      // 該方法故意為空
                  }
              }

          Startup.ConfigureServices中注入,以便替換依賴項(xiàng)注入容器中的默認(rèn) IObjectModelValidator 實(shí)現(xiàn)。

          services.AddSingleton<IObjectModelValidator, NullObjectModelValidator>();

          統(tǒng)一模型攔截器

          增加ModelActionFiter過(guò)濾器

              public class ModelActionFiter : ActionFilterAttribute
              {
                  public override void OnActionExecuted(ActionExecutedContext context)
                  {
                  }

                  public override void OnActionExecuting(ActionExecutingContext context)
                  {
                      if (!context.ModelState.IsValid)
                      {
                          var errorResults = new List<ErrorResultDTO>();
                          foreach (var item in context.ModelState)
                          {
                              var result = new ErrorResultDTO
                              {
                                  Field = item.Key,
                              };
                              foreach (var error in item.Value.Errors)
                              {
                                  if (!string.IsNullOrEmpty(result.Message))
                                  {
                                      result.Message += '|';
                                  }
                                  result.Message += error.ErrorMessage;
                              }
                              errorResults.Add(result);
                          }
                          context.Result = new BadRequestObjectResult(new
                          {
                              Code = StatusCodes.Status400BadRequest,
                              Errors = errorResults
                          });
                      }
                  }

                  public class ErrorResultDTO
                  {
                      /// <summary>
                      /// 參數(shù)領(lǐng)域
                      /// </summary>
                      public string Field { get; set; }

                      /// <summary>
                      /// 錯(cuò)誤信息
                      /// </summary>
                      public string Message { get; set; }
                  }
              }

          參考文檔:https://www.cnblogs.com/minskiter/p/11601873.html

          ConfigureServices中注冊(cè)過(guò)濾器并禁用默認(rèn)的自動(dòng)模型驗(yàn)證

              services.AddControllers(options =>
              {
                  options.Filters.Add<ModelActionFiter>(); //注冊(cè)過(guò)濾器 
              }).AddNewtonsoftJson().ConfigureApiBehaviorOptions(options =>
              {
                  //[ApiController] 默認(rèn)自帶有400模型驗(yàn)證,且優(yōu)先級(jí)比較高,如果需要自定義模型驗(yàn)證,則需要先關(guān)閉默認(rèn)的模型驗(yàn)證
                  options.SuppressModelStateInvalidFilter = true
              });

          ASP.NET Core MVC 使用 ModelStateInvalidFilter 操作篩選器來(lái)執(zhí)行自定義驗(yàn)證。

          輸出結(jié)果

          {
            "code": 400,
            "errors": [
              {
                "field""PassWord",
                "message""密碼不能為空|密碼必須大于6位"
              },
              {
                "field""UserName",
                "message""名稱不能為空|名稱不能小于6位"
              }
            ]
          }

          參考文檔

          模型綁定:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/models/model-binding?view=aspnetcore-5.0

          禁用綁定源推理:https://docs.microsoft.com/zh-cn/aspnet/core/web-api/?view=aspnetcore-5.0#disable-inference-rules

          禁用驗(yàn)證:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/models/validation?view=aspnetcore-5.0#disable-validation

          禁用自動(dòng)400響應(yīng):https://docs.microsoft.com/zh-cn/aspnet/core/web-api/?view=aspnetcore-5.0#disable-automatic-400-response

          歡迎各位讀者加入微信群一起學(xué)習(xí)交流,
          在公眾號(hào)后臺(tái)回復(fù)“加群”即可~~


          瀏覽 127
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  美女的尿口视频网站 | 青青综合网 | 51妺嘿嘿午夜福利视频 | 开心成人激情 | 噜噜久久综合 |