【问题标题】:How to write API action method that would accept a file?如何编写可以接受文件的 API 操作方法?
【发布时间】:2020-04-18 08:07:07
【问题描述】:

我正在尝试编写一个 API 端点,允许用户使用 Asp.NET Core 3.1 将文件发送到我的服务器。

我有下面的action方法需要响应客户端的请求并处理文件验证和存储文件。

[HttpPost, Route("api/store")]
public async Task<IActionResult> Store([FromBody] MultipartFormDataContent content)
{
    // Validate the file, store it.

    return Ok();
}

我正在尝试像这样从客户端发送文件

using HttpClient client = new HttpClient();

var multiForm = new MultipartFormDataContent();
multiForm.Add(new StringContent("1/12/1.jpg"), "custom_file_name");
FileStream fs = System.IO.File.OpenRead("1.jpg");
multiForm.Add(new StreamContent(fs), "file", "1.jpg");

// send request to API
var url = "https://localhost:123/api/store";
var response = await client.PostAsync(url, multiForm);

但是服务器返回HTTP Code 404 Not Found

如何正确调整上述Store 方法,使其接受客户端发送的文件?

【问题讨论】:

  • 您的控制器上有[Route("...")] 属性吗?
  • 是的,我的控制器装饰有[ApiController, Route("api/[controller]")]。我什至在同一个控制器上添加了一个HttpGet 方法并且我能够使用它。
  • 你能发布你完整的控制器类和实现吗?

标签: c# asp.net-core asp.net-core-2.0 asp.net-core-3.0 asp.net-apicontroller


【解决方案1】:

您不应在操作方法路由中包含api/。控制器的路由已经以api 为前缀。另外,api/[controller] 表示控制器路由将是api/ControllerName,那么您的操作将是api/ControllerName/store

如果你的控制器类被命名为ThingsController,那么它的路由是api/things,你的Store动作应该是api/things/store

您不需要在操作上同时使用 HttpPostRoute 属性。 Route 属性接受所有 HTTP 动词,因此请使用其中一个(通常只是 HttpPost 或您将使用 action 方法处理的任何 HTTP 动词。所以:

[ApiController]
[Route("api/[controller]")]
public class ThingsController : ControllerBase
{
    // POST api/things/store
    [HttpPost("store")]
    public async Task<IActionResult> Store([FromBody] MultipartFormDataContent content)
    {
        // Do stuff
    }
}

此外,控制器应该使用IFormFile 来处理控制器中的文件上传。文档在这里:

Upload files in ASP.NET Core .

您的控制器将接受IFormFile files 参数而不是MultipartFormDataContent content

来自 MSDN 的示例,适合您的操作

[HttpPost("store")]
public async Task<IActionResult> Store(List<IFormFile> files)
{
    // Get total size
    long size = files.Sum(f => f.Length);

    foreach (var formFile in files)
    {
        if (formFile.Length > 0)
        {
            // Gets a temporary file name
            var filePath = Path.GetTempFileName();

            // Creates the file at filePath and copies the contents
            using (var stream = System.IO.File.Create(filePath))
            {
                await formFile.CopyToAsync(stream);
            }
        }
    }

    // Process uploaded files
    // Don't rely on or trust the FileName property without validation.

    return Ok(new { count = files.Count, size, filePath });
}

如果您正在处理单个文件,您可以只传递IFormFile file 而不是列表,并摆脱 action 方法中的循环。

我自己没有测试过这段代码,是在移动设备上编写的。希望它可以解决问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-20
    • 1970-01-01
    • 2013-02-09
    • 2013-05-07
    相关资源
    最近更新 更多