netcore webapi接口同时支持多种编码请求方式
功能:netcore webapi接口支持GET和POST请求,同时支持application/x-www-form-urlencoded和application/json两种请求方式。
背景:在以前.NET Framework写MVC5的时候,Action的参数前端传递的时候默认是可以自适应的,即:以queryString、表单或者json传递都能够被正确接收,而到了asp.net core中,action接收参数默认只有queryString,显式声明了FromForm或FromBody之后也只能被表单或json接受,即使是同时打上FromForm和FromBody,也只有FromForm生效,FromBody不会起作用。
一、基本介绍
简介:在http协议中规定了GET、HEAD、POST、PUT、DELETE、CONNECT 等请求方式,其中比较常用的就是POST和GET,其中POST用来向服务器提交数据,POST只规定了提交的数据必须放在请求的主体中,但是并没有规定传输数据的编码方式。简单介绍一下主流的两种编码方式,也是实际开发中使用最多的两种方式:
1.application/x-www-form-urlencoded
最常见的请求方式,特别是自己在测试后端接口时,经常在前端url中直接以键值对的形式写入参数的值。但是该方式默认采用URLENCODE编码会导致消息包大,form表单默认以该方式提交,请求一般是如下的方式:
Content-Type: application/x-www-form-urlencoded
title=test&text=test
2.application/json
application/json 这个 Content-Type 也是非常常见的,越来越多的人使用该方式传递,该方式传递的是序列化后的字符串,因为采用的是JSON格式的数据,因此支持更多复杂的类型。请求体一般如下:
{
"id":123,
"name":"admin",
}
二、服务端接口请求和接收方法分析
方式1:
请求地址:/?id=123&name=bob
服务端方法:void Action(int id, string name)
这里的所有参数都是简单类型,都来自url
方式2:
请求地址:/?id=123&name=bob
服务端方法:
(1)void Action([FromUri] int id, [FromUri] string name)
这个和方式1一样
(2) void Action([FromBody] string name);
这里[FormBody]特性显示标明读取整个body为一个字符串作为参数
方式3:
请求地址: /?id=123
类定义:
public class Customer { // 定义的一个复杂对象类型
public string Name { get; set; }
public int Age { get; set; }
}
(1)服务端方法: void Action(int id, Customer c)
参数id从query string中读取,参数c是一个复杂Customer对象类戏,通过formatter从body中读取
(2)服务端方法: void Action(Customer c1, Customer c2)
这里会出错!多个参数都是复杂类型,都试图从body中读取,而body只能被读取一次
(3)服务端方法: void Action([FromUri] Customer c1, Customer c2)
这种可以!不同于上面的action,复杂类型c1将从url中读取,c2将从body中读取
方式4:
ModelBinder方式:
void Action([ModelBinder(MyCustomBinder)] SomeType c)
//标示使用特定的model binder来解析参数
[ModelBinder(MyCustomBinder)]
public class
SomeType { } //
通过给特定类型SomeType声明标注[ModelBidner(MyCustomBinder)]特性使得所有SomeType类型参数应用此规则
void Action(SomeType c) // 由于c的类型为SomeType,因而应用SomeType上的特性决定其采用model binding
/*网上有篇文章:https://masuit.org/1889?t=v3oivzkjzjeo,里面介绍了为ASP.NET Core实现一个自适应ModelBinder,让Action自适应前端参数传递
大体介绍如下:文章的目的是替换掉FromBody的默认行为,写一个自定义的ModelBinder。
最终实现的方法:
安装这个nuget包,
并加入
builder.Services.AddControllers(options => options.ModelBinderProviders.InsertBodyOrDefaultBinding());
这行代码即可*/
通过这个方法解决了queryString、表单或者json传递都能够被正确接收,但是这里如果通过浏览器GET请求访问就会出错了,因此笔者找到了另外一种解决方式。
方式5:
直接上代码:
定义接收参数实体:
[ModelBinder(BinderType = typeof(SampleClassModelBinder))]
public class ParamEntity
{
public string id { get; set; }
public string name{ get; set; }
}
public class SampleClassModelBinder : IModelBinder
{
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
try
{
var stream = bindingContext.HttpContext.Request.Body;
string body;
using (var reader = new StreamReader(stream))
{
body = await reader.ReadToEndAsync();
}
var someClass = JsonConvert.DeserializeObject<TimeCountParamEntity>(body);
bindingContext.Result = ModelBindingResult.Success(someClass);
}
catch (Exception ex)
{ }
}
}
方法:Action([FromBody] ParamEntity data)
通过这个方法解决了queryString、表单或者json传递都能够被正确接收,并可以通过浏览器GET请求访问。