内容简介:前篇文章實踐以 NSwag 取代 Swashbuckle 為 ASP.NET WebAPI 產生 Swagger 文件及 Swagger UI 線上測試介面的第一步。接著面對之前 Swashbuckle 遇過的老問題 -首先,為讓 ApiController 比照 ASP.NET MVC 同時支援 Query String 和 Body (application/x-www-form-urlencoded) 取值,一樣需安裝 WebApiContrib NuGet 套件,並在 Controller 加註 [
前篇文章實踐以 NSwag 取代 Swashbuckle 為 ASP.NET WebAPI 產生 Swagger 文件及 Swagger UI 線上測試介面的第一步。接著面對之前 Swashbuckle 遇過的老問題 - 如何改以 POST Body 方式傳遞參數 ,以避免 Query String 參數外露衍生風險,為什麼要讓 ASP.NET WebAPI 2 改用 POST Body 的原理細節請參考前文,本文將聚焦如何改用 NSwag 實現。
首先,為讓 ApiController 比照 ASP.NET MVC 同時支援 Query String 和 Body (application/x-www-form-urlencoded) 取值,一樣需安裝 WebApiContrib NuGet 套件,並在 Controller 加註 [MvcStyleBinding]。
依上回修改 Swashbuckle 的經驗,要將參數定義以 application/x-www-form-urlencoded 傳送,需將 Swagger.json Operation Consumes 修改為 application/x-www-form-urlencoded 並將參數 SwaggerParameterKind 改為 formData。這方面一樣是 ASP.NET Core 支援較完整,直接在 Action 加上 [Consumes("application/x-www-form-urlencoded")],在參數加上 ASP.NET Core Attribute [FromForm] 就可搞定。若是 ASP.NET WebAPI 2,並沒有這些 Attribute 並不存在,所幸查過原始碼發現 NSwag 產生文件時是以 Attribute 名稱識別配合 dynamic 控制 Consumes 及 SwaggerParameterKind,並不依賴強型別物件,這給了一個很簡單的切入點,想辦法自己做一個同名 Attribute 型別,一樣可以產生效果。
所以我在 Models 目錄下新增兩個自訂 Attribute。ConsumesAttribute.cs
using System; namespace Microsoft.AspNetCore.Mvc { [System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class ConsumesAttribute : Attribute { public string[] ContentTypes { get; set; } public ConsumesAttribute(params string[] contentTypes) { ContentTypes = contentTypes; } } }
FromFormAttribute.cs
using System; namespace Microsoft.AspNetCore.Mvc { [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false, Inherited = true)] public class FromFormAttribute : Attribute { public string Name { get; set; } } }
接著要改寫 CodecController.cs,主要修改點為:
- CodecController 加上 [MvcStyleBinding]
- EncryptString() 欲改為 POST Body 傳值,故要加上 [Consumes("application/x-www-form-urlencoded")]
- encKey, rawText 兩個參數加上 [FromForm]
using System.Collections.Generic; using System.Web.Http; using Microsoft.AspNetCore.Mvc; using WebApiContrib.ModelBinders; using WebApiDemo.Models; namespace WebApiDemo.Controllers { /// <summary> /// 加解密功能 /// </summary> [MvcStyleBinding] public class CodecController : ApiController { /// <summary> /// 加密字串 /// </summary> /// <param name="encKey">加密金鑰</param> /// <param name="rawText">明文字串</param> /// <returns>加密字串</returns> [HttpPost] [Consumes("application/x-www-form-urlencoded")] public byte[] EncryptString([FromForm]string encKey, [FromForm]string rawText) { return CodecModule.EncrytString(encKey, rawText); } /// <summary> /// 解密請求參數物件 /// </summary> public class DecryptParameter { /// <summary> /// 加密金鑰 /// </summary> public string EncKey { get; set; } /// <summary> /// 加密字串陣列 /// </summary> public List<byte[]> EncData { get; set; } } /// <summary> /// 批次解密 /// </summary> /// <param name="decData">解密請求參數(加解密金鑰與加密字串陣列)</param> /// <returns>解密字串陣列</returns> [HttpPost] public List<string> BatchDecryptData([FromBody]DecryptParameter decData) { return CodecModule.DecryptData(decData.EncKey, decData.EncData); } } }
調整後,由 Swagger UI 可驗證我們已成功將 EncryptString() 改造為 application/x-www-form-urlencoded 傳送,而 encKey 與 rawText 參數屬性也已調為 formData,由下方 Curl 範例也可確認參數是以 POST Body UrlEncoded 方式傳送。
不過,Controller 套用 [MvcStyleBinding] 有後遺症,原本 BatchDecryptData() 使用 [FromBody] 讀取 DecryptParameter 型別參數,在套用 MvcStyleBinding 後失效,出現以下錯誤:
深入調查發現問題出在 MvcStyleBinding 背後是靠 MvcActionBinding 處理參數繫結,其邏輯未考慮 [FromBody],一律將 POST 內容解析成 FormDataCollection 型別。參考 Stackoverflow 討論 ,我也決定從修改 MvcActionBinding 邏輯下手,加一段客製邏輯,當參數被標註 [FromBody] 時,改將 POST 內容反序列化為該參數型別:
public override Task ExecuteBindingAsync(HttpActionContext actionContext, CancellationToken cancellationToken) { HttpRequestMessage request = actionContext.ControllerContext.Request; HttpContent content = request.Content; if (content != null) { HttpParameterDescriptor fromBodyParam = null; //偵測是否有任何參數有[FromBody] if ((fromBodyParam = actionContext.ActionDescriptor .GetParameters().SingleOrDefault(o => o.GetCustomAttributes<FromBodyAttribute>().Any())) != null) { //若有,將Content內容JSON反列化為參數型別 var json = content.ReadAsStringAsync().Result; var value = JsonConvert.DeserializeObject(json, fromBodyParam.ParameterType); var vp = new NameValuePairsValueProvider(new Dictionary<string, object>() { [fromBodyParam.ParameterName] = value }, CultureInfo.CurrentCulture); request.Properties.Add(Key, vp); } else { FormDataCollection fd = content.ReadAsAsync<FormDataCollection>().Result; if (fd != null) { IValueProvider vp = new NameValuePairsValueProvider(fd, CultureInfo.InvariantCulture); request.Properties.Add(Key, vp); } } } return base.ExecuteBindingAsync(actionContext, cancellationToken); }
修改後,[MvcStyleBinding] 與 [FromBody] 就能並存了。
Tips of how to setup ASP.NET WebAPI2 with NSwag to accept appliation/x-www-form-urlencoded content type parameters from post body.
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- RecyclerView使用指南(一)—— 基本使用
- 如何使用Meteorjs使用URL参数
- 使用 defer 还是不使用 defer?
- 使用 Typescript 加强 Vuex 使用体验
- [译] 何时使用 Rust?何时使用 Go?
- UDP协议的正确使用场合(谨慎使用)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
随机密码生成器
多种字符组合密码
HTML 编码/解码
HTML 编码/解码