http post Raw(application/json)数据接收处理

 

在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

 

转自:https://www.cnblogs.com/fancunwei/p/9567497.html

<think>好的,用户之前问过关于C#中异步POST上传数据的问题,现在又询问关于请求头Content-Type: application/json的内容。我需要先理解用户的需求。可能用户在使用之前的代码示例时,遇到了设置Content-Type的问题,或者想更深入了解这个请求头的作用。 用户可能是刚开始学习API调用,对于HTTP请求头不太熟悉。他们可能在尝试发送JSON数据时,不确定如何正确设置请求头,导致服务器返回错误。所以需要详细解释Content-Type的作用,特别是application/json的用法,以及如何在C#中正确设置它。 需要确认用户是否了解HTTP请求头的基本概念,但根据之前的提问,用户可能已经接触过HttpClient的使用。因此,我的回答应该既包括基础解释,也要结合C#的具体实现,比如使用StringContent时的设置方法。同时,要注意提醒常见的错误,比如忘记设置Content-Type或者拼写错误,这些可能导致服务器无法正确解析数据。 另外,用户可能遇到的问题是,即使代码看起来正确,但服务器仍然返回错误,这时候需要检查Content-Type是否正确设置。可能还需要提到其他相关的Content-Type类型,比如表单数据,以便用户区分不同场景的用法。最后,给出一个具体的代码示例,帮助用户将理论应用到实践中,确保他们能够正确实现JSON数据POST请求。</think>在 HTTP 请求中,`Content-Type: application/json` 是一个 **关键请求头**,它明确告知服务器请求体(Body)中的数据格式是 **JSON**(JavaScript Object Notation)。这是现代 API 交互中最常用的数据格式之一。 --- ### 📌 **核心作用** 1. **数据格式声明** 告诉服务器端:`"我发送的请求体是 JSON 结构,请按 JSON 规则解析"` 2. **避免解析歧义** 未指定或错误设置会导致服务器无法正确解析数据(如返回 `400 Bad Request`) --- ### 🧩 **C# 中的典型用法** 使用 `HttpClient` 发送 JSON 数据时,需通过 `StringContent` 显式指定该头: ```csharp // 创建 JSON 内容 var data = new { Name = "John", Age = 30 }; string json = JsonSerializer.Serialize(data); // 序列化为 JSON 字符串 // 关键设置 Content-Type var content = new StringContent( json, Encoding.UTF8, "application/json" // 👈 这里指定 Content-Type ); // 发送 POST 请求 var response = await httpClient.PostAsync(apiUrl, content); ``` --- ### 🔍 **常见问题与注意事项** | 问题现象 | 原因与解决方案 | |---------| | 服务器返回 `400` 错误 | 未设置或拼写错误(如 `appliction/json`) | | 中文乱码 | 未指定 `Encoding.UTF8` | | 数据解析格式不符 | 实际发送的数据格式与声明的 Content-Type 不一致 | --- ### 💡 **其他常见 Content-Type 对比** | Content-Type | 适用场景 | C# 实现示例 | |--|-------------------------|--------------------------------| | `application/x-www-form-urlencoded` | 传统表单提交 | `FormUrlEncodedContent` | | `multipart/form-data` | 文件上传 | `MultipartFormDataContent` | | `text/plain` | 纯文本数据 | `new StringContent("raw text")` | --- ### 🚀 **调试技巧** 1. 使用 [Fiddler](https://www.telerik.com/fiddler) 或 [Postman](https://www.postman.com/) 抓包验证实际请求头 2. 服务端可添加日志检查接收到的原始数据 3. 使用 `curl` 快速测试: ```bash curl -X POST -H "Content-Type: application/json" -d '{"name":"test"}' http://api.example.com ``` --- 正确设置 `Content-Type: application/json` 是保证 JSON 数据交互成功的 **第一步**,建议与序列化/反序列化库(如 `System.Text.Json`)配合使用。如果需要处理复杂嵌套对象,可进一步配置 JSON 序列化选项(如驼峰命名、忽略空值等)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值