1.模型绑定
asp.net core mvc 中的模型绑定将数据从http请求映射到操作方法参数。参数既可以是简单类型,也可以是复杂类型。mvc 通过抽象绑定解决了这个问题。
2.使用模型绑定
当 mvc 收到一个http 请求时,它会将其路由到一个控制器指定的操作方法。它基于路由数据来决定运行哪个操作,然后将值从http请求绑定到操作方法的参数中,例如
http://afei.com/movies/edit/2
movies/edit/2 通过路由模板路由到 movies 控制器的 edit 方法,同时接收到一个可选参数 id 。url 中的字符串是不区分大小写的。
mvc 将尝试通过名称将请求数据绑定到操作参数上。 mvc 将使用参数名称和其公共可设置的属性名称查找每个参数值。上面的例子,唯一的操作参数被命名为 id ,其中 mvc 绑定到路由值中具有相同名称的值。除了 路由值, mvc 还绑定请求的各个部分的数据,并按照设置的顺序这样做。
模型绑定查找数据源的顺序列表:
- 1. form values : 通过 http post 请求发送的表单数据(包括 jquery post 请求)。
- 2. route values :由 routing 提供的路由数据集。
- 3. query string : url 的查询字符串的一部分。
表单值,路由数据以及查询字符串都是以键值对的形式存储的。
因为模型绑定要找一个名为 id 的键,但是表单中没有,所以接下来在路由数据中找寻。绑定发生时,该值转换为整数类型的 2 。使用 edit(string id)的同一请求转换为字符串 “2” .
如果action 方法的参数是一个类,比如 movies 类型,尽管这个类包含简单类型和复杂类型的属性,mvc 也可以模型绑定。它使用反射和递归遍历复杂类型寻找匹配的属性。模型绑定寻找 parameter_name.property_name 的模式去绑定值到属性上。如果没有从表单中找到匹配的值,则尝试只通过 property_name 进行绑定。对于集合类型,模型绑定会去匹配 parameter_name[index] 或只是 [index] 。模型绑定对待字典类型也是一样,前提是key 是简单类型。key 支持匹配html 和 tag helpers 为相同的模型类型生成的字段名。当创建或编辑的绑定数据未通过验证时,回传值使得用户输入的表单字段仍然保留,方便用户输入。
为了发生绑定,类必须具有公共默认的构造函数,要绑定的成员必须是公共可写的属性,当绑定发生时,类只会使用公共默认的构造函数,然后设置属性。
当一个参数被绑定后,模型绑定停止寻找具有该名称的值,并继续绑定下一个参数。如果绑定失败,mvc 也不会抛出异常,可以通过modelstate.isvalid 属性来查询模型状态错误。
在执行绑定时,需要考虑一些特殊的数据类型:
- iformfile , ienumerable<iformfile> :作为 http 请求一部分的一个或多个上传的文件。
- cancelationtoken : 用于取消一部控制器中的活动。
这些类型可以绑定到操作参数或类的属性上。
一旦模型绑定完成,就会进行验证。默认模型绑定适合绝大多数开发场景,同时也可以自定义内置的行为。
3.通过特性自定义模型绑定行为
mvc 包含集中可以指定与默认绑定源不同行为的特性。比如,可以通过使用 [bindrequired] 或 [bindnever] 特性指定一个属性是否需要绑定,或者是否该发生。而且可以覆盖默认数据源,指定模型绑定器的数据源。
- [bindrequired] :如果不发生绑定,将添加模型状态错误。
- [bingnever] : 告诉模型绑定从不绑定到此参数。
- [fromheader] , [fromquery] , [fromroute] , [fromform] : 使用这些来指定应用的确切绑定源。
- [fromservices] : 此特性使用依赖注入来绑定服务的参数。
- [frombody] : 使用配置的格式化程序绑定请求主体中的数据,基于请求的内容类型选择格式化器。
- [modelbinder] : 用于覆盖默认模型绑定器,绑定源和名称。
4.从请求主体绑定格式化的数据
http 请求数据支持各种的格式,包括 json , xml 以及其他格式。当使用 [frombody] 特性的时候,表示要从请求主体中绑定参数。mvc 使用一组配置的格式化程序来根据其内容类型处理请求数据。默认情况下,mvc 包含一个 jsoninputformatter 类来处理json数据,当然也可以添加其他格式化程序来处理xml 和其他自定义格式。
每个操作最多可以有一个 [frombody] 装饰的参数。asp.net core mvc 运行时将读取请求流的职责委托给格式化程序。一旦读取了参数的请求流,通常不可能再次读取请求流以绑定其他 [frombody] 参数。
asp.net 基于content-type 头和参数的类型来选择输入格式化程序。如果想用 xml 或者其他格式,则必须在 startup.cs 文件中配置它,但首先使用 nuget 来获取 microsoft.aspnetcore.mvc.formatters.xml 引用。启动代码如下:
public void configureservices(iservicecollection services) { services.addmvc().setcompatibilityversion(compatibilityversion.version_2_1) .addxmlserializerformatters(); }
示例中,我们添加一个xml 格式化程序作为此mvc应用程序提供的服务。传递给 addmvc 方法的 options参数允许在应用程序启动时从mvc添加和管理过滤器,格式化程序和其他系统选项,然后应用各种特性到控制器类或方法上实现预期的效果。
5.模型验证
在应用程序将数据存储到数据库之前,应用程序必须验证数据。对于数据必须检查其是否存在潜在的安全隐患,验证类型和大小是否正确并且符合所定制的规则。尽管验证的实现可能时冗余且繁琐的,但是必要的。在 mvc 中,验证可以发生在客户端和服务端。
.net 已经将验证抽象为验证特性。这些特性包含验证代码,从而减少必须编码。
public class movies { public int id { get; set; } [required] [stringlength(100)] public string title { get; set; } [required] [classicmovie(1996)] [datatype(datatype.date)] public datetime releasedate { get; set; } [required] [stringlength(100)] public string description { get; set; } [required] [range(0,999.99)] public decimal price { get; set; } [required] public genre genre { get; set; } public bool preorder { get; set; } }
常见内置验证属性:
- [creditcard] : 验证属性是否为信用卡格式
- [compare] : 验证模型中的两个属性是否匹配
- [emailaddress] : 验证属性是否为电子邮件格式
- [phone] : 验证属性是否为电话号码格式
- [range] : 验证属性值是否在给定范围内
- [regularexpression] : 验证数据是否与指定的正则表达式匹配
- [required] : 必填的属性
- [stringlength] : 验证字符串属性的最大长度
- [url] : 验证属性是否为网址格式
mvc 支持从 validationattribute 派生的任何特性或者更改模型来实现 ialidatableobject 即创建自定义验证特性以用于验证目的。许多内置的验证特性可以在 ystem.componentmodel.dataannotations 中找到。
有时候我们需要手动需要验证模型,可以调用 tryvalidatemodel 方法来验证,如 tryvalidatemodel (movie)。
模型状态表示在html表单提交值的一系列验证错误。mvc 将持续验证字段直到错误数达到最大值(默认200)。可以在 configureservices 方法中配置这个最大值:
services.addmvc(options => options.maxmodelvalidationerrors = 500);
模型验证发生在每个控制器的操作 被调用之前,而检查 modelstate.isvalid 和做出适当的反应时操作方法的职责。许多情况下,适当的反应是返回某种错误响应,理想情况下则是详细介绍模型验证失败的原因。
6.自定义验证
实现自定义验证的方法很简单,只需要继承 validationattribute,并且重写 isvalid 方法即可。isvalid 方法接受两个参数,第一个是名为 value 的 object 对象,第二个对象是名为validationcontext 的 validationcontext 对象。value 指的是自定义验证器验证的字段的值。
下面自定义[classicmovie] 特性,该特性检查 movies 类的 releasedate 年份是否大于1960:
public class classicmovieattribute:validationattribute { private int _year; public classicmovieattribute(int year) { _year = year; } protected override validationresult isvalid(object value, validationcontext validationcontext) { movies movie = (movies)validationcontext.objectinstance; if (movie.releasedate.year > _year) { return new validationresult("发布年份不能大于"+_year); } return validationresult.success; } }
这个例子只对 movies 类型有效,因为在 isvalid 方法中硬编码了 movies 类型。一个更好的选择是 ivalidationobject。
通过在 ivalidatableobject 接口上实现 validate 方法,可以将相同的代码放在模型中。虽然自定义验证属性是用于验证单个属性,但实现 ivalidationobject 可用于实现类级别验证;
public class movies: ivalidatableobject { private int _classicyear = 1960; public int id { get; set; } [required] [stringlength(100)] public string title { get; set; } [required] [datatype(datatype.date)] public datetime releasedate { get; set; } [required] [stringlength(100)] public string description { get; set; } [required] [range(0,999.99)] public decimal price { get; set; } public bool preorder { get; set; } public ienumerable<validationresult> validate(validationcontext validationcontext) { if (releasedate.year > _classicyear) { yield return new validationresult("发布年份不能大于" + _classicyear,new[] { "releasedate" }); } } }
7.客户端验证
客户端验证带来了极大便利,它可以节省时间,不必花费一个来回时间等待服务器的验证结果。
你必须引用 javascript 脚本来进行客户端验证;
jquery-x.x.x.min.js jquery.validate.min.js jquery.validate.unobtrusive.min.js
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.3.1.min.js" asp-fallback-src="~/lib/jquery/dist/jquery.min.js" asp-fallback-test="window.jquery" crossorigin="anonymous" integrity="sha384-tsqfqpereu7zlhbv2vzlau7zcov+rxbylf2cqb8txi/8azajjp4bqd+v6d5igvkt"> </script> <script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.17.0/jquery.validate.min.js" asp-fallback-src="~/lib/jquery-validation/dist/jquery.validate.min.js" asp-fallback-test="window.jquery && window.jquery.validator" crossorigin="anonymous" integrity="sha384-rzfj/ogbloos6wzlgppkkor/gpkbnlz6b6yly4o+ok+t/sakll5mvxlr0oxni1hp"> </script> <script src="https://ajax.aspnetcdn.com/ajax/jquery.validation.unobtrusive/3.2.9/jquery.validate.unobtrusive.min.js" asp-fallback-src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js" asp-fallback-test="window.jquery && window.jquery.validator && window.jquery.validator.unobtrusive" crossorigin="anonymous" integrity="sha384-ifv0tydwxbhzvak2z0n8r434fl1rlv/av18dxe43n/1rvhyog4izkst0f2isldds"> </script>
除了模型属性的类型元数据外,mvc 还用 attribute 通过 javascript 验证数据并展示所有错误信息。当使用 mvc 去渲染使用 tag helper 或者 html helpers 的表单数据时,它将在需要验证的表单元素中添加html 5 data- attributes ,如下面,mvc 对内置验证 attribute 和自定义验证 attribute 生成 data - 特性。可以通过 tag helper 在客户端显示验证错误:
<div class="form-group"> <label asp-for="id" class="control-label"></label> <input asp-for="id" class="form-control" /> <span asp-validation-for="id" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="releasedate" class="control-label"></label> <input asp-for="releasedate" class="form-control" /> <span asp-validation-for="releasedate" class="text-danger"></span> </div>
上面的 tag helper 渲染的 html 如下,注意输出的 html 中,data-特性对应 name 属性的验证 attribute ,data-val-required 特性包含一个用于展示错误消息,如果没填写 releasedate 字段,则错误消息将随着<span> 一起显示:
<div class="form-group"> <label class="control-label" for="id">id</label> <input name="id" class="form-control" id="id" type="number" value="" data-val-required="the id field is required." data-val="true"> <span class="text-danger field-validation-valid" data-valmsg-replace="true" data-valmsg-for="id"></span> </div> <div class="form-group"> <label class="control-label" for="releasedate">releasedate</label> <input name="releasedate" class="form-control" id="releasedate" data-val-required="the releasedate field is required." data-val="true" type="text" value=""> <span class="text-danger field-validation-valid" data-valmsg-replace="true" data-valmsg-for="releasedate"></span> </div>
客户端验证防止表单提交直到有效为止。无论提交表单还是显示错误信息,提交按钮都会执行 javascript 代码。
mvc 基于 .net 属性的数据类型决定了类型特性值。可以使用 [datatype] 特性来覆盖。基础的 [datatype] 特性并不是真正的服务端验证。浏览器选择自己的错误信息,并显示,但 jquery validation unobtrusive 包可以重写消息,并让他们显示方式一致。
8.远程验证
当需要在客户端上使用服务器上的数据进行验证的时候,远程验证是一个很好的功能。比如,应用程序需要验证一个用户名是否已经被使用,并且需要查询大量数据才能执行。下载大量数据验证会占用大量资源,也可能暴露敏感信息。另一个办法是使用回传请求来验证。
两个步骤就可以实现远程验证,首先必须使用 [remote] 特性注释模型属性。 [remote] 特性接受多个重载,可以使用它将客户端 javascript 定向到要调用的相应代码。下面的代码时执行 users 控制器的 verfyuser 方法:
public class user { [remote(action: "verfyuser",controller:"users")] public string name { get; set; } }
然后在 verfyuser 方法中实现方法,它返回一个 jsonresult :
public class userscontroller : controller { public iactionresult verfyuser(string name) { if (!_usersrepository.verfyuser(name)) { return json(data:$"{name} 已存在"); } return json(data:true); } }
现在,当用户输入名字时,视图中的 javascript 会进行远程调用进行验证。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持代码网。
发表评论