当前位置: 移动技术网 > IT编程>开发语言>.net > asp.net core系列 45 Web应用 模型绑定和验证

asp.net core系列 45 Web应用 模型绑定和验证

2019年03月21日  | 移动技术网IT编程  | 我要评论

山风点伙,13918779629,快手看片电脑版

一. 模型绑定

  asp.net core mvc 中的模型绑定,是将 http 请求中的数据映射到action方法参数。 这些参数可能是简单类型的参数,如字符串、整数或浮点数,也可能是复杂类型的参数。 当 mvc 收到 http 请求时,它会将此请求路由定位到控制器的指定action方法。默认路由模板为 {controller=home}/{action=index}/{id?}

    //例如:请求url
     http://contoso.com/movies/edit/2
    
     //映射到movies/edit/2
    public iactionresult edit(int? id)

   上面url请求对应movies控制器下的edit方法,该方法接受名为 id 的可选参数。mvc会将edit中的id参数绑定到路由值中具有相同名称的值。 url 路由中的字符串不区分大小写。

  上面示例绑定的参数是简单类型,如果参数是一个类,例如movie类型,该类包含简单和复杂类型作为属性,mvc的模型绑定仍然可以很好地处理它。它使用反射和递归来遍历寻找匹配的复杂类型的属性(如:collection、dictionary)。

  如果模型绑定失败,mvc 不会引发错误,参数值会是null。 如果http 请求中的数据是用户输入的值,在action中应使用 modelstate.isvalid 属性检查,不需要手动去检查。

  注意:若要实现模型绑定,该类必须具有要绑定的公共默认构造函数和公共可写属性。 发生模型绑定时,在使用公共默认构造函数对类进行实例化后才可设置属性。

  模型绑定完成后,将发生模型验证。 对于绝大多数开发方案,默认模型绑定效果极佳。还可以扩展,如果有特殊需求,则可自定义内置行为包括:模型绑定特性、全局自定义模型绑定和验证、绑定请求正文中的带格式数据(json、xml 和许多其他格式)、还有高级篇中自定义模型绑定。这里不在说明,请查看文档。

 

二.模型验证

  在将数据存储到数据库之前,应用程序必须先验证数据。在 mvc 中,验证发生在客户端和服务器上。

  

  2.1 验证属性

    验证属性是模型验证的一种方法, 概念上类似于对数据库表中字段的验证, 验证属性在属性级别指定,下面是一个示例:

public class movie
{
    public int id { get; set; }

    [required]
    [stringlength(100)]
    public string title { get; set; }

    [classicmovie(1960)]
    [datatype(datatype.date)]
    public datetime releasedate { get; set; }

    [required]
    [stringlength(1000)]
    public string description { get; set; }

    [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] url格式,还可以包括自定义验证属性(例如classicmovie)。所有的内置验证属性

   

   2.2 自定义验证

     上面的验证属性适用于大多数验证需求。 但是,某些验证规则特定于你的业务。在 mvc 中创建自定义验证属性很简单。只需从 validationattribute 继承并重写 isvalid方法。 isvalid 方法采用两个参数,第一个是名为 value 的对象,第二个是名为 validationcontext 的 validationcontext 对象。 value 引用自定义验证程序要验证的字段中的实际值。

    /// <summary>
    /// 自定义验证
    /// </summary>
    public class classicmovieattribute : validationattribute
    {
        private int _year;

        /// <summary>
        /// 验证规则值
        /// </summary>
        /// <param name="year"></param>
        public classicmovieattribute(int year)
        {
            _year = year;
        }

        protected override validationresult isvalid(object value, validationcontext validationcontext)
        {
            movie movie = (movie)validationcontext.objectinstance;

            //用户不能将 1960 年以后发行的电影的流派设置为 classic
            if (movie.genre == "classic" && movie.releasedate.year > _year)
            {
                return new validationresult(geterrormessage());
            }

            return validationresult.success;
        }

        private string geterrormessage()
        {
            return $"classic movies must have a release year earlier than {_year}.";
        }
    }

     运行程序,releasedate是1989年,genre是classic,点击save,验证是在服务端进行,显示错误消息,没有经过前端js验证,如下所示:

  

         2.3 客户端js验证介绍

