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]
[Route("api/blog/rawbytes")]
public byte[] RawBytesFormatter([FromBody] byte[] rawData)
{
    return rawData;
}


本文比较详细的讲解了在ASP.NET Core WebAPi中如何获取请求原始Body中的参数并添加了额外类型的支持,希望对阅读本文的您有所帮助。

目录
相关文章
|
14天前
|
编解码 前端开发 JavaScript
.NET_web前端框架_layui_栅格布局
【8月更文挑战第27天】
33 4
|
4天前
|
存储 XML 开发框架
ASP.NET Web Pages - 文件
本章介绍有关使用文本文件的知识。
16 4
|
5天前
|
开发框架 NoSQL .NET
利用分布式锁在ASP.NET Core中实现防抖
【9月更文挑战第5天】在 ASP.NET Core 中,可通过分布式锁实现防抖功能,仅处理连续相同请求中的首个请求,其余请求返回 204 No Content,直至锁释放。具体步骤包括:安装分布式锁库如 `StackExchange.Redis`;创建分布式锁服务接口及其实现;构建防抖中间件;并在 `Startup.cs` 中注册相关服务和中间件。这一机制有效避免了短时间内重复操作的问题。
|
4天前
|
开发框架 .NET
ASP.NET Web Pages - HTML 表单
表单是 HTML 文档中放置输入控件(文本框、复选框、单选按钮、下拉列表)的部分。
21 2
|
11天前
|
存储 消息中间件 前端开发
Web2py框架下的神秘力量:如何轻松集成第三方API,让你的应用不再孤单!
【8月更文挑战第31天】在开发现代Web应用时,常需集成第三方服务如支付网关、数据存储等。本文将指导你使用Web2py框架无缝接入第三方API。通过实例演示从注册获取API密钥、创建控制器、发送HTTP请求到处理响应的全过程。利用`requests`库与Web2py的内置功能,轻松实现API交互。文章详细介绍了如何编写RESTful控制器,处理API请求及响应,确保数据安全传输。通过本教程,你将学会如何高效整合第三方服务,拓展应用功能。欢迎留言交流心得与建议。
26 1
|
11天前
|
API C# 开发框架
WPF与Web服务集成大揭秘:手把手教你调用RESTful API,客户端与服务器端优劣对比全解析!
【8月更文挑战第31天】在现代软件开发中,WPF 和 Web 服务各具特色。WPF 以其出色的界面展示能力受到欢迎,而 Web 服务则凭借跨平台和易维护性在互联网应用中占有一席之地。本文探讨了 WPF 如何通过 HttpClient 类调用 RESTful API,并展示了基于 ASP.NET Core 的 Web 服务如何实现同样的功能。通过对比分析,揭示了两者各自的优缺点:WPF 客户端直接处理数据,减轻服务器负担,但需处理网络异常;Web 服务则能利用服务器端功能如缓存和权限验证,但可能增加服务器负载。希望本文能帮助开发者根据具体需求选择合适的技术方案。
41 0
|
11天前
|
API 网络安全 数据库
Web2py框架如何颠覆传统的RESTful API开发?掌握这些技巧,让你的开发效率飞跃!
【8月更文挑战第31天】Web2py是一款全栈Python Web框架,适用于快速开发复杂交互的Web应用。本文将介绍如何使用Web2py创建RESTful API,包括设置新控制器、定义RESTful路由、处理数据库交互、确保API安全性、编写文档与使用Swagger、测试API以及部署时的注意事项。Web2py的高度抽象和易用性使其成为实现RESTful API的理想选择,帮助开发者专注于业务逻辑而非技术细节。
19 0
|
15天前
|
开发框架 监控 .NET
开发者的革新利器:ASP.NET Core实战指南,构建未来Web应用的高效之道
【8月更文挑战第28天】本文探讨了如何利用ASP.NET Core构建高效、可扩展的Web应用。ASP.NET Core是一个开源、跨平台的框架,具有依赖注入、配置管理等特性。文章详细介绍了项目结构规划、依赖注入配置、中间件使用及性能优化方法,并讨论了安全性、可扩展性以及容器化的重要性。通过这些技术要点,开发者能够快速构建出符合现代Web应用需求的应用程序。
28 0
|
17天前
|
存储 Kubernetes API
【APIM】Azure API Management Self-Host Gateway是否可以把请求的日志发送到Application Insights呢?让它和使用Azure上托管的 Gateway一样呢?
【APIM】Azure API Management Self-Host Gateway是否可以把请求的日志发送到Application Insights呢?让它和使用Azure上托管的 Gateway一样呢?
|
28天前
|
机器人 API Python
智能对话机器人(通义版)会话接口API使用Quick Start
本文主要演示了如何使用python脚本快速调用智能对话机器人API接口,在参数获取的部分给出了具体的获取位置截图,这部分容易出错,第一次使用务必仔细参考接入参数获取的位置。