第十八节:Asp.Net Core WebApi基础总结和请求方式

一. 基础总结

1.Restful服务改造

  Core下的WebApi默认也是Restful格式服务,即通过请求方式(Get,post,put,delete)来区分请求哪个方法,请求的URL中不需要写方法名。 但是我们不喜欢这种方式,所以我们将默认的路由规则 [Route("api/[controller]")] 改为: [Route("api/[controller]/[action]")] 或  [Route("api/[area]/[controller]/[action]")]

2.基本格式

继承 ControllerBase 类,需要加特性[ApiController].

(1) 特性[ApiController]的作用:

  a.特性路由要求,和[Route]成对出现,有了它,通过 UseMvc 定义的传统路由或通过 Startup.Configure 中的 UseMvcWithDefaultRoute 访问操作均无效。

  b.模型验证错误自动触发 HTTP 400 响应

  c.绑定源参数推理(如果没有 [ApiController] 属性,同时也没有 [FromQuery][FromBody]等 的绑定源属性,ASP.NET Core 运行时会尝试使用复杂对象模型绑定器。)

  d.Multipart/form-data 请求推理

  f.错误状态代码的问题详细信息

(2) 特性[ApiController]的作用位置:

  a.作用于controller

  b.作用于程序集,服务于整个项目。在Startup类上的namespace上添加:[assembly: ApiController]

注:它不能作用action上。

PS:MVC中项目中控制器类继承的Controller类,Api项目中的控制器类继承ControllerBase类,而mvc的controller类自身是继承controllerBase,实现了IActionFilter, IFilterMetadata, IAsyncActionFilter, IDisposable接口。

3.路由规则

  详见  第二十节:Asp.Net Core WebApi和MVC 路由规则比较

4.常见的特性

  [Route] 指定控制器或操作的 URL 模式。

  [Bind] 指定要包含的前缀和属性,以进行模型绑定。

  [HttpGet] [HttpPost]标识支持 HTTP GET 等操作谓词的操作。

  [Consumes] 指定某个操作接受的数据类型。

  [Produces] 指定某个操作返回的数据类型。

5.绑定源参数推理

  [FromBody] 实体JSON格式的获取,和不加效果相同

  [FromForm] 请求正文中的表单数据

  [FromQuery] 请求查询字符串参数,Get请求的时候,用实体接受需要加

  [FromHeader] 请求标头

  [FromRoute] 当前请求中的路由数据

  [FromServices] 作为操作参数插入的请求服务,即将对象注入到方法中

6.允许跨域

  同Core MVC相同,详见:https://www.cnblogs.com/yaopengfei/p/11191938.html

7.过滤器

  同Core MVC相同,详见:https://www.cnblogs.com/yaopengfei/p/11232921.html, 但webapi中页面相关的过滤器不适用

 

二. Get和Post请求

1. Get请求

  前端JS发送Get请求的时候,后台可以分参数接收,也可以用实体接收,但是需要在实体的前面加上[FromQuery]。

 注:不能用dynamic接收,不管怎么处理都报错。

案例测试:

 (1).分参数接收,可以正常访问。

 (2).用实体类接收,前面加[FromQuery],可以正常访问(否则报415)。

 (3).用dynamic接收,前面什么不加,报错415,前面加[FromQuery],也报错,报500。

服务器端代码:

 1     [Route("api/[controller]/[action]")]
 2     [ApiController]
 3     public class FirstController : ControllerBase
 4     {
 5         /******************************************下面是测试Get请求的相关方法***************************************************/
 6 
 7         [HttpGet]
 8         public string GetInfor1(string userName, string pwd)
 9         {
10             return $"{userName}+{pwd}";
11         }
12 
13         [HttpGet]
14         public string GetInfor2([FromQuery]UserInfor model)
15         {
16             return $"{model.userName}+{model.pwd}";
17         }
18         [HttpGet]
19         //加上[FromQuery]也报错
20         public string GetInfor3([FromQuery]dynamic model)
21         {
22             return $"{model.userName}+{model.pwd}";
23         }
24  
25     }

前端代码:

 1             //一.下面是Get请求的测试
 2             //1. 分参数接收,可以正常访问
 3             $("#getBtn1").click(function () {
 4                 $.ajax({ url: "https://localhost:44387/api/First/GetInfor1", type: "get", data: { userName: "admin", pwd: "123456" }, success: function (data) { console.log(data); } });
 5             });
 6             //2. 用实体类接收,前面加[FromQuery],可以正常访问(否则报415)
 7             $("#getBtn2").click(function () {
 8                 $.ajax({ url: "https://localhost:44387/api/First/GetInfor2", type: "get", data: { userName: "admin", pwd: "123456" }, success: function (data) { console.log(data); } });
 9             });
