在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
主要目标 在Asp.net Core控制器中,通过自定义格式化程序来映射自定义处理控制器中的“未知”内容。本文将给大家详细介绍关于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查看返回结果 注意!!
没有JSON 虽然传输json数据是最常用的,但有时候我们需要支持普通的文本或者二进制信息。我们将Content-Type改为 User-Agent: Fiddler Host: localhost:5000 Content-Type:text/plain Content-Length: 16 请求body: {"123456"} 悲剧的事情来,报404!
事情到此就变得稍微复杂了一些,因为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有几个要求
在这个例子中,对于“原始内容”,我想查看具有以下类型的请求:
要创建格式化程序,你可以实现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()检查对内容类型的请求以支持,然后将ReadRequestBodyAsync()内容读取和反序列化为应在控制器方法的参数中返回的结果类型。 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; } 然后你就可以发送post请求,像这样: User-Agent: Fiddler Host: localhost:5000 Content-Length: 6 或者 User-Agent: Fiddler Host: localhost:5000 Content-Type:text/plain Content-Length: 6 请注意,您可以使用内容类型调用相同的控制器方法application/json并传递JSON字符串,这也将起作用。在RawRequestBodyFormatter 简单地增加它支持的附加内容类型的支持。 二进制数据 [HttpPost] [Route("PostBinaryPlus")] public byte[] PostBinaryPlus([FromBody] byte[] value) { return value; } 请求内容如下: User-Agent: Fiddler Host: localhost:5000 Content-Length: 6 Content-Type: application/octet-stream 源代码 示例代码已上传到 CsharpFanDemo (本地下载) 参考链接 本文包含翻译和自己实践。主要思路和代码来源于以下链接: 总结 以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对极客世界的支持。 |
请发表评论