繁体   English   中英

ASP.NET Core Web API - Get file along with [FromBody] JSON model

[英]ASP.NET Core Web API - Get file along with [FromBody] JSON model

我有一个现有的 API 端点,它从客户端接收数据。 我想添加在该端点上接收文件以及数据的功能。 这是我的 model:

public class TestModel
{
    public IList<IFormFile> File { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public string Hobbies { get; set; }
}

这是我的终点:

[HttpPost]
[Route("upload")]
public async Task<ActionResult> UploadTest([FromBody] TestModel test)
{
    return Ok();
}

我目前正在用 Postman 测试我的 API。 我知道在 Postman 中有一个选项可以发送带有form-data文件,但在这种情况下,我的客户需要使用form-data ,而我使用 json 作为数据(使用FromBody )。

我找不到如何做到这一点的方法 - 有可能吗? 如果是这样,我如何使用 Postman 发送文件视图raw json?

上传文件时,如果您选择使用 JSON,那么您将客户端置于泡菜中。 您将强制客户端必须对所有文件进行 Base64 编码。 对于客户端来说,这可能是一项繁琐的任务,特别是如果它是浏览器 JS 代码。 更不用说你增加了 33% 的有效载荷。

表单数据?

发布文件时,您要使用 HTML forms。 这是浏览器非常擅长的事情。

话虽如此,正确的方法是将端点设置为如下所示:

// NOTE: This is the same as you have it in your question
public class TestModel
{
    public IList<IFormFile> File { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public string Hobbies { get; set; }
}

[HttpPost, Route("testme")]
public IActionResult TestMe([FromForm] TestModel testModel)
{
    return Ok();
}

记下[FromForm]参数。

然后设置 postman 看起来像这样:

邮差

  1. 将其设置为Post
  2. Go 到Body部分并将其设置为Form-Data
  3. 添加参数。 您可以通过多次添加相同的参数名称来测试多个文件。
  4. 单击Send

当端点被命中时,它应该如下所示:

发布数据

JSON?

假设您绝对必须使用 JSON 作为发布数据。 然后您的 model 将如下所示:

public class TestModel
{
    public IList<byte[]> File { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public string Hobbies { get; set; }
}

您将端点从[FromForm]更改为[FromBody] ,然后您必须说服前端开发人员他们必须使用Base64对所有文件进行编码并构建 JSON ZA8CFDE6331BD59EB2AC96F8911C4B66 到 send。 我认为您不会因此而获得超级成功,因为它没有经过优化,也不是常态。 他们可能会给你奇怪的表情。

让我们来看看这个场景:

  1. 用户选择 3 个文件。 每个都是 100 MB。
  2. 用户单击“提交”,JS 现在将 300 MB 读入 memory。
  3. JS 在浏览器中将 300 MB 的文件编码为 Base64。 Memory 使用量已增加到 400 MB。
  4. JS 客户端构建 JSON 有效负载 - Memory 再次增加,因为需要基于 400 MB 字符串创建新字符串。
  5. 网络流量开始。
  6. 后端获取帖子数据。 由于它是 JSON,因此 ASP.NET 内核将反序列化为您的 model。 框架必须分配 400 MB 的 memory 以从网络读取 JSON。
  7. ASP.NET 将反序列化,这也需要解码 400 MB 的数据,这不是一项轻松的任务。
  8. 您的端点在 memory 中使用 ~300MB model 调用。

您将在某个时候遇到 OutOfMemory 异常。 几乎可以保证。

表格数据在 ASP.NET 内核中得到了极大的优化。 它将使用 memory 处理较小的文件,然后转储到磁盘以获取较大的文件。 使用表单数据时不可能遇到 memory 问题。

让我们运行一个表单场景:

  1. 用户选择 3 个文件。 每个都是 100 MB。
  2. 用户点击“提交”。 浏览器现在将向后端发起请求,并将文件读入 memory 的小块并逐块发送。 一次一个文件。 这些块的大小根据环境进行了优化。 不会超过有效传输数据所需的量。 Memory 的使用量将最少。
  3. ASP.NET 开始读取数据。 如果IFormFile数据超过 5 MB(我相信这是限制),它会将其从内存 stream 移动到磁盘 stream。
  4. ASP.NET 使用对源自磁盘的流的引用来调用您的端点。 memory 中 model 的大小无关紧要,因为它只是保存对磁盘流的引用。

PostMan? 在 PostMan 中没有任何方法可以在不编写脚本的情况下执行此操作,同样因为这不正常。 我敢肯定,如果你用谷歌搜索这个主题,你可能会找到一些东西,但充其量只是很笨拙。

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Test.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class TestController : ControllerBase
    {
        [HttpPost]
        [Route("upload")]
        public async Task<ActionResult> UploadTest([FromForm] TestModel test)
        {
            return Ok();
        }
    }
    public class TestModel
    {
        public IList<IFormFile> File { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
        public string Hobbies { get; set; }
    }
}

这是来自 postman 的请求的样子。

在此处输入图像描述

这是我们得到文件的证据。 在此处输入图像描述

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM