ASP.NET Core Web APi获取原始请求内容
前言
我们讲过ASP.NET Core Web APi路由绑定,本节我们来讲讲如何获取客户端请求过来的内容。
ASP.NET Core Web APi捕获Request.Body内容
[HttpPost]
[Route("api/blog/jsonstring")]
public string Index([FromBody] string content)
{
return content;
}
//或者
[HttpPost("api/blog/jsonstring")]
public string Index([FromBody] string content)
{
return content;
}

由上图我们能够看到发出的为Post请求且Content-Type为application/json,所以此时在后台接受请求需要通过【FromBody】特性接受来自Post请求中的Body内容。这里需要特别说明的是:当在Vue利用axios发出Post请求且参数为简单类型参数时,但是在后台只能利用对象接收,即不能按照如下形式接收参数。
[HttpPost("api/blog/jsonstring")]
public int Index([FromBody] int id)
{
return id;
}
对于简单类型参数此时无需额外再定义对象来接收,我们可以采用变通的方式来接收即动态对象。
[HttpPost("api/blog/jsonstring")]
public int Index([FromBody] dynaminc dy)
{
var id = dy.id as int;
return id;
}
//或者
[HttpPost("api/blog/jsonstring")]
public int Index([FromBody] JObject jo)
{
JToken token = jo["id"];
if(token.Type == JTokenType.Null)
{
// Do your logic
}
......
}
上述两种方式皆可,利用JObject好处在于可判断参数是否为空情况,而dynamic可能会出现异常。如果我们想发送一个RAW字符串或者二进制数据,并且想要把它作为请求的一部分,那么这个时候就有意思,同时事情也会变得更加复杂。因为ASP.NET Core只会处理它所知道的信息,默认情况下是JSON和Form数据。 默认情况下原始数据不能直接映射到控制器上的方法参数上。什么意思呢?接下来我们若改变Content-Type为text/plain,此时将返回状态为415,如下图

那么对于此种情况,我们该如何获取请求参数呢?不幸的是,ASP NET Core不允许我们仅仅通过方法参数以任何有意义的方式捕获“原始”数据。因此我们需要通过处理Request.Body来获取原始数据,然后反序列化它。
我们可以捕获原始的Request.Body并从原始缓冲区中读取参数。最简而有效的方法是接受不带参数的POST或PUT数据,然后从Request.Body读取原始数据:
[HttpPost("api/blog/jsonstring")]
public async Task<string> Index()
{
var result = string.Empty;
using (var reader = new StreamReader(Request.Body, Encoding.UTF8))
{
result = await reader.ReadToEndAsync();
}
return result;
}

若我们想要读取二进制数据,我们可如下操作。
[HttpPost("api/blog/bytes")]
public async Task<byte[]> RawBinaryData()
{
using (var ms = new MemoryStream(2048))
{
await Request.Body.CopyToAsync(ms);
return ms.ToArray();
}
}

