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中的参数并添加了额外类型的支持,希望对阅读本文的您有所帮助。

目录
相关文章
|
22天前
|
中间件 Go
Golang | Gin:net/http与Gin启动web服务的简单比较
总的来说,`net/http`和 `Gin`都是优秀的库,它们各有优缺点。你应该根据你的需求和经验来选择最适合你的工具。希望这个比较可以帮助你做出决策。
65 35
|
5月前
|
开发框架 前端开发 JavaScript
ASP.NET Web Pages - 教程
ASP.NET Web Pages 是一种用于创建动态网页的开发模式,采用HTML、CSS、JavaScript 和服务器脚本。本教程聚焦于Web Pages,介绍如何使用Razor语法结合服务器端代码与前端技术,以及利用WebMatrix工具进行开发。适合初学者入门ASP.NET。
|
7月前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
408 3
|
4月前
|
安全 Java 数据安全/隐私保护
springSecurity学习之springSecurity过滤web请求
通过配置 Spring Security 的过滤器链,开发者可以灵活地管理 Web 请求的安全性。理解核心过滤器的作用以及如何配置和组合这些过滤器,可以帮助开发者实现复杂的安全需求。通过具体的示例代码,可以清晰地了解 Spring Security 的配置方法和实践。
182 23
|
5月前
|
运维 前端开发 C#
一套以用户体验出发的.NET8 Web开源框架
一套以用户体验出发的.NET8 Web开源框架
142 7
一套以用户体验出发的.NET8 Web开源框架
|
4月前
|
开发框架 数据可视化 .NET
.NET 中管理 Web API 文档的两种方式
.NET 中管理 Web API 文档的两种方式
79 14
|
5月前
|
开发框架 .NET PHP
ASP.NET Web Pages - 添加 Razor 代码
ASP.NET Web Pages 使用 Razor 标记添加服务器端代码,支持 C# 和 Visual Basic。Razor 语法简洁易学,类似于 ASP 和 PHP。例如,在网页中加入 `@DateTime.Now` 可以实时显示当前时间。
|
7月前
|
开发框架 .NET API
Windows Forms应用程序中集成一个ASP.NET API服务
Windows Forms应用程序中集成一个ASP.NET API服务
154 9
|
8月前
|
开发框架 监控 前端开发
在 ASP.NET Core Web API 中使用操作筛选器统一处理通用操作
【9月更文挑战第27天】操作筛选器是ASP.NET Core MVC和Web API中的一种过滤器,可在操作方法执行前后运行代码,适用于日志记录、性能监控和验证等场景。通过实现`IActionFilter`接口的`OnActionExecuting`和`OnActionExecuted`方法,可以统一处理日志、验证及异常。创建并注册自定义筛选器类,能提升代码的可维护性和复用性。
118 3
|
2月前
|
关系型数据库 MySQL 数据库
基于Flink CDC 开发,支持Web-UI的实时KingBase 连接器,三大模式无缝切换,效率翻倍!
TIS 是一款基于Web-UI的开源大数据集成工具,通过与人大金仓Kingbase的深度整合,提供高效、灵活的实时数据集成方案。它支持增量数据监听和实时写入,兼容MySQL、PostgreSQL和Oracle模式,无需编写复杂脚本,操作简单直观,特别适合非专业开发人员使用。TIS率先实现了Kingbase CDC连接器的整合,成为业界首个开箱即用的Kingbase CDC数据同步解决方案,助力企业数字化转型。
347 5
基于Flink CDC 开发,支持Web-UI的实时KingBase 连接器,三大模式无缝切换,效率翻倍!

热门文章

最新文章