Мне нужно протестировать XUnit следующую конечную точку:
/// <summary>
/// Cease all account in csv file
/// </summary>
/// <param name = "dispatcher"></param>
/// <param name = "csvFile">CSV file with format (Id,Name,SID,CeaseDate,Note)</param>
/// <param name = "ct"></param>
/// <returns></returns>
[HttpPost("cease/bulk")]
[Authorize(Roles = VdcSecurity.Role.ManagementAdmin)]
[AllowAnonymous]
public async Task<ActionResult<bool>> CeaseBulkAccountAsync(
[FromServices][IsSensitive] ICommandDispatcher dispatcher,
[FromForm] IFormFile csvFile,
[IsSensitive] CancellationToken ct = default
)
{
var identity = Vdc.Libs.AspNet.Controller.HttpContextExtensions.GetIdentity(HttpContext);
var ipAddress = HttpContext.GetIpAddress();
var command = new CeaseBulkCommand(identity, HttpContext.TraceIdentifier)
{
Stream = csvFile.OpenReadStream(),
IpAddress = ipAddress
};
var result = await dispatcher.DispatchAsync(_provider, command, ct);
return result.ToActionResult(this);
}
Моя проблема в том, что независимо от того, как я создаю объект IFormFile, он всегда принимается как нулевой.
Это одна из моих попыток:
const string filePath = "CeaseBulkAccount.csv";
using (var httpClient = ApiClient.HttpClient)
{
var form = new MultipartFormDataContent();
byte[] fileData = File.ReadAllBytes(filePath);
ByteArrayContent byteContent = new ByteArrayContent(fileData);
byteContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
form.Add(byteContent, "file", Path.GetFileName(filePath));
var result = await httpClient.PostAsync("/api/accounts/cease/bulk", form);
}
Я добираюсь до контроллера, но csvFile получен как нулевой.
ApiClient.HttpClient — наш собственный клиент, но я бы не отказался использовать универсальный.
Я должен сказать, что наш httpClient «PostAsync» получает HttpContent.
Вторая попытка:
var httpClient = ApiClient.HttpClient;
var fileContent = new ByteArrayContent(ReadFully(file));
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "CeaseBulkAccount.csv"
};
var response = await httpClient.PostAsync("/api/accounts/cease/bulk", fileContent, ct);
public static byte[] ReadFully(Stream input)
{
byte[] buffer = new byte[16 * 1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
Опять же, csvFile имеет значение null.
Наш PostAsync:
//
// Summary:
// Send a POST request with a cancellation token as an asynchronous operation.
//
// Parameters:
// requestUri:
// The Uri the request is sent to.
//
// content:
// The HTTP request content sent to the server.
//
// cancellationToken:
// A cancellation token that can be used by other objects or threads to receive
// notice of cancellation.
//
// Returns:
// The task object representing the asynchronous operation.
//
// Exceptions:
// T:System.InvalidOperationException:
// The requestUri must be an absolute URI or System.Net.Http.HttpClient.BaseAddress
// must be set.
//
// T:System.Net.Http.HttpRequestException:
// The request failed due to an underlying issue such as network connectivity, DNS
// failure, server certificate validation or timeout.
//
// T:System.Threading.Tasks.TaskCanceledException:
// .NET Core and .NET 5 and later only: The request failed due to timeout.
//
// T:System.UriFormatException:
// The provided request URI is not valid relative or absolute URI.
public Task<HttpResponseMessage> PostAsync([StringSyntax("Uri")] string? requestUri, HttpContent? content, CancellationToken cancellationToken);





Имя, передаваемое в form.Add, должно совпадать с именем в методе действия контроллера; [FromForm] IFormFile csvFile.
Поскольку это csvFile, вам необходимо добавить файл, как показано ниже.
form.Add(byteContent, "csvFile", Path.GetFileName(filePath));
С указанным выше изменением ваша первая попытка работает нормально.
Альтернативно вы можете установить фиксированное имя с помощью FromForm.
Это также защитит вас от любых переименований при рефакторинге любого связанного кода.
Это имя, указанное в атрибуте, которое необходимо использовать при публикации файла.
[FromForm(Name = "file")] IFormFile csvFile
Большое спасибо @pfx, вы были совершенно правы. Теперь все работает как положено, и я ценю ваше время :)