    jquery 非介入式验证脚本是一个自定义微软前端库,建立在流行的 jquery validate 插件。客户端验证原理是: mvc 的标记帮助程序和 html 帮助程序则能够使用模型属性中的验证特性和类型元数据,呈现需要验证的表单元素中的 html 5 data- 属性。mvc 为内置模型属性和自定义模型属性生成 data- 属性。然后,jquery 非介入式验证分析 data- 属性并将逻辑传递给 jquery validate,从而将服务器端验证逻辑有效地“复制”到客户端。 可以使用相关标记帮助程序在客户端上显示验证错误。

    下面示例表单中,asp- 标记帮助程序代码如下:

       <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>

    标记帮助程序将生成以下source html。请注意,html 输出中的 data-属性与 releasedate 属性的验证特性相对应。下面的 data-val-required 属性包含在用户未填写发行日期字段时将显示的错误消息。jquery 非介入式验证将此值传递给 jquery validate required() 方法,该方法随后在随附的 <span> 元素中显示该错误消息。

<form action="/movies/create" method="post">
    <div class="form-horizontal">
        <h4>movie</h4>
        <div class="text-danger"></div>
        <div class="form-group">
            <label class="col-md-2 control-label" for="releasedate">releasedate</label>
            <div class="col-md-10">
                <input class="form-control" type="datetime"
                data-val="true" data-val-required="the releasedate field is required."
                id="releasedate" name="releasedate" value="" />
                <span class="text-danger field-validation-valid"
                data-valmsg-for="releasedate" data-valmsg-replace="true"></span>
            </div>
        </div>
    </div>
</form>

   

  2.4 动态表单添加验证

    在创建动态表单后,需要立即对其进行分析。 例如,下面的代码展示如何对通过 ajax 添加的表单设置客户端验证。

$.get({
    url: "https://url/that/returns/a/form",
    datatype: "html",
    error: function(jqxhr, textstatus, errorthrown) {
        alert(textstatus + ": couldn't add form. " + errorthrown);
    },
    success: function(newformhtml) {
        //添加表单newformhtml
        var container = document.getelementbyid("form-container");
        container.insertadjacenthtml("beforeend", newformhtml);
        //验证第一个表单
        var forms = container.getelementsbytagname("form");
        var newform = forms[forms.length - 1];
        //分析表单的 data- 属性
        $.validator.unobtrusive.parse(newform);
    }
})

     $.validator.unobtrusive.parse() 方法分析该选择器内表单的 data- 属性。当用户填写表单中的属性值提交时, 这些属性的值传递到 jquery validate 插件中,以便表单展示所需的客户端验证规则。

    下面用一个简单示例来说明:

