ASP.NET Core MVC/WebAPi 模型绑定探索

前言

相信一直关注我的园友都知道,我写的博文都没有特别枯燥理论性的东西,主要是当每开启一门新的技术之旅时,刚开始就直接去看底层实现原理,第一会感觉索然无味,第二也不明白到底为何要这样做,所以只有当你用到了,你再去看理论性的文章时才会豁然开朗,这是我一直以来学习技术的方法。本文我们来讲解.NET Core中的模型绑定。

话题

在ASP.NET Core之前MVC和Web APi被分开,也就说其请求管道是独立的,而在ASP.NET Core中,WebAPi和MVC的请求管道被合并在一起,当我们建立控制器时此时只有一个Controller的基类而不再是Controller和APiController。所以才有本节的话题在模型绑定上呈现出有何不同呢?下面我们一起来看看。

ASP.NET MVC模型绑定

我们首先还是老规矩给出测试类

    public class Person
    {
        public string Name { get; set; }
        public string Address { get; set; }
        public int Age { get; set; }
    }

接着POST请求通过Action方法进行模型绑定。

        [HttpPost]
        public JsonResult PostPerson(Person p)
        {
            return Json(p);
        }

到这里,后台就大概over了,是不是就这么完了呢,我们一直在强调模型绑定这个词语,那么到底什么是模型绑定呢,有必要解释下。我们PostPerson这个方法中有一个Person的变量参数,那么问题来了,前台发出请求到这个方法从而该参数接收到传递过来的数据从而进行响应,这个p到底是怎么接收到的呢,恩,通过模型绑定呗,为了将数据聚合到对象或者其他简单的参数可以通过模型绑定来查找数据,常见的绑定方式有如下四种。

路由值(Route Values):通过导航到路由如{controller}/{action}/{id}此时将绑定到id参数。

查询字符串(QueryStrings):通过查询字符串中的参数来绑定,如name=Jeffcky&id=1,此时name和id将进行绑定。

请求Body(Body):通过在POST请求中将数据传入到Body中此时将绑定如上述Person对象中。

请求Header(Header):绑定数据到Http中的请求头中,这种相对来说比较少见。

所以通过上述讲述我们知道有多种方式将数据从客户端传递到服务端,然后模型绑定会自动为我们创建正确的方法来绑定到后台参数中,简单和复杂的类型参数都会进行绑定。

接下来我们来演示在ASP.NET MVC中绑定的方式。此时只需给出前台页面了。

      <form id="form">
        <div class="form-group">
            <div class="control-label col-md-2">名称:</div>
            <div class="col-md-10">
                <input name="Name" id="Name" class="form-control" />
            </div>
        </div>
        <div class="form-group">
            <div class="control-label col-md-2">年龄:</div>
            <div class="col-md-10">
                <input name="Age" id="Age" class="form-control" />
            </div>
        </div>
        <div class="form-group">
            <div class="control-label col-md-2">家乡地址:</div>
            <div class="col-md-10">
                <input name="Address" id="Address" class="form-control" />
            </div>
        </div>
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="button" id="btnForm" value="MVC提交表单" class="btn btn-success" />
                <input type="button" id="btnJson" value="WebAPi提交" class="btn btn-warning" />
            </div>
        </div>
    </form>

首先我们提交表单形式来传输数据。

           $("#btnForm").on("click", function () {
                var dataform = $('form').serialize();
                $.ajax({
                    url: "../MVC/PostPerson",
                    contentType: "application/x-www-form-urlencoded;charset=utf-8",
                    dataType: "json",
                    type: "post",
                    data: dataform,
                    success: function (data) {
                        console.log(data);
                    }
                });
            });

 

然后我们通过传输JSON的数据同样来发出POST请求。

             $("#btnForm").on("click", function () {
                $.ajax({
                    url: "../MVC/PostPerson",
                    contentType: "application/json;charset=utf-8",
                    dataType: "json",
                    type: "post",
                    data: JSON.stringify(datajson),
                    success: function (data) {
                        console.log(data);
                    }
                });
            });

结果同样返回上述数据,就不再演示,下面我们看看WebAPi中的情况。

ASP.NET WebAPi模型绑定

当然上述利用JSON传输数据同样也适用于WebAPi,下面我们来看看利用查询字符串在WebAPi中的情况。

           $("#btnJson").on("click", function () {
                var dataform = $('form').serialize();
                console.log(dataform);
                $.ajax({
                    url: "../api/WebAPi/PostPerson",
                    contentType: "application/x-www-form-urlencoded;charset=utf-8",
                    dataType: "json",
                    type: "post",
                    data: dataform,
                    success: function (data) {
                        console.log(data);
                    }
                });
            });

我们再来看看在WebAPi中表单的形式。

             $("#btnJson").on("click", function () {
                var datajson = { Name: "Jeffcky", Age: 24, Address: "湖南省" };
                console.log(datajson);
                $.ajax({
                    url: "../api/WebAPi/PostPerson",
                    contentType: "application/x-www-form-urlencoded;charset=utf-8",
                    dataType: "json",
                    type: "post",
                    data: datajson,
                    success: function (data) {
                        console.log(data);
                    }
                });
            });