10             //3. 用dynamic接收,前面什么不加,报错415,前面加[FromQuery],也报错,报500
11             $("#getBtn3").click(function () {
12                 $.ajax({ url: "https://localhost:44387/api/First/GetInfor3", type: "get", data: { userName: "admin", pwd: "123456" }, success: function (data) { console.log(data); } });

2. Post请求

  前端JS发送Post请求的时候,可能是表单提交,也可能是JOSN格式提交,所以下面要分情况讨论:默认情况下在我们注入MVC服务时被配置使用的时JsonInputFormatter,即实体默认接受JSON格式的数据,我们如果想让它接受表单数据,需要在实体前面加[FromForm].

 (1) 接收JSON格式:实体前面什么也不加 或者 实体前面加[FromBody]

 (2) 接收表单格式: 实体前面加[FromForm]

注:不能分参数接收!! 用dynamic接收的时候,只能处理前端JOSN格式的数据,加[FromBody]或者不加都行, 不能处理前端表单格式数据!!

案例测试:

 (1). 一个参数的情况,后台分参数接收,拿不到值

 (2).表单提交,实体前面什么也不加 或者 实体前面加[FromForm],Login1 和 Login2 均报415,Login3可以正常访问

 (3).JSON提交,实体前面加[FromBody],Login1,Login2正常访问,Login3能访问通,但是后台拿不到值,都为空

 (4).JOSN格式,后台用dynamic能接收,加[FromBody]或者不加都可以接收

 (5).表单格式,后台用dynamic不能接收,加[FromForm]或者不加都报500,报错。

服务器端代码:

 1     [Route("api/[controller]/[action]")]
 2     [ApiController]
 3     public class FirstController : ControllerBase
 4     {
 5      
 6         /******************************************下面是测试Post请求的相关方法***************************************************/
 7 
 8         [HttpPost]
 9         public string Login0(string userName)
10         {
11             return $"{userName}";
12         }
13 
14         [HttpPost]
15         public string Login1(UserInfor model)
16         {
17             return $"{model.userName}+{model.pwd}";
18         }
19 
20         [HttpPost]
21         public string Login2([FromBody]UserInfor model)
22         {
23             return $"{model.userName}+{model.pwd}";
24         }
25 
26         [HttpPost]
27         public string Login3([FromForm]UserInfor model)
28         {
29             return $"{model.userName}+{model.pwd}";
30         }
31         [HttpPost]
32         public string Login4([FromBody]dynamic model)
33         {
34             return $"{model.userName}+{model.pwd}";
35         }
36 
37         [HttpPost]
38         public string Login5([FromForm]dynamic model)
39         {
40             return $"{model.userName}+{model.pwd}";
41         }
42     }

前端代码:

 1              //二.下面是Post请求的测试
 2             //(Post请求默认情况下为:ContentType = "application/x-www-form-urlencoded"提交表单的形式,如果要发送JOSN格式,需要加上参数contentType: 'application/json')
 3             //PS: { userName: "admin", pwd: "123456" } 这就是一个JSON对象,也可以叫实体
 4 
 5             //1. 一个参数的情况,后台分参数接收,均拿不到值
 6             $("#postBtn0").click(function () {
 7                 //1.1 正常拼接,可以访问通,但是拿不到userName的值
 8                 //$.ajax({ url: "https://localhost:44387/api/First/Login0", type: "Post", data: { userName: "admin" }, success: function (data) { console.log(data);  } });
 9                 //1.2 没有键,只有值,可以访问通,但是拿不到userName的值 (这里同.Net 平台下的WebApi不同)
10                 $.ajax({ url: "https://localhost:44387/api/First/Login0", type: "Post", data: { "": "admin" }, success: function (data) { console.log(data); } });
11             });
12 
13             //2. 表单提交,Login1 和 Login2 均报415,Login3可以正常访问
14             $("#postBtn1").click(function () {
15                 $.ajax({ url: "https://localhost:44387/api/First/Login1", type: "Post", data: { userName: "admin", pwd: "123456" }, success: function (data) { console.log("Login1:" + data); } });
16                 $.ajax({ url: "https://localhost:44387/api/First/Login2", type: "Post", data: { userName: "admin", pwd: "123456" }, success: function (data) { console.log("Login2:" + data); } });
17                 $.ajax({ url: "https://localhost:44387/api/First/Login3", type: "Post", data: { userName: "admin", pwd: "123456" }, success: function (data) { console.log("Login3:" + data); } });
18             });
19 
20             //3.JSON提交,Login1,Login2正常访问,Login3能访问通,但是后台拿不到值,都为空
21             $("#postBtn2").click(function () {
22                 //将post请求指定为contentType: 'application/json',且传递的参数格式化成Json字符串,则可以正常访问
23                 $.ajax({ url: "https://localhost:44387/api/First/Login1", type: "Post", contentType: 'application/json', data: JSON.stringify({ userName: "admin", pwd: "123456" }), success: function (data) { console.log("Login1:" + data); } });
24                 $.ajax({ url: "https://localhost:44387/api/First/Login2", type: "Post", contentType: 'application/json', data: JSON.stringify({ userName: "admin", pwd: "123456" }), success: function (data) { console.log("Login2:" + data); } });
25                 $.ajax({ url: "https://localhost:44387/api/First/Login3", type: "Post", contentType: 'application/json', data: JSON.stringify({ userName: "admin", pwd: "123456" }), success: function (data) { console.log("Login3:" + data); } });
26 
27             });
28 
29             //4.JOSN格式,后台用dynamic能接收,加[FromBody]或者不加都可以接收
30             $("#postBtn3").click(function () {
31                 //将post请求指定为contentType: 'application/json',且传递的参数格式化成Json字符串,则可以正常访问
32                 $.ajax({ url: "https://localhost:44387/api/First/Login4", type: "Post", contentType: 'application/json', data: JSON.stringify({ userName: "admin", pwd: "123456" }), success: function (data) { console.log("JSON:" + data); } });
33             });
34             //5.表单格式,后台用dynamic不能接收,加[FromForm]或者不加都报500
35             $("#postBtn4").click(function () {
36                 $.ajax({ url: "https://localhost:44387/api/First/Login5", type: "Post", data: { userName: "admin", pwd: "123456" }, success: function (data) { console.log("表单:" + data); } });
37             });

3.总结

  Get请求,可以分参数接收,也可以用实体接收,需要在实体的前面加上[FromQuery]。

  POST请求,只能用实体接收,针对js默认的表单提交方式,实体前面加[FromForm];针对js的JSON格式的提交方式,实体前面什么也不加 或者 实体前面加[FromBody]。

4. 返回值的问题(补充)

  用Content标记json类型和直接返回序列化字符串的区别。用 Content(jsonData, "application/json"); 标记,前端什么不用加,可以直接点出来的。

   /// <summary>
        /// 前端不能直接点出来
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public string GetGoodById1()
        {
            var myData = new
            {
                status = "ok",
                goods = new UserInfor()
                {
                    userName = "apple",
                    pwd = "12345"
                }
            };
            var jsonData = JsonConvert.SerializeObject(myData);
            return jsonData;
        }

        /// <summary>
        /// 前端可以直接 点出来
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public IActionResult GetGoodById2()
        {
            var myData = new
            {
                status = "ok",
                goods = new UserInfor()
                {
                    userName = "apple",
                    pwd = "12345"
                }
            };
            var jsonData = JsonConvert.SerializeObject(myData);
            return Content(jsonData, "application/json");
        }

前端调用

         $("#rBtn1").click(function () {
                $.ajax({
                    url: "https://localhost:44387/api/First/GetGoodById1", type: "get", data: { userName: "admin", pwd: "123456" },  success: function (data) {
                        console.log(data);
                        //不能直接点出来
                        console.log(data.status);
                    }
                });
                $.ajax({
                    url: "https://localhost:44387/api/First/GetGoodById1", type: "get", data: { userName: "admin", pwd: "123456" }, dataType: "json", success: function (data) {
                        console.log(data);
                        //加上dataType: "json",就能直接点出来
                        console.log(data.status);
                    }
                });
            });
            $("#rBtn2").click(function () {
                $.ajax({
                    url: "https://localhost:44387/api/First/GetGoodById2", type: "get", data: { userName: "admin", pwd: "123456" }, success: function (data) {
                        console.log(data);
                        //能直接点出来
                        console.log(data.status);
                    }
                });
            });

5. 特殊情况补充 (null问题)

 在Core WebApi(或Core Mvc)中,当前端发送的字段为空字符串的时候,无论get还是post请求,后端解析到的都是null,显然我们需要的不是null,而是 空字符串 “”  

解决方案:

 接口用实体接收,且对应字段上加上特性  [DisplayFormat(ConvertEmptyStringToNull = false)]

$.get("/api/Test1/GetGoodNum2", { id: 111, userName: "" }, function () {

})

 接口:

[HttpGet]
public int GetGoodNum2([FromQuery] User user)
{
            return user.id;
}
public class User
{
        public int id { get; set; }

        [DisplayFormat(ConvertEmptyStringToNull = false)]
        public string userName { get; set; }

}

 

 

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鹏飞)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 声     明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
  • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 
posted @ 2019-09-20 17:32  Yaopengfei  阅读(4178)  评论(5编辑  收藏  举报