代码中的结果被捕获为二进制字节并以JSON返回,这也就是为什么我们会从上图看到base64编码的结果字符串伪装成二进制结果的原因。
像上述操作若很频繁我们完全可以封装起来,比如第三方调用我们接口时。封装如下:
public static class HttpRequestExtensions
{
public static async Task<string> GetRawBodyStringAsync(this HttpRequest request, Encoding encoding = null)
{
if (encoding is null)
encoding = Encoding.UTF8;
using (var reader = new StreamReader(request.Body, encoding))
return await reader.ReadToEndAsync();
}
public static async Task<byte[]> GetRawBodyBytesAsync(this HttpRequest request)
{
using (var ms = new MemoryStream(2048))
{
await request.Body.CopyToAsync(ms);
return ms.ToArray();
}
}
}
如果我们期望对于原始参数使用确定的方法,那么我们还需要做更多额外的工作。也就是说需要自定义InputFormatter。在ASP.NET Core中通过使用InputFormatter来自定义格式化内容,并将自定义格式化内容注入到请求ASP.NET Core管道中,并根据特定参数类型来决定是否需要处理请求内容。最终请求运行通过则将请求主体进行反序列化。对于自定义InputFormatter需要满足以下两个条件。
(1)必须使用[FromBody]特性来触发它。
(2) 对于对应的请求内容需自定义对应处理。
public class HandleRequestBodyFormatter : InputFormatter
{
public HandleRequestBodyFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/plain"));
SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/octet-stream"));
}
/// <summary>
/// 允许 text/plain, application/octet-stream和没有Content-Type的参数类型解析到原始数据
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override Boolean CanRead(InputFormatterContext context)
{
if (context == null) throw new ArgumentNullException(nameof(context));
var contentType = context.HttpContext.Request.ContentType;
if (string.IsNullOrEmpty(contentType) || contentType == "text/plain" ||
contentType == "application/octet-stream")
return true;
return false;
}
/// <summary>
/// 处理text/plain或者没有Content-Type作为字符串结果
/// 处理application/octet-stream类型作为byte[]数组结果
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
{
var request = context.HttpContext.Request;
var contentType = context.HttpContext.Request.ContentType;
if (string.IsNullOrEmpty(contentType) || contentType == "text/plain")
{
using (var reader = new StreamReader(request.Body))
{
var content = await reader.ReadToEndAsync();
return await InputFormatterResult.SuccessAsync(content);
}
}
if (contentType == "application/octet-stream")
{
using (var ms = new MemoryStream(2048))
{
await request.Body.CopyToAsync(ms);
var content = ms.ToArray();
return await InputFormatterResult.SuccessAsync(content);
}
}
return await InputFormatterResult.FailureAsync();
}
}
上述自定义InputFormatter使用CanRead方法来检查要支持的请求内容类型,然后使用ReadRequestBodyAsync方法将内容进行读取并反序列化为类型结果,该结果类型在控制器中的方法参数中返回。
最后需要做的则是将我们自定义的InputFormatter注入到MVC请求管道中一切大功告成。
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(o => o.InputFormatters.Insert(0, new HandleRequestBodyFormatter()));
}
通过对请求输入参数的自定义格式化处理,我们现在可以使用来自客户端Post或者Put请求且类型为text/plain, application/octet-stream或者没有类型。。下面我们来通过例子说明。
[HttpPost("api/blog/rawstring")]
public string RawRequestBodyFormatter([FromBody] string rawString)
{
return rawString;
}
ASP.NET Core自身本来就支持application/json类型,我们通过自定义InputFormatter不过是多了对text/plain额外类型的支持而已。
[HttpPost("api/blog/rawbytes")]
public byte[] RawBytesFormatter([FromBody] byte[] rawData)
{
return rawData;
}


