主要目标
在Asp.net Core控制器中,通过自定义格式化程序来映射自定义处理控制器中的“未知”内容。
简单案例
为了演示这个问题,我们用VS2017创建一个默认的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"}通过后台调试和fiddler抓包,我们可以看到请求输入和返回。
后台调试,查看请求输入结果
fiddler查看请求header
fiddler查看返回结果
注意!!
别忘了[FromBody],有时候会忘的。
后台action接收类型为string的时候,请求body只能是字符串,不能传json对象。我演示这个例子时,被这点坑了。如果接收对象是一个类的时候,才可以传json对象。
没有JSON
虽然传输json数据是最常用的,但有时候我们需要支持普通的文本或者二进制信息。我们将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。默认情况下,原始数据不能直接映射到控制器参数。这是个小坑,不知你踩到过没有?仔细想想,这是有道理的。MVC具有特定内容类型的映射,如果您传递的数据不符合这些内容类型,则无法转换数据,因此它假定没有匹配的端点可以处理请求。
那么怎么支持原始的请求映射呢?
支持原始正文请求
不幸的是,ASP.NET Core不允许您仅通过方法参数以任何有意义的方式捕获“原始”数据。无论如何,您需要对其进行一些自定义处理Request.Body以获取原始数据,然后对其进行反序列化。
您可以捕获原始数据Request.Body并从中直接读取原始缓冲区。
最简单,最不易侵入,但不那么明显的方法是使用一个方法接受没有参数的 POST或PUT数据,然后从Request.Body以下位置读取原始数据:
读取字符串缓冲区
[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
要读取二进制数据,你可以使用以下内容:
读取byte缓冲区
[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
}
}查看执行结果
接收文本内容
接收二进制数据
HttpRequest静态扩展
如果你为了方便,写了很多HttpRequest的扩展,接收参数时,可以看起来更简洁一些。
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();
}自动转换文本和二进制值
上面虽然解决了原始参数转换问题,但不够友好。如果你打算像原生MVC那样自动映射参数的话,你需要做一些自定义格式化适配。
创建一个Asp.net MVC InputFormatter
ASP.NET Core使用一种干净且更通用的方式来处理内容的自定义格式InputFormatter。输入格式化程序挂钩到请求处理管道,让您查看特定类型的内容以确定是否要处理它。然后,您可以阅读请求正文并对入站内容执行自己的反序列化。
InputFormatter有几个要求
您需要使用[FromBody]去获取
您必须能够查看请求并确定是否以及如何处理内容。
在这个例子中,对于“原始内容”,我想查看具有以下类型的请求:
text/plain(文本)
appliaction/octet-stream(byte[])
没有内容类型(string)
要创建格式化程序,你可以实现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();
}
}
作者:范存威
链接:https://www.jianshu.com/p/4a96d73428dc
共同学习,写下你的评论
评论加载中...
作者其他优质文章