Asp.Net Core控制器如何接收原始請求正文內(nèi)容詳解
主要目標
在Asp.net Core控制器中,通過自定義格式化程序來映射自定義處理控制器中的“未知”內(nèi)容。本文將給大家詳細介紹關(guān)于Asp.Net Core控制器接收原始請求正文內(nèi)容的相關(guān)內(nèi)容,分享出來供大家參考學習,下面話不多說了,來一起看看詳細的介紹吧
簡單案例
為了演示這個問題,我們用VS2017創(chuàng)建一個默認的Asp.net Core Web Api項目。
[Route("api/[controller]")] [ApiController] public class ValuesController : ControllerBase{ [HttpGet] public ActionResult<string> Get() { return "ok"; } [HttpPost] [Route("PostX")] public ActionResult<string> Post([FromBody] string value) { return value; } }
Json請求
我們從最常見的json輸入請求開始。
User-Agent: Fiddler Host: localhost:5000 Content-Type: application/json Content-Length: 16
請求body:
{"123456"}
通過后臺調(diào)試和fiddler抓包,我們可以看到請求輸入和返回。
后臺調(diào)試,查看請求輸入結(jié)果
fiddler查看請求header
fiddler查看返回結(jié)果
注意??!
別忘了[FromBody],有時候會忘的。
后臺action接收類型為string的時候,請求body只能是字符串,不能傳json對象。我演示這個例子時,被這點坑了。如果接收對象是一個類的時候,才可以傳json對象。
沒有JSON
雖然傳輸json數(shù)據(jù)是最常用的,但有時候我們需要支持普通的文本或者二進制信息。我們將Content-Type改為
text/plain
User-Agent: Fiddler Host: localhost:5000 Content-Type:text/plain Content-Length: 16
請求body:
{"123456"}
悲劇的事情來,報404!
不支持text/plain
事情到此就變得稍微復雜了一些,因為asp.netcore只處理它認識的類型,如json和formdata。默認情況下,原始數(shù)據(jù)不能直接映射到控制器參數(shù)。這是個小坑,不知你踩到過沒有?仔細想想,這是有道理的。MVC具有特定內(nèi)容類型的映射,如果您傳遞的數(shù)據(jù)不符合這些內(nèi)容類型,則無法轉(zhuǎn)換數(shù)據(jù),因此它假定沒有匹配的端點可以處理請求。
那么怎么支持原始的請求映射呢?
支持原始正文請求
不幸的是,ASP.NET Core不允許您僅通過方法參數(shù)以任何有意義的方式捕獲“原始”數(shù)據(jù)。無論如何,您需要對其進行一些自定義處理Request.Body以獲取原始數(shù)據(jù),然后對其進行反序列化。
您可以捕獲原始數(shù)據(jù)Request.Body并從中直接讀取原始緩沖區(qū)。
最簡單,最不易侵入,但不那么明顯的方法是使用一個方法接受沒有參數(shù)的 POST或PUT數(shù)據(jù),然后從Request.Body以下位置讀取原始數(shù)據(jù):
讀取字符串緩沖區(qū)
[HttpPost] [Route("PostText")] public async Task<string> PostText() { using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8)) { return await reader.ReadToEndAsync(); } }
這適用于一下Http和文本
User-Agent: Fiddler Host: localhost:5000 Content-Type: text/plain Content-Length: 6
要讀取二進制數(shù)據(jù),你可以使用以下內(nèi)容:
讀取byte緩沖區(qū)
[HttpPost] [Route("PostBinary")] public async Task<byte[]> PostBinary() { using (var ms = new MemoryStream(2048)) { await Request.Body.CopyToAsync(ms); return ms.ToArray(); // returns base64 encoded string JSON result } }
查看執(zhí)行結(jié)果
接收文本內(nèi)容
接收二進制數(shù)據(jù)
HttpRequest靜態(tài)擴展
如果你為了方便,寫了很多HttpRequest的擴展,接收參數(shù)時,可以看起來更簡潔一些。
public static class HttpRequestExtension { /// <summary> /// /// </summary> /// <param name="httpRequest"></param> /// <param name="encoding"></param> /// <returns></returns> public static async Task<string> GetRawBodyStringFormater(this HttpRequest httpRequest, Encoding encoding) { if (encoding == null) { encoding = Encoding.UTF8; } using (StreamReader reader = new StreamReader(httpRequest.Body, encoding)) { return await reader.ReadToEndAsync(); } } /// <summary> /// 二進制 /// </summary> /// <param name="httpRequest"></param> /// <param name="encoding"></param> /// <returns></returns> public static async Task<byte[]> GetRawBodyBinaryFormater(this HttpRequest httpRequest, Encoding encoding) { if (encoding == null) { encoding = Encoding.UTF8; } using (StreamReader reader = new StreamReader(httpRequest.Body, encoding)) { using (var ms = new MemoryStream(2048)) { await httpRequest.Body.CopyToAsync(ms); return ms.ToArray(); // returns base64 encoded string JSON result } } } }
[HttpPost] [Route("PostTextX")] public async Task<string> PostTextX() { return await Request.GetRawBodyStringAsyn(); } /// <summary> /// 接收 /// </summary> /// <returns></returns> [HttpPost] [Route("PostBinaryX")] public async Task<byte[]> PostBinaryX() { return await Request.GetRawBodyBinaryAsyn(); }
自動轉(zhuǎn)換文本和二進制值
上面雖然解決了原始參數(shù)轉(zhuǎn)換問題,但不夠友好。如果你打算像原生MVC那樣自動映射參數(shù)的話,你需要做一些自定義格式化適配。
創(chuàng)建一個Asp.net MVC InputFormatter
ASP.NET Core使用一種干凈且更通用的方式來處理內(nèi)容的自定義格式InputFormatter。輸入格式化程序掛鉤到請求處理管道,讓您查看特定類型的內(nèi)容以確定是否要處理它。然后,您可以閱讀請求正文并對入站內(nèi)容執(zhí)行自己的反序列化。
InputFormatter有幾個要求
- 您需要使用[FromBody]去獲取
- 您必須能夠查看請求并確定是否以及如何處理內(nèi)容。
在這個例子中,對于“原始內(nèi)容”,我想查看具有以下類型的請求:
- text/plain(文本)
- appliaction/octet-stream(byte[])
沒有內(nèi)容類型(string)
要創(chuàng)建格式化程序,你可以實現(xiàn)IInputFormatter或者從InputFormatter繼承。
public class RawRequestBodyFormatter : IInputFormatter { public RawRequestBodyFormatter() { } public bool CanRead(InputFormatterContext context) { if (context == null) throw new ArgumentNullException("argument is Null"); var contentType = context.HttpContext.Request.ContentType; if (string.IsNullOrEmpty(contentType) || contentType == "text/plain" || contentType == "application/octet-stream") return true; return false; } public async Task<InputFormatterResult> ReadAsync(InputFormatterContext context) { var request = context.HttpContext.Request; var contentType = context.HttpContext.Request.ContentType; if (string.IsNullOrEmpty(contentType) || contentType.ToLower() == "text/plain") { using (StreamReader reader = new StreamReader(request.Body, Encoding.UTF8)) { var content = await reader.ReadToEndAsync(); return await InputFormatterResult.SuccessAsync(content); } } if (contentType == "application/octet-stream") { using (StreamReader reader = new StreamReader(request.Body, Encoding.UTF8)) { using (var ms = new MemoryStream(2048)) { await request.Body.CopyToAsync(ms); var content = ms.ToArray(); return await InputFormatterResult.SuccessAsync(content); } } } return await InputFormatterResult.FailureAsync(); } }
格式化程序用于CanRead()檢查對內(nèi)容類型的請求以支持,然后將ReadRequestBodyAsync()內(nèi)容讀取和反序列化為應(yīng)在控制器方法的參數(shù)中返回的結(jié)果類型。
InputFormatter必須在ConfigureServices()啟動代碼中注冊MVC :
public void ConfigureServices(IServiceCollection services) { services.AddMvc(o=>o.InputFormatters.Insert(0,new RawRequestBodyFormatter())).SetCompatibilityVersion(CompatibilityVersion.Version_2_1); }
接受原始輸入
[HttpPost] [Route("PostTextPlus")] public string PostTextPlus([FromBody] string value) { return value; }
然后你就可以發(fā)送post請求,像這樣:
User-Agent: Fiddler Host: localhost:5000 Content-Length: 6
或者
User-Agent: Fiddler Host: localhost:5000 Content-Type:text/plain Content-Length: 6
請注意,您可以使用內(nèi)容類型調(diào)用相同的控制器方法application/json并傳遞JSON字符串,這也將起作用。在RawRequestBodyFormatter 簡單地增加它支持的附加內(nèi)容類型的支持。
二進制數(shù)據(jù)
[HttpPost] [Route("PostBinaryPlus")] public byte[] PostBinaryPlus([FromBody] byte[] value) { return value; }
請求內(nèi)容如下:
User-Agent: Fiddler Host: localhost:5000 Content-Length: 6 Content-Type: application/octet-stream
源代碼
示例代碼已上傳到 CsharpFanDemo (本地下載)
參考鏈接
本文包含翻譯和自己實踐。主要思路和代碼來源于以下鏈接:
Accepting Raw Request Body Content in ASP.NET Core API Controllers
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
ASP.NET?Core使用MiniProfiler分析應(yīng)用
這篇文章介紹了ASP.NET?Core使用MiniProfiler分析應(yīng)用的方法,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-02-02ASP.NET MVC 3仿Server.Transfer效果的實現(xiàn)方法
這篇文章主要介紹了ASP.NET MVC 3仿Server.Transfer效果的實現(xiàn)方法,需要的朋友可以參考下2015-10-10C#中遍歷各類數(shù)據(jù)集合的方法總結(jié)
C#中遍歷各類數(shù)據(jù)集合的方法,這里自己做下總結(jié):枚舉類型、遍歷ArrayList(Queue、Stack)、Winform窗體中的控件、HashTable哈希表等等,具體祥看下文2013-05-05在?.NET?中使用?FixedTimeEquals?應(yīng)對計時攻擊的例子
在計算機安全中,計時攻擊(Timing attack)是旁道攻擊 (Side-channel attack) 的一種,而旁道攻擊是根據(jù)計算機處理過程發(fā)出的信息進行分析,這篇文章主要介紹了在?.NET?中使用?FixedTimeEquals?應(yīng)對計時攻擊,需要的朋友可以參考下2022-06-06asp.net訪問網(wǎng)絡(luò)路徑方法(模擬用戶登錄)
這篇文章主要介紹了asp.net訪問網(wǎng)絡(luò)路徑方法,其實就是模擬用戶登錄,需要的朋友可以參考下2014-08-08Repeater控件實現(xiàn)編輯、更新、刪除等操作示例代碼
如何在Repeater控件中實現(xiàn)像GridView控件一樣的編輯、更新、刪除功能?下面有個不錯的示例,感興趣的朋友可以參考下,希望對大家有所幫助2014-01-01