总结
本文比较详细的讲解了在ASP.NET Core WebAPi中如何获取请求原始Body中的参数并添加了额外类型的支持,希望对阅读本文的您有所帮助。
ASP.NET Core Web APi获取原始请求内容的更多相关文章
- 在Mac下创建ASP.NET Core Web API
在Mac下创建ASP.NET Core Web API 这系列文章是参考了.NET Core文档和源码,可能有人要问,直接看官方的英文文档不就可以了吗,为什么还要写这些文章呢? 原因如下: 官方文档涉 ...
- 支持多个版本的ASP.NET Core Web API
基本配置及说明 版本控制有助于及时推出功能,而不会破坏现有系统. 它还可以帮助为选定的客户提供额外的功能. API版本可以通过不同的方式完成,例如在URL中添加版本或通过自定义标头和通过Accept- ...
- 在ASP.NET Core Web API中为RESTful服务增加对HAL的支持
HAL(Hypertext Application Language,超文本应用语言)是一种RESTful API的数据格式风格,为RESTful API的设计提供了接口规范,同时也降低了客户端与服务 ...
- 如何在ASP.NET Core Web API测试中使用Postman
使用Postman进行手动测试 如果您是开发人员,测试人员或管理人员,则在构建和使用应用程序时,有时了解各种API方法可能是一个挑战. 使用带有.NET Core的Postman为您的Web API生 ...
- ASP.NET Core Web API下事件驱动型架构的实现(一):一个简单的实现
很长一段时间以来,我都在思考如何在ASP.NET Core的框架下,实现一套完整的事件驱动型架构.这个问题看上去有点大,其实主要目标是为了实现一个基于ASP.NET Core的微服务,它能够非常简单地 ...
- ASP.NET Core Web API下事件驱动型架构的实现(二):事件处理器中对象生命周期的管理
在上文中,我介绍了事件驱动型架构的一种简单的实现,并演示了一个完整的事件派发.订阅和处理的流程.这种实现太简单了,百十行代码就展示了一个基本工作原理.然而,要将这样的解决方案运用到实际生产环境,还有很 ...
- ASP.NET Core Web API 集成测试
本文需要您了解ASP.NET Core Web API 和 xUnit的相关知识. 这里有xUnit的介绍: https://www.cnblogs.com/cgzl/p/9178672.html#t ...
- Asp.Net Core Web Api图片上传(一)集成MongoDB存储实例教程
Asp.Net Core Web Api图片上传及MongoDB存储实例教程(一) 图片或者文件上传相信大家在开发中应该都会用到吧,有的时候还要对图片生成缩略图.那么如何在Asp.Net Core W ...
- [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了
[译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 本文首发自:博客园 文章地址: https://www.cnblogs.com/yilezhu/p/ ...
随机推荐
- 定义一个数,它可能为正 也可能为负 var num = Math.pow(-1,parseInt(Math.random() * 2) + 1);
// 定义一个随机数范围从0 ~页面宽度 var x = parseInt(Math.random() * myCanvas.width); // 定义一个随机数 范围从0 ~页面高度 var y = ...
- java_web学习(三) eclipse_jsp学习
1.首先打开eclipse,新建一个Dynamac web project项目文件 2.在WebContent单击右键创建JSP File 3.过程 4.简单的jsp代码 运行结果: 5.导出war文 ...
- deeplearning.ai 神经网络和深度学习 week4 深层神经网络 听课笔记
1. 计算深度神经网络的时候,尽量向量化数据,不要用for循环.唯一用for循环的地方是依次在每一层做计算. 2. 最常用的检查代码是否有错的方法是检查算法中矩阵的维度. 正向传播: 对于单个样本,第 ...
- 实例讲解基于 React+Redux 的前端开发流程
原文地址:https://segmentfault.com/a/1190000005356568 前言:在当下的前端界,react 和 redux 发展得如火如荼,react 在 github 的 s ...
- 2017"百度之星"程序设计大赛 - 复赛1001&&HDU 6144 Arithmetic of Bomb【java大模拟】
Arithmetic of Bomb Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Other ...
- HDU6038-Function-数学+思维-2017多校Team01
学长讲座讲过的,代码也讲过了,然而,当时上课没来听,听代码的时候也一脸o((⊙﹏⊙))o 我的妈呀,语文不好是硬伤,看题意看了好久好久好久(死一死)... 数学+思维题,代码懂了,也能写出来,但是还是 ...
- BZOJ1758: [Wc2010]重建计划(01分数规划+点分治+单调队列)
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1758 01分数规划,所以我们对每个重心进行二分.于是问题转化为Σw[e]-mid>=0, ...
- JAVA爬虫实践(实践三:爬虫框架webMagic和csdnBlog爬虫)
WebMagic WebMagic是一个简单灵活的Java爬虫框架.基于WebMagic,你可以快速开发出一个高效.易维护的爬虫. 采用HttpClient可以实现定向的爬虫,也可以自己编写算法逻辑来 ...
- 【Git】Git基础操作
repository:版本库又名仓库,可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改.删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以&q ...
- c# base 和this 继承
父类的构造函数总是在子类之前执行的.既先初始化静态构造函数,后初始化子类构造函数. public class BaseCircle { public BaseCircle() { Console.Wr ...