【问题标题】:API controller POST method multipart/form-data using boundary get message inside the bodyAPI 控制器 POST 方法 multipart/form-data 在主体内使用边界获取消息
【发布时间】:2021-10-20 08:31:45
【问题描述】:

我无法在 API 控制器上开发 C# (.Net 5) 方法,该控制器可以管理具有“multipart/form-data;”的请求;边界=边界'

我无法在客户端更改请求,我只能修改我的后端以适应它。

请求指定'Content-Type: multipart/form-data;边界=边界',它在正文中有一条我需要提取的 json 消息。

在请求的正文中,消息是这样的:

--boundary
Content-Disposition: form-data; name="ZZZZZZZZZZ"
Content-Type: application/json
Content-Length: 474

{
    //a well formed json
}
--boundary--

我可以使用以下代码:string dataMessage = new StreamReader(Request.Body).ReadToEndAsync().Result;

然后我可以手动解析该字符串以使用 IndexOf 和 Substring 仅提取 json 部分,然后使用 JsonConvert 将其转换为对象。

我想知道 .Net 上是否有一些选项可以在不手动解析字符串的情况下自动执行此操作。我认为必须有某种方法来解释边界标记并获取直接json数据。但我找不到任何 :( 这让我想到了,因为当使用 webhook.site 测试客户端请求时,它可以毫无问题地解析消息。

我的控制器定义是这样的

[ApiController]
[Route("api/[controller]")]
public class XXXXController : ControllerBase

而且方法是这样的

[HttpPost]
[Route("[action]")]
public IActionResult YYYYYYY()

我已经尝试指定模型绑定来更改行为,但我无法让它工作。

在提问之前我已经搜索过帮助:

感谢您的帮助!!

【问题讨论】:

    标签: c# controller multipartform-data .net-5 boundary


    【解决方案1】:

    .Net5 默认不支持multi-part/form-data 媒体类型。因此,输入格式化程序应该附加到MvcBuilder

    考虑到您无法操作客户端这一事实,可以使用启用此类输入格式的ApiMultipartFormDataFormatter

    1. 添加包:

    Install-Package ApiMultipartFormDataFormatter -Version 3.0.0

    1. Startup.cs中配置格式化程序

      public void ConfigureServices(IServiceCollection services)
      {
          services.AddControllers(options =>
          {
              options.InputFormatters.Add(new MultipartFormDataFormatter());
          });
      }
      
    2. 假设上述格式良好的 json 类似于下面的类:

      public class CustomContent
      {
          public string Name { get; set; }
      }
      

      因此,我们需要将它包装到另一个包含 this 作为参数的类中。 注意: 目前该库不支持反序列化因此,模型中添加了一个 getter-only 属性,该属性返回反序列化的对象。

      public class ViewModel
      {
          // Equivalent to ZZZZZZZZZZ
          public string SerializedContent { get; set; }
          public CustomContent Content => !string.IsNullOrEmpty(SerializedContent)
              ? JsonConvert.DeserializeObject<CustomContent>(SerializedContent)
              : null;
      }
      
    3. 改革 Action 以接受包装模型。

      [HttpPost]
      [Route("[action]")]
      public IActionResult Parse(ViewModel vm)
      {            
          return Ok($"Received Name: {vm?.Content?.Name}");
      }
      

    对应示例的工作cURL请求为:

    curl --location --request POST 'http://localhost:25599/api/MultiPart/Parse' 
    
    \
    --header 'Content-Type: multipart/form-data; boundary=boundary' \
    --data-raw '--boundary
    Content-Disposition: form-data; name="SerializedContent"
    Content-Type: application/json
    Content-Length: 100
    
    {
        "Name" : "Foo"
    }
    --boundary--'
    

    响应应该等于Received Name: Foo

    【讨论】:

    • A.Mokhtari 你的回答太棒了,非常感谢。我正在更改代码,使其符合您的指示,我将对其进行测试:)
    • 希望对您有所帮助。还有一个更好但耗时的解决方案,通过将 IModelBinderService 的新实现添加到 ApiMultipartFormDataFormatter 以启用直接反序列化来消除额外的包装器。
    猜你喜欢
    • 2019-07-08
    • 2016-09-08
    • 2017-10-11
    • 2011-04-22
    • 2015-09-19
    • 2021-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多