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

主要目标

在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查看返回结果

注意!!

别忘了[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();
  }
 }

格式化程序用于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 (本地下载)

参考链接

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

总结

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

(0)

相关推荐

  • .NET Core Windows环境安装配置教程

    1.安装.NET Core SDK 在windows下开发.NET Core最好使用Visual Studio工具.下载地址与安装: VS2015最新版本:Visual Studio 2015 Update 3* VS环境下的.Net Core:.NET Core 1.0 for Visual Studio 对应下载的文件DotNetCore.1.0.0-VS2015Tools.Preview2.exe .Net Core安装过程(安装过程比较慢,亲们耐心等待): (安装慢,等待中--.) (O

  • win10下ASP.NET Core部署环境搭建步骤

    随着ASP.NET Core 1.0 rtm的发布,网上有许多相关.net core 相关文章,今刚好有时间也在win10环境上搭建下 ASP.NET Core的部署环境,把过程记录下给大家. 1. 开发运行环境 1> Visual Studio 2015 Update 3* 2> .NET Core 1.0 for Visual Studio (包括asp.net core 模板,其中如果机器上没有.net core sdk会默认安装)地址https://go.microsoft.com/f

  • 简单谈谈.NET Core跨平台开发

    对于.NET开源计划想必关注的人已经跃跃欲试了,但是真正将其用于开发的目前来说不多.毕竟截至本文发布时.NET Core才发布到1.0RC2版本.正式版预计还有一段时间.况且大多数人都是持观望态度,就算开发仍然用的还是.NET Framework.另外不得不说的一点就是.NET开源的进度很惊人但是社区建设还有待提升,很多配套的东西还不齐全.Java在语言层级上可能落后.NET但是Java的社区力量是.NET远不及的. 一.安装SDK .NET Core发布版:https://www.micros

  • 在IIS上部署ASP.NET Core项目的图文方法

    概述 与ASP.NET时代不同,ASP.NET Core不再是由IIS工作进程(w3wp.exe)托管,而是使用自托管Web服务器(Kestrel)运行,IIS则是作为反向代理的角色转发请求到Kestrel不同端口的ASP.NET Core程序中,随后就将接收到的请求推送至中间件管道中去,处理完你的请求和相关业务逻辑之后再将HTTP响应数据重新回写到IIS中,最终转达到不同的客户端(浏览器,APP,客户端等).而配置文件和过程都会由些许调整,中间最重要的角色便是AspNetCoreModule,

  • 详解ASP.NET Core Token认证

    令牌认证(Token Authentication)已经成为单页应用(SPA)和移动应用事实上的标准.即使是传统的B/S应用也能利用其优点.优点很明白:极少的服务端数据管理.可扩展性.可以使用单独的认证服务器和应用服务器分离. 如果你对令牌(token)不是太了解,可以看这篇文章( overview of token authentication and JWTs) 令牌认证在asp.net core中集成.其中包括保护Bearer Jwt的路由功能,但是移除了生成token和验证token的部

  • Asp.Net Core控制器如何接收原始请求正文内容详解

    主要目标 在Asp.net Core控制器中,通过自定义格式化程序来映射自定义处理控制器中的"未知"内容.本文将给大家详细介绍关于Asp.Net Core控制器接收原始请求正文内容的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧 简单案例 为了演示这个问题,我们用VS2017创建一个默认的Asp.net Core Web Api项目. [Route("api/[controller]")] [ApiController] public cl

  • ASP.NET Core扩展库之Http日志的使用详解

    最佳实践都告诉我们不要记录请求的详细日志,因为这有安全问题,但在实际开发中,请求的详细内容对于快速定位问题却是非常重要的,有时也是系统的强力证据.Xfrogcn.AspNetCore.Extensions扩展库提供了服务端和客户端的详细日志功能,通过配置可以开启. 服务端日志通过请求中间件来完成,中间件会以Trace级别记录请求和应答详情,以Debug级别记录请求耗时.服务的请求日志的名称为ServerRequest.Logger 要开启服务端详情日志,只需将扩展库配置中的ServerReque

  • ASP.NET Core中Razor页面的Handlers处理方法详解

    简介 在前一篇文章中,我们讨论了Razor页面.今天我们来谈谈处理方法(Handlers). 我们知道可以将代码和模型放在 .cshtml 文件里面或与 .cshtml 匹配的 .cshtml.cs 文件中. Razor页面处理程序或处理方法将用户请求匹配到我们的方法:请求来自 **.cshtml **文件. Razor页面遵循特定的命名约定.从上一篇文章可以看出,.NET Core开发工具自动生成了很多处理方法,例如下面这些: OnGet OnPost OnGetAsync OnPostAsy

  • ASP.NET Core扩展库之日志功能的使用详解

    上一篇我们对Xfrogcn.AspNetCore.Extensions扩展库功能进行了简单的介绍,从这一篇文章开始,我将逐步介绍扩展库中的核心功能.     日志作为非业务的通用领域基础功能,有非常多的技术实现,这些第三方库避免了我们花费时间去重复实现,不过,很多日志库配置复杂,不易于使用,入手较难,而有些库可能与ASP.NET Core的结合并不好.     如果我们没有对所使用的日志库进行详细了解,日志库也可能产生严重的问题,在我的开发生涯中,曾经遇到过多次因为日志库而导致的生产事故.  

  • ASP.NET Core使用自定义验证属性控制访问权限详解

    前言 大家都知道在应用中,有时我们需要对访问的客户端进行有效性验证,只有提供有效凭证(AccessToken)的终端应用能访问我们的受控站点(如WebAPI站点),此时我们可以通过验证属性的方法来解决. 本文将详细介绍ASP.NET Core使用自定义验证属性控制访问权限的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧 方法如下 一.public class Startup的配置: //启用跨域访问(不同端口也是跨域) services.AddCors(options

  • ASP.NET Core实现单体程序的事件发布/订阅详解

    背景 事件发布/订阅是一种非常强大的模式,它可以帮助业务组件间实现完全解耦,不同的业务组件只依赖事件,只关注哪些事件是需要自己处理的,而不用关注谁来处理自己发布事件,事件追溯(Event Sourcing)也是基于事件发布/订阅的.在微服务架构中,事件发布/订阅有非常多的应用场景.今天我给大家分享一个基于ASP.NET Core的单体程序使用事件发布/订阅的例子,针对分布式项目的事件发布/订阅比较复杂,难点是事务处理,后续我会另写一篇博文来演示. 案例说明 当前我们有一个基于ASP.NET Co

  • ASP.NET Core Mvc中空返回值的处理方法详解

    前言 如果你是一个初学者开始学习 ASP.NET 或 ASP.NET MVC, 你可能并不知道什么是. net Framework和. net ore.不用担心!我建议您看下官方文档https://docs.microsoft.com/zh-cn/aspnet/index , 您可以轻松地看到比较和差异. .NET Core MVC在如何返回操作结果方面非常灵活的. 你可以返回一个实现IActionResult接口的对象, 比如我们熟知的ViewResult, FileResult, Conte

  • ASP.NET Core如何添加统一模型验证处理机制详解

    一.前言 模型验证自ASP.NET MVC便有提供,我们可以在Model(DTO)的属性上加上数据注解(Data Annotations)特性,在进入Action之前便会根据数据注解,来验证输入的数据是否合法,下面介绍以下如何统一处理验证并返回错误信息.话不多说了,来一起看看详细的介绍吧. 二.Action过滤器实现统一验证 我们在判断验证状态时一般会在Action里判断ModelState.IsValid是否为true. public IActionResult Create([FromBod

  • Asp.Net Core利用文件监视进行快速测试开发详解

    前言 在进行 Asp.Net Core 应用程序开发过程中,通常的做法是先把业务代码开发完成,然后建立单元测试,最后进入本地系统集成测试:在这个过程中,程序员的大部分时间几乎都花费在开发.运行.调试上,而且一再的重复这个过程,我称这个过程为"程序员开发螺旋",并且在这个步骤中,重复率最高且没有创造力的工作就是启动.测试,作为程序员,努力提高生产力我们追求的目标,我们的工作就是尽量消灭重复劳动,解放生产力,提高产出效率: 下面就通过一个简单的例子来演示,如何通过文件监视进行快速开发. 本

  • ASP.NET Core中如何使用表达式树创建URL详解

    表达式树(Expression Tree) 表达式树是不可执行的代码,它只是用于表示一种树状的数据结构,树上的每一个节点都表示为某种表达式类型,大概有25种表达式类型,它们都派生自Expression类.创建表达式树具体有两个优势: 1.对表达式树的代码进行编辑修改,使表达式树中的代码变成动态代码,根据不同的数据库修改树上的代码逻辑从而达到动态切换数据库查询语句的目的,用表达式树可以动态构建针对不同数据库的查询语句. 2.完成类似反射访问未知对象的属性,通过动态构造表达式树,生成委托. 当我们在

随机推荐