asp.net - 尝试使用 HttpWebRequest 方法将文件从控制器上传到具有 [FromForm] IFormFile 文件参数的 API 操作接收 null
问题描述
我正在尝试将文件从 Controller 上传到 asp.net core mvc 中的 API。接收文件的 API 方法如下:
[Route("api/[controller]")]
public class ReservationController : Controller
{
...
[HttpPost]
public void UploadFile ([FromForm] IFormFile file)
{
....
}
}
现在在我的另一个项目中有一个包含文件上传控件的视图:
<form method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<div class="text-center panel-body">
<button type="submit" class="btn btn-sm btn-primary">Add</button>
</div>
</form>
现在在提交按钮上,被调用的控制器操作是:
[HttpPost]
public void AddFile(IFormFile file)
{
HttpWebRequest apiRequest = WebRequest.Create("http://localhost:8888/api/Reservation") as HttpWebRequest;
apiRequest.Method = "Post";
apiRequest.ContentType = "application/octet-stream";
string apiResponse = "";
using (HttpWebResponse response = apiRequest.GetResponse() as HttpWebResponse)
{
StreamReader reader = new StreamReader(response.GetResponseStream());
apiResponse = reader.ReadToEnd();
}
}
IFormFile 文件获取文件。到目前为止,它工作正常。
问题
现在我正在创建 HttpWebRequest 的对象以使用 API 方法上传我的文件。
API方法被调用,但我恢复了空值,即在下面的方法中,“文件”参数为空?
[HttpPost]
public void UploadFile ([FromForm] IFormFile file)
{
}
为什么会这样?请帮忙?
笔记
如果我只是将视图更改为将表单上的操作属性包含到我的 API 的 URL,那么它可以工作:
<form method="post" enctype="multipart/form-data" action="http://localhost:8888/api/Reservation/UploadFile">
<input type="file" name="file" />
<div class="text-center panel-body">
<button type="submit" class="btn btn-sm btn-primary">Add</button>
</div>
</form>
由于我想从控制器执行此操作并获取响应,因此我想从控制器进行 API 调用。
解决方案
IFormFile
特别只适用于multipart/form-data
编码,这就是为什么它在你的第一个动作中很好。但是,在您在那里提出的请求中,最大的问题是您实际上甚至没有首先在请求中发送文件。然后,您还需要使用multipart/form-data
而不是application/octet-stream
.
处理这个通过WebRequest
实际上是相当复杂的,并且HttpClient
无论如何都是发出请求的首选方式。与HttpClient
:
string apiResponse;
using (var httpClient = new HttpClient())
{
var form = new MultipartFormDataContent();
using (var fileStream = file.OpenReadStream())
{
form.Add(new StreamContent(fileStream), "file", file.Filename);
using (var response = await httpClient.PostAsync(url, form))
{
response.EnsureSuccessStatusCode();
apiResponse = await response.Content.ReadAsStringAsync();
}
}
}
我想故意使示例简单,但实际上您不应该这样使用HttpClient
。在大多数情况下,它应该被视为一个单例,幸运的是,通过IHttpClientFactory
. 首先,您将需要某种包装类,这在您使用 API 时是个好主意:
public class ReservationService
{
private readonly HttpClient _client;
public ReservationService(HttpClient client)
{
_client = client ?? throw new ArgumentNullException(nameof(client));
}
public async Task<string> AddFileAsync(IFormFile file)
{
var form = new MultipartFormDataContent();
using (var fileStream = file.OpenReadStream())
{
form.Add(new StreamContent(fileStream), "file", file.Filename);
using (var response = await httpClient.PostAsync("", form))
{
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
}
}
然后,在ConfigureServices
中Startup.cs
:
services.AddScoped<ReservationService>();
services.AddHttpClient<ReservationService>(c =>
{
c.BaseAddress = new Uri("http://localhost:8888/api/Reservation");
});
最后,在您的控制器中:
public class FooController : Controller
{
private readonly ReservationService _service;
public FooController(ReservationService service)
{
_service = service ?? throw new ArgumentNullException(nameof(service));
}
...
[HttpPost]
public async Task<IActionResult> AddFile(IFormFile file)
{
var apiResponse = await _service.AddFileAsync(file);
// whatever else you want to do
}
}
推荐阅读
- apache-nifi - 在 Apache NiFi 中解析微秒
- php - Google Recaptcha V2“网站所有者错误:无效的网站密钥”
- c - 如何释放这段 C 代码中分配的内存?
- java - JPA 和 MS SQL GenerationType.Identity 始终为空
- c# - .NET Framework 中的 DbProviderFactories.RegisterFactory?
- json - 需要将数据从一个文本文件附加到 linux 中的 json 文件
- sql-server - SQL Server 2019 中的标量 UDF 内联问题
- php - 带有 PHP Soap 请求的变音符号
- node.js - 带有帖子的映射条目的缩进错误
- sql - 跨多个数据库的 Azure SQL 触发器