理解 ASP.NET Web API 中的 HttpParameterBinding


背景

  問題的起因是這樣的。群里面一個哥們兒發現在使用 ASP.NET WebAPI 時,不能在同一個方法簽名中使用多次 FromBodyAttribute 這個 Attribute 。正好我也在用 WebAPI,不過我還沒有這種需求。所以就打算研究一下。

 

異常信息

  當使用多個 FromBodyAttribute 時,會收到下面的異常信息:

{
  "Message": "An error has occurred.",
  "ExceptionMessage": "Can't bind multiple parameters ('a' and 'b') to the request's content.",
  "ExceptionType": "System.InvalidOperationException",
  "StackTrace": "   在 System.Web.Http.Controllers.HttpActionBinding.ExecuteBindingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)\r\n   在 System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()\r\n--- 引發異常的上一位置中堆棧跟蹤的末尾 ---\r\n   在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   在 System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"
}

意思就是不能參數 a 和 b 綁定到當前請求。

 

源代碼追蹤

  通過異常信息可以發現是在 HttpActionBinding 這個類里面拋出了這個異常。立馬去源代碼中找這個類。下面是源代碼:

通過 1,2,3 這三個點,發現是參數綁定類里面的驗證失敗。接着看了 HttpParameterBinding 是個抽象類。沒有看到太多可用信息。去 FromBodyAttribute 里面看看有沒有什么可用信息。

發現這里需要提供一個 HttpParameterBinding 的實例。從箭頭標記的方法根進去接着看。

  這里可以看到返回了一個 FormatterParameterBinding 的實例。並且需要三個參數。

  • parameter:從命名可以看出來是參數描述信息;
  • formatters:這個應該比較熟悉了,是格式化器;
  • bodyModelValidator:這個是對應參數的驗證器;

  以上三個參數的意義基本就是看命名+大概閱讀源代碼得到的(所以寫代碼,命名很重要)。接着進入 FormatterParameterBinding 的源代碼。這個類里面的代碼也就 100 多行,邏輯就是從 HttpContent 中讀取內容並設置為當前參數的值。

  到了這兒算是理清了一點:原來在參數上打的這些 Attribute 都是從 ParameterBindingAttribute 繼承的,又通過實現 GetBinding(HttpParameterDescriptor parameter); 方法將請求的參數與方法的參數進行綁定。

  但是,在哪兒標記了不能使用多個 FromBodyAttribute 呢?既然是在 HttpActionBinding 中進行的驗證,那就順着 HttpActionBinding 往上找。通過 HttpActionBinding 的構造函數,發現只有 DefaultActionValueBinder 調用了它。接着往下看,看誰使用了這個 new 出來的實例。緊挨着就看到了 EnsureOneBodyParameter 這個方法,有點兒可疑,進去看一下。

  

  這個地方的 WillReadBody 如果為 true 並且 idxFromBody 大於 0 ,就會給 ParameterBinding 設置錯誤消息。看了一下消息內容,就是最上面的異常消息的模板。到這里應該算是找到根兒上了。

  現在來梳理一下:也就是說 HttpParameterBindingWillReadBody 如果返回 true 就不能在一個方法的簽名中使用多次,一旦使用多次,就會把這個錯誤。剛才上面看到的 FormatterParameterBinding 里面的 WillReadBody 是直接返回的 true ,而且是只讀的,並且在執行綁定時是直接讀取的 HttpContent 的內容,設置為當前參數的值了。假如要執行的 action 的方法簽名中有多個參數就綁定不成功了。

 

定制開發

  知道了這個原理,那么想在一個有多個參數的 action 中進行參數的靈活綁定,就有了辦法。分兩步走:

  1. 自定義一個 Attribute 從 ParameterBindingAttribute 繼承;
  2. 自定義一個 ParameterBinding 從 HttpParameterBinding 繼承;在 ExecuteBindingAsync 方法中綁定 action 的參數的值。並把這個自定義的類的 WillReadBody 設置為 false 。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM