J'essaie de créer une API Web qui gérerait le stockage des fichiers.
Le framework Asp.Net core 1.0+ est livré avec IFormFile interface qui permet de lier le fichier à un modèle de vue. Le documentation sur le téléchargement de fichiers dans ASP.NET Core indique ce qui suit
IFormFile peut être utilisé directement comme paramètre de méthode d'action ou comme propriété de modèle liée.
Lorsque j'ai utilisé IFormFile
comme paramètre de méthode d'action, cela fonctionnait sans problème. Mais dans mon cas, je souhaite l'utiliser comme propriété sur un modèle car je voudrais lier d'autres valeurs en plus d'inclure des règles de validation personnalisées. Voici mon modèle de vue.
public class NewFile
{
[Required]
[MinFileSize(125), MaxFileSize(5 * 1024 * 1024)]
[AllowedExtensions(new[] { ".jpg", ".png", ".gif", ".jpeg", ".tiff" })]
public IFormFile File { get; set; }
[Required]
public int? CustomField1 { get; set; }
[Required]
public int? CustomField2 { get; set; }
[Required]
public int? CustomField3 { get; set; }
}
Voici mon code pour la demande client et le code serveur qui accepte le fichier. Les deux méthodes sont placées dans le même contrôleur par souci de simplicité. Mais en réalité, la méthode "client" sera placée dans une application séparée qui envoie les fichiers.
[ApiController, Route("api/[controller]")]
public class FilesController : ControllerBase
{
[HttpGet("client")]
public async Task<IActionResult> Client()
{
using HttpClient client = new HttpClient();
// we need to send a request with multipart/form-data
var multiForm = new MultipartFormDataContent
{
// add API method parameters
{ new StringContent("CustomField1"), "1" },
{ new StringContent("CustomField2"), "1234" },
{ new StringContent("CustomField3"), "5" },
};
// add file and directly upload it
using FileStream fs = System.IO.File.OpenRead("C:/1.jpg");
multiForm.Add(new StreamContent(fs), "file", "1.jpg");
// send request to API
var responce = await client.PostAsync("https://localhost:123/api/files/store", multiForm);
return Content("Done");
}
[HttpPost("store")]
public async Task<IActionResult> Store(NewFile model)
{
if (ModelState.IsValid)
{
try
{
var filename = MakeFileName(model, Path.GetFileName(model.File.FileName));
Directory.CreateDirectory(Path.GetDirectoryName(filename));
using var stream = new FileStream(filename, FileMode.Create);
await model.File.CopyToAsync(stream);
return PhysicalFile(filename, "application/octet-stream");
}
catch (Exception e)
{
return Problem(e.Message);
}
}
// Are there a better way to display validation errors when using Web API?
var errors = string.Join("; ", ModelState.Values.SelectMany(v => v.Errors).Select(v => v.ErrorMessage));
return Problem(errors);
}
}
Lorsque je fais la demande, j'obtiens l'erreur suivante mais la demande ne parvient jamais à la méthode store
car j'ai placé un point d'arrêt là-bas, mais elle n'y parvient jamais.
StatusCode: 415, ReasonPhrase: 'Unsupported Media Type', Version: 1.1, Contenu: System.Net.Http.HttpConnectionResponseContent
Comment puis-je envoyer correctement le fichier au serveur et le lier à la propriété File
sur mon modèle de vue?
ApiController
par défaut attend JSON sauf indication contraire explicite
Utilisation [FromForm]
pour lier le modèle en utilisant des données de formulaire dans le corps de la requête.
public async Task<IActionResult> Store([FromForm]NewFile model) {
//...
}.
Référence Liaison de modèle dans ASP.NET Core
les CustomField1, CustomField2 et CustomField3` sont nuls même s'ils sont envoyés comme vous le voyez dans ma question d'origine
le client n'envoie pas correctement ces autres champs. Vous avez changé le contenu et les noms de champ
var multiForm = new MultipartFormDataContent {
// add API method parameters
{ new StringContent("1"), "CustomField1" },
{ new StringContent("1234"), "CustomField2" },
{ new StringContent("5"), "CustomField3" },
};