.net core Web API引數繫結規則

古道發表於2020-07-03

引數推理繫結

先從一個問題說起,微信小程式按照WebAPI定義的引數傳遞,Get請求伺服器端可以正常接收到引數,但是Post請求取不到。
Web API程式碼(.netcore 3.1)如下:

[HttpGet("Login")]
public LoginResult Login(string code)
{
  ...
}
[HttpPost("PostAvatar")]
public BaseResult<string> PostAvatar(int id,string imgUrl)
{
...
}

客戶端程式碼(微信小程式js)如下:

    wx.request({
      url: '/api/Login',
      data:{code:'xxx'},
      method:'GET',
      success:function(res){}
    })
    wx.request({
      url: '/api/PostAvatar',
      data: { id: 1,imgUrl:'xxx' },
      method: 'POST',
      success: function (res) { }
    })

後來發現如果把引數放在一個實體裡就可以接收到,像這樣

[HttpPost("PostAvatar")]
public BaseResult<string> PostAvatar(Avatar model)
{
...
}
...
public class Avatar 
{
   public int Id { get; set; }
   public string imgUrl{ get; set; }
}

於是找到官方文件對於繫結源引數推理的解釋:

WEB API存在以下繫結源特性:
[FromBody] 請求正文
[FromForm] 請求正文中的表單資料
[FromHeader] 請求標頭
[FromQuery] 請求查詢字串引數
[FromRoute] 當前請求中的路由資料
[FromServices] 作為操作引數插入的請求服務

Web API 還有一套預設的推理規則,意思就是上面的這些特性可以加在引數的前面用來強制的指定這個引數是用那種方式獲取,但是如果不顯式的宣告,它會按照這套規則預設匹配。規則如下:

繫結源推理規則的行為如下:
[FromBody] 針對複雜型別引數進行推斷。 [FromBody] 不適用於具有特殊含義的任何複雜的內建型別,如 IFormCollection 和 CancellationToken。 繫結源推理程式碼將忽略這些特殊型別。
[FromForm] 針對 IFormFile 和 IFormFileCollection 型別的操作引數進行推斷。 該特性不針對任何簡單型別或使用者定義型別進行推斷。
[FromRoute] 針對與路由模板中的引數相匹配的任何操作引數名稱進行推斷。 當多個路由與一個操作引數匹配時,任何路由值都視為 [FromRoute]。
[FromQuery] 針對任何其他操作引數進行推斷。

預設按照從上到下的順序去匹配,最後才是FromQuery,所以最開始的問題就解釋通了,因為簡單型別的引數如果不顯式指定型別就是從Query裡取,而我們的用的post請求方式,引數都在body裡所以伺服器接收不到。

簡單型別、複雜型別混合

知道了上面的推理規則,所以下面的這個例子裡,wechat引數因為是簡單型別所以不會跟其他的引數在一起,必須放在URL裡。

[HttpPost("PostAvatar")]
public BaseResult<string> PostAvatar(Avatar model,int wechat)
{
...
}
...
public class Avatar 
{
   public int id { get; set; }
   public string imgUrl{ get; set; }
}

js的請求必須是這樣才行

    wx.request({
      url: '/api/PostAvatar?wechat=1',//增加在這
      data:{
        id:1,
        imgUrl:'',
        //wechat:1 放在data裡一樣取不到
      },
      method:"POST",
      success:function(res){
      ...
      }
    })

空key問題

問題還沒完,如果想把所有POST請求使用統一的格式,單個引數也想放在body裡請求,是不是按照上面的繫結規則顯式指定為FromBody就可以呢?

public IActionResult Post([FromBody] string name) { ... }

答案是:依然取不到,因為js傳送的body是這樣的

{"name":"xxxx"}

而webapi期望的body裡只有xxxx。

總結

Web API 引數的繫結如果不是顯式的指定會按照一定預設規則識別引數的來源,GET請求比較簡單從QueryString中取值,POST請求會因為引數的型別有所不同。

[FromBody][FromForm]還是有些差別的,涉及到了ContentType後續可能還會針對HTTP請求的一些細節做些說明。

文章中有錯誤的還請留言交流!!!

參考文章:

相關文章