上述我们看到在ASP.NET MVC/WebAPi中无论是以表单POST的形式抑或JSON的形式控制器具有绑定都Http请求Body的能力同时数据都会返回给我们,我们不需要做出任何特别的说明,所以都没毛病。接下来我们来看看ASP.NET Core MVC/WebAPi中的模型绑定形式。

ASP.NET Core MVC/WebAPi

在ASP.NET Core中为了加载服务器的静态文件如css、js、文件等等记住需要在Startup.cs中的Configure方法下添加如下一句来启用静态文件:

 app.UseStaticFiles();

由于在ASP.NET Core中MVC和WebAPi请求管道合并,所以只有Controller基类,我们在控制器下建立如下方法:

   [Route("[controller]")]
    public class HomeController : Controller
    {
        [HttpGet("Index")]
        public IActionResult Index()
        {
            return View();
        }

        [HttpPost("PostPerson")]
        public IActionResult PostPerson(Person p)
        {
            return Json(p);
        }
    }

此时加载页面如下:

接下来我们分别演示以表单形式和JSON形式来发出POST请求。

        $(function () {
            $("#btn").on("click", function () {
                var dataform = $('form').serialize();
                console.log(dataform);
                $.ajax({
                    url: "../Home/PostPerson",
                    contentType: "application/x-www-form-urlencoded;charset=utf-8",
                    dataType: "json",
                    type: "post",
                    data: dataform,
                    success: function (data) {
                        console.log(data);
                    }
                });
            });
        });        

接下来我们再来看看传输JSON。

        var datajson = { Name: "Jeffcky", Age: 24, Address: "湖南省" };
        $(function () {
            $("#btn").on("click", function () {
                console.log(datajson);
                $.ajax({
                    url: "../Home/PostPerson",
                    contentType: "application/json;charset=utf-8",
                    dataType: "json",
                    type: "post",
                    data: JSON.stringify(datajson),
                    success: function (data) {
                        console.log(data);
                    }
                });
            });
        });

此时就和ASP.NET MVC/WebAPi中情况就不一样,此时后台接收不到数据,从而返回null。在ASP.NET Core中为了正确绑定到JSON我们需要在Action方法中对参数显式指定[FromBody]。

        [HttpPost("PostPerson")]
        public IActionResult PostPerson([FromBody]Person p)
        {
            return Json(p);
        }

通过使用[FromBody]则能正常使用了,那么到了这里你是不是就认为我们应该总是使用[FromBody]特性呢,如果你这样想就大错特错了,我们将上述contentType修改成表单形式

 contentType: "application/x-www-form-urlencoded;charset=utf-8",

此时会得到415不支持此媒体类型,当我们使用[FromBody]特性时,也就是明确告诉.NET Core要使用请求中的contentType头来决定输入参数对于模型绑定。默认情况下在我们注入MVC服务时被配置使用的时JsonInputFormatter,当然我们可以配置其他formatter比如xml,所以在这里我们将绑定到请求的Body中,但是输入参数不对,因为其格式为Name=Jeffcky&Age=24&Address=湖南省,所以会出现不支持该媒体类型,在这里我们要么去除[FromBody]特性,要么添加[FromForm]特性。如果我们既需要绑定表单也需要绑定JSON该如何是好呢?我们只能写两个方法。如下:

        [HttpPost("PostFormPerson")]
        public IActionResult PostFormPerson(Person p)
        {
            return Json(p);
        }

        [HttpPost("PostJsonPerson")]
        public IActionResult PostJsonPerson([FromBody] Person p)
        {
            return Json(p);
        }

现在看来想想是不是没有之前ASP.NET MVC/WebAPi灵活和方便呢,微软是不是闲的蛋疼啊,所以我们是不是觉得虽然是两个方法我们将其路由定义成相同的,那么当我们在调用时让其自己去匹配不就得了,于是乎就有了如下的情况。

        [HttpPost("PostPerson")]
        public IActionResult PostFormPerson(Person p)
        {
            return Json(p);
        }

        [HttpPost("PostPerson")]
        public IActionResult PostJsonPerson([FromBody] Person p)
        {
            return Json(p);
        }

此时还没到控制器下的路由方法就已经发生500错误了,如下:

好了看到这里我们本节的内容就已经接近尾声了,是不是觉得微软闲的没事干了,明明一个方法就可以ok的事,非得要我们写两个方法,原因到底是什么呢,据了解社区是为了安全考虑,主要原因是为了防止CSRF(Cross-Site Rquest Forgery)究竟内部到底是怎么防止CSRF的呢,不得而知,难道像之前MVC中的For...那样么,不得而知。

总结