    (1)  创建dynamic-form-validate.js文件,模拟动态生成表单,以及点击(#idbtn)按钮时验证:

var newformhtml = "<form action=\"create\" method=\"post\">";
newformhtml += "<div class=\"form-group\">";
newformhtml += "<label asp-for=\"title\" class=\"control- label\"></label>";
newformhtml += "<input  type=\"text\" data-val=\"true\" data-val-required=\"the title field is required.\" id = \"title\" name= \"title\">";
newformhtml += "<span class=\"text- danger field- validation - valid\" data-valmsg-for=\"title\" data-valmsg-replace=\"true\"></span>";
newformhtml += "</div>";
newformhtml += "<div class=\"form-group\" >";
newformhtml += "<input type=\"submit\" value=\"save\" class=\"btn btn-primary\" />";
newformhtml += "</div >";
newformhtml += "</form>";

$("#idbtn").click(function () {
    var container = document.getelementbyid("form-container");
    container.insertadjacenthtml("beforeend", newformhtml);

    var forms = container.getelementsbytagname("form");
    var newform = forms[forms.length - 1];
    //分析表单的 data- 属性
    $.validator.unobtrusive.parse(newform);
});

     (2) 新建create页

@model studymvcdemo.models.movie
@{
    viewdata["title"] = "create";
}
<h1>create</h1>

<div class="row">
    <input value="动态加载表单" type="button" id="idbtn" />
    <div id="form-container" class="col-md-4">

    </div>
</div>
<div>
    <a asp-action="index">back to list</a>
</div>
@section scripts {
    @{await html.renderpartialasync("_validationscriptspartial");}
    <script src="~/js/dynamic-form-validate.js"></script>
}

     运行程序,点击"动态加载表单" 调用js将html表单添加到form-container元素容器中,点击save提示该字段不能为空,效果如下所示:

  

  2.5 动态控件添加验证

    当向表单动态添加控件(比如: <input/> 和 <select/>)时,需要更新表单上的验证规则。做法是应当先删除现有的验证数据,然后重新分析整个表单,如下js代码所示:

$.get({
    url: "https://url/that/returns/a/control",
    datatype: "html",
    error: function(jqxhr, textstatus, errorthrown) {
        alert(textstatus + ": couldn't add control. " + errorthrown);
    },
    success: function(newinputhtml) {
         //向表单动态添加input控件
        var form = document.getelementbyid("my-form");
        form.insertadjacenthtml("beforeend", newinputhtml);
         //移除现有的验证
        $(form).removedata("validator")    // added by jquery validate
               .removedata("unobtrusivevalidation");   // added by jquery unobtrusive validation
          //重新分析整个表单
        $.validator.unobtrusive.parse(form);
    }
})

 

  2.6  iclientmodelvalidator

    在上面2.2自定义验证中,继承了validationattribute进行服务端验证,还可以结合实现iclientmodelvalidator接口实现客户端验证,该接口用来控制要添加哪些 data- 属性。实现接口如下所示:

    /// <summary>
    /// 自定义验证
    /// </summary>
    public class classicmovieattribute : validationattribute,iclientmodelvalidator
    {
        private int _year;

        /// <summary>
        /// 年份参考值
        /// </summary>
        /// <param name="year"></param>
        public classicmovieattribute(int year)
        {
            _year = year;
        }

        protected override validationresult isvalid(object value, validationcontext validationcontext)
        {
            movie movie = (movie)validationcontext.objectinstance;

            //用户不能将 1960 年以后发行的电影的流派设置为 classic
            if (movie.genre == "classic" && movie.releasedate.year > _year)
            {
                return new validationresult(geterrormessage());
            }

            return validationresult.success;
        }

        private string geterrormessage()
        {
            return $"classic movies must have a release year earlier than {_year}.";
        }

        public void addvalidation(clientmodelvalidationcontext context)
        {
            if (context == null)
            {
                throw new argumentnullexception(nameof(context));
            }

            mergeattribute(context.attributes, "data-val", "true");
            mergeattribute(context.attributes, "data-val-classicmovie", geterrormessage());

            var year = _year.tostring(cultureinfo.invariantculture);
            mergeattribute(context.attributes, "data-val-classicmovie-year", year);
        }

        private bool mergeattribute(idictionary<string, string> attributes, string key, string value)
        {
            if (attributes.containskey(key))
            {
                return false;
            }

            attributes.add(key, value);
            return true;
        }
    }

     生成的源html代码如下所示:

<input class="form-control" type="date" 
data-val="true" 
data-val-classicmovie="classic movies must have a release year earlier than 1960."
data-val-classicmovie-year="1960" 
data-val-required="the releasedate field is required." 
id="releasedate" name="releasedate" value="1989-02-12">    

     在上面虽然实现了iclientmodelvalidator接口,但jquery不了解规则或消息,还需要自定义classicmovie客户端验证方法,添加到jquery validator对象。脚本如下所示:

//添加验证方法
$.validator.addmethod('classicmovie',function (value, element, params) {
         //value ,是当前验证的元素的值。
         //element  元素本身。
         //params 是传入的参数(options.rules)
        var genre = $("#form1").find("#genre").val(),
            year = params[0],
            date = new date(value);
        if (genre.length > 0 && genre === 'classic') {
            // since this is a classic movie, invalid if release date is after given year.
            return date.getfullyear() <= year;
        }
        return true;
    });

//注册一个适配器,参数1是适配器名称,参数2是验证规则的名称
$.validator.unobtrusive.adapters.add('classicmovie',['year'],function (options) {
        //适配器规则绑定到jquery validation上面
        options.rules['classicmovie'] = [parseint(options.params['year'])];
        options.messages['classicmovie'] = options.message;
    });

     运行程序,releasedate是1989年,genre是classic,点击save,客户端验证返回false,提示错误信息,如下所示:

    

  参考文献

    

    

  

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网