asp.net core 實現支援自定義 Content-Type
阿新 • • 發佈:2021-03-14
# asp.net core 實現支援自定義 Content-Type
## Intro
我們最近有一個原本是內網的服務要上公網,在公網上有一層 `Cloudflare` 作為網站的公網流量提供者,CloudFlare 會有一層防火牆攔截掉一些非法的請求,我們有一些 API 會提交一些 html 內容,經過 `Cloudflare` 的時候會被 `Cloudflare` 攔截,導致某些功能不能夠正常使用,於是就想對提交的資料進行一個編碼之後再提交,伺服器端針對需要解碼的請求進行解碼再解析,我們新加了一個 `Content-Type` 的支援,編碼後的資料使用新的 `Content-Type`,對於不編碼的資料依然可以工作,目前我們做了一個簡單的 base64 編碼,如果需要的話也可以實現複雜一些的加密、壓縮等。
## Basis
asp.net core 預設支援 JSON 請求,因為內建了針對 JSON 內容的 `Formatter`,.NET Core 2.x 使用的是 `Newtonsoft.Json` 作為預設 JSON formatter,從 .NET Core 3.0 開始引入了 `System.Text.Json` 作為預設的 JSON formatter,如果要支援 XML 需要引入針對 XML 的 formatter,相應的如果需要增加其他型別的請求實現自己的 formatter 就可以了
Formatter 分為 `InputFormatter` 和 `OutputFormatter`
- `InputFormatter` 用來解析請求 `Body` 的資料,將請求引數對映到強型別的 model,Request Body => Value
- `OutputFormatter` 用來將強型別的資料序列化成響應輸出,Value => Response Body
Formatter 需要指定支援的 `MediaType`,可以理解為請求型別,體現在請求頭上,對於 `InputFormatter` 對應的就是 `Content-Type` ,對於 `OutputFormatter` 對應的是 `Accept`,asp.net core 會根據請求資訊來選擇註冊的 formatter。
## Sample
先來看一下實現效果吧,實現效果如下:
![swagger](https://img2020.cnblogs.com/blog/489462/202103/489462-20210313221210331-129128092.png)
swagger 的支援也算比較好了,在增加了新的 `Content-Type` 支援之後在 swagger 上可以看得到,而且可以切換請求的 `Content-Type`,上圖中的 `text/base64-json` 就是我自定義的一個 `Content-Type`
預設請求:
![json-request](https://img2020.cnblogs.com/blog/489462/202103/489462-20210313221210129-1671651422.png)
對原始請求進行 base64 編碼,再請求:
![base64-json-request](https://img2020.cnblogs.com/blog/489462/202103/489462-20210313221209725-262816060.png)
## Implement
實現程式碼如下:
``` c#
public class Base64EncodedJsonInputFormatter : TextInputFormatter
{
public Base64EncodedJsonInputFormatter()
{
// 註冊支援的 Content-Type
SupportedMediaTypes.Add("text/base64-json");
SupportedEncodings.Add(Encoding.UTF8);
}
public override async Task ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
{
try
{
using var reader = context.ReaderFactory(context.HttpContext.Request.Body, encoding);
var rawContent = await reader.ReadToEndAsync();
if (string.IsNullOrEmpty(rawContent))
{
return await InputFormatterResult.NoValueAsync();
}
var bytes = Convert.FromBase64String(rawContent);
var services = context.HttpContext.RequestServices;
var modelValue = await GetModelValue(services, bytes);
return await InputFormatterResult.SuccessAsync(modelValue);
async ValueTask