本节我们比较详细的讨论了ASP.NET Core MVC/WebAPi中的模型绑定,如果在前台是JSON绑定,在ASP.NET Core MVC/WebAPi必须要用[FromBody]明确标识,否则你懂的。

时间: 2024-10-29 17:44:35

ASP.NET Core MVC/WebAPi 模型绑定探索的相关文章

ASP.NET Core MVC/WebAPi如何构建路由?

前言 本节我们来讲讲ASP.NET Core中的路由,在讲路由之前我们首先回顾下之前所讲在ASP.NET Core中的模型绑定这其中有一个问题是我在项目当中遇见的,我们下面首先来看看这个问题. 回顾ASP.NET Core模型绑定 我们有这样一个场景:修改个人资料中的各个属性,此时每个属性的值的类型肯定是不一样的,所以我们将值定义为object,如下model. public class BlogViewModel { public string prop { get; set; } publi

详解ASP.NET Core MVC四种枚举绑定方式

前言 本节我们来讲讲在ASP.NET Core MVC又为我们提供了哪些方便,之前我们探讨过在ASP.NET MVC中下拉框绑定方式,这节我们来再来重点看看枚举绑定的方式,充分实现你所能想到的场景,满满的干货,你值得拥有. 探讨枚举绑定方式 我们首先给出要绑定的枚举类. public enum Language { JavaScript, Java, C, Python, SQL, Oracle } 枚举绑定方式一(@Html.DropDownList) 接下来我们废话少说直接进入主题. 复制代

ASP.NET MVC数组模型绑定详解_实用技巧

在ASP.NET MVC中使用Razor语法可以在视图中方便地展示数组,如果要进行数组模型绑定,会遇到索引断裂问题,如下示例: <input type="text" name="[0].Name" /> <input type="text" name="[1].Name" /> <input type="text" name="[2].Name" />

【翻译】在Visual Studio中使用Asp.Net Core MVC创建你的第一个Web API应用(一)

HTTP is not just for serving up web pages. It's also a powerful platform for building APIs that expose services and data. HTTP is simple, flexible, and ubiquitous. Almost any platform that you can think of has an HTTP library, so HTTP services can re

ASP.NET Core MVC之Serilog日志处理,你了解多少?

前言 本节我们来看看ASP.NET Core MVC中比较常用的功能,对于导入和导出目前仍在探索中,项目需要自定义列合并,所以事先探索了如何在ASP.NET Core MVC进行导入.导出,更高级的内容还需等我学习再给出. EntityFramework Core 在学习ASP.NET Core MVC之前我们来看看在EF Core中如何更新对象指定属性,这个问题之前我们已经探讨过,但是还是存在一点问题,请往下看. public void Update(T entity, params Expr

解决ASP.NET Core Mvc文件上传限制问题实例_实用技巧

一.简介 在ASP.NET Core MVC中,文件上传的最大上传文件默认为20MB,如果我们想上传一些比较大的文件,就不知道怎么去设置了,没有了Web.Config我们应该如何下手呢? 二.设置上传文件大小 1.应用程序级别设置 我们需要在 ConfigureServices方法中添加如下代码,设置文件上传的大小限制为60 MB. public void ConfigureServices(IServiceCollection services) { servicesConfigure<For

ASP.NET Core MVC上传、导入、导出功能详解

前言 本君已成夜猫子,本节我们来讲讲ASP.NET Core MVC中的上传,这两天在研究批量导入功能,本节顺便简单搞搞导入.导出,等博主弄妥当了再来和大家一并分享. .NET Core MVC上传 首先我们来看看官网的上传的例子,再然后进行拓展训练,官网的表单是这样的. <form method="post" enctype="multipart/form-data" asp-controller="UploadFiles" asp-ac

ASP.NET Core MVC 配置全局路由前缀_实用技巧

ASP.NET Core MVC 配置全局路由前缀 前言 大家好,今天给大家介绍一个 ASP.NET Core MVC 的一个新特性,给全局路由添加统一前缀.严格说其实不算是新特性,不过是Core MVC特有的. 应用背景 不知道大家在做 Web Api 应用程序的时候,有没有遇到过这种场景,就是所有的接口都是以 /api 开头的,也就是我们的api 接口请求地址是像这样的: http://www.example.com/api/order/333 或者是这样的需求 http://www.exa

ASP.NET Core MVC压缩样式、脚本及总是复制文件到输出目录

前言 在.NET Core之前对于压缩样式文件和脚本我们可能需要借助第三方工具来进行压缩,但在ASP.NET MVC Core中则无需借助第三方工具来完成,本节我们来看看ASP.NET Core MVC为我们提供了哪些方便. 自动压缩样式和脚本 当我们在测试环境中肯定不需要压缩脚本的,如果一旦压缩脚本的话,若在控制台出现错误不利于我们调试,但是在生产环境中我们通过压缩脚本或者样式一来可以减少传输流量,二来可以加速页面加载时间,换句话说,此时我们需要测试环境和生产环境对应的原生版本和压缩版本,那么