功能: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請求訪問。