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

  • 相关阅读:
    vue数据传递--我有特殊的实现技巧
    解决Vue引入百度地图JSSDK:BMap is undefined 问题
    vue-quill-editor-upload : 实现vue-quill-editor上传图片到服务器
    vue.js的<slot>
    实例化vue发生了什么?(详解vue生命周期)
    vue2实现自定义样式radio单选框
    vue-lazyload插件
    Axios 使用时遇到的问题
    Vue组件开发 -- Markdown
    Javascript系列——对象元素的数组去重实现
  • 原文地址:https://www.cnblogs.com/CreateMyself/p/8410686.html
Copyright © 2011-2022 走看看