首页 > 网络编程 > ASP.NET > 正文

Asp.Net Core控制器如何接收原始请求正文内容详解_实用技巧

2018-10-12 16:22:09

主要目标

在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: FiddlerHost: localhost:5000Content-Type: application/jsonContent-Length: 16 

请求body:

{"123456"}

通过后台调试和fiddler抓包,我们可以看到请求输入和返回。

后台调试,查看请求输入结果

fiddler查看请求header

fiddler查看返回结果

注意!!

别忘了[FromBody],有时候会忘的。
后台action接收类型为string的时候,请求body只能是字符串,不能传json对象。我演示这个例子时,被这点坑了。如果接收对象是一个类的时候,才可以传json对象。

没有JSON

虽然传输json数据是最常用的,但有时候我们需要支持普通的文本或者二进制信息。我们将Content-Type改为
text/plain

User-Agent: FiddlerHost: localhost:5000Content-Type:text/plainContent-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: FiddlerHost: localhost:5000Content-Type: text/plainContent-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();  } }

格式化程序用于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: FiddlerHost: localhost:5000Content-Length: 6

或者

User-Agent: FiddlerHost: localhost:5000Content-Type:text/plainContent-Length: 6

请注意,您可以使用内容类型调用相同的控制器方法application/json并传递JSON字符串,这也将起作用。在RawRequestBodyFormatter 简单地增加它支持的附加内容类型的支持。

二进制数据

  [HttpPost]  [Route("PostBinaryPlus")]  public byte[] PostBinaryPlus([FromBody] byte[] value)  {   return value;  }

请求内容如下:

User-Agent: FiddlerHost: localhost:5000Content-Length: 6Content-Type: application/octet-stream

源代码

示例代码已上传到 CsharpFanDemo (本地下载

参考链接

本文包含翻译和自己实践。主要思路和代码来源于以下链接:
Accepting Raw Request Body Content in ASP.NET Core API Controllers

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

  • 相关标签:ASP.NET
  • 本文发布HTML5中文学习网 ,转载请注明出处,感谢您!
  • 相关文章


  • 曝网友假装外国人写投诉信 ofo秒退押金并回函致歉
  • 苹果市值缩水逾2000亿美元 遭多家投行下调目标价
  • Asp.net Core与类库读取配置文件信息的方法_实用技巧
  • asp.net在Repeater嵌套的Repeater中使用复选框详解_实用技巧
  • 利用IIS调试ASP.NET网站程序的完整步骤_实用技巧
  • Asp.Net Core轻松学习系列之配置文件_实用技巧
  • ASP.NET 页生命周期概述(小结)_实用技巧
  • 详解ASP.NET Core WebApi 返回统一格式参数_实用技巧
  • 2018年网络流行语有哪些?2018年十大网络流行语盘点
  • 华为首席财务官孟晚舟被暂扣 深圳市政府要求加方立即放人!
  • 独孤九贱(4)_PHP视频教程

    江湖传言:PHP是世界上最好的编程语言。真的是这样吗?这个梗究竟是从哪来的?学会本课程,你就会明白了。 PHP中文网出品的PHP入门系统教学视频,完全从初学者的角度出发,绝不玩虚的,一切以实用、有用...

    独孤九贱(5)_ThinkPHP5视频教程

    ThinkPHP是国内最流行的中文PHP开发框架,也是您Web项目的最佳选择。《php.cn独孤九贱(5)-ThinkPHP5视频教程》课程以ThinkPHP5最新版本为例,从最基本的框架常识开始,将...

    独孤九贱(1)_HTML5视频教程

    《php.cn原创html5视频教程》课程特色:php中文网原创幽默段子系列课程,以恶搞,段子为主题风格的php视频教程!轻松的教学风格,简短的教学模式,让同学们在不知不觉中,学会了HTML知识。 ...

    ThinkPHP5实战之[教学管理系统]

    本套教程,以一个真实的学校教学管理系统为案例,手把手教会您如何在一张白纸上,从零开始,一步一步的用ThinkPHP5框架快速开发出一个商业项目。

    PHP入门视频教程之一周学会PHP

    所有计算机语言的学习都要从基础开始,《PHP入门视频教程之一周学会PHP》不仅是PHP的基础部分更主要的是PHP语言的核心技术,是学习PHP必须掌握的内容,任何PHP项目的实现都离不开这部分的内容,通...

    作者信息

    kevin

    永远在学习的路上!

    相关教程

  • javascript初级视频教程 javascript初级视频教程
  • jquery 基础视频教程 jquery 基础视频教程
  • javascript三级联动视频教程 javascript三级联动视频教程
  • 独孤九贱(3)_JavaScript视频教程 独孤九贱(3)_JavaScript视频教程
  • 独孤九贱(6)_jQuery视频教程 独孤九贱(6)_jQuery视频教程
  • 热门教程