Home - About me - Browse by categories

[ASPNET Web API] Upload de fichier avec le HttpClient

Il est fréquent qu’un service http est besoin de récupérer un fichier envoyé par l’utilisateur. ASP.NET Web API permet bien entendu de mettre en place ce type de scénario.

Code côté Serveur

Côté serveur, il s’agit d’un contrôleur ASP.NET Web API tout à fait classique qui va exploiter une multipart provider pour pouvoir télécharger et enregistrer le fichier. Si vous ne connaissez pas les multipart provider, je vous invite à lire cet article que j’ai posté il y a quelques temps.

public class FileUploadController : ApiController
{
public async Task<HttpResponseMessage> Post()
{
//si la requête n'est pas en multipart/form-data
if (!Request.Content.IsMimeMultipartContent("form-data"))
{
return Request.CreateErrorResponse(HttpStatusCode.UnsupportedMediaType, "non supporté");
}

//création du multipart provider fournit avec Web API et qui permet d'enregistrer les fichiers
//sur le file system (ici dans un répertoire temporaire)
var multipartFileStreamProvider = new MultipartFileStreamProvider(Path.GetTempPath());

//récupération des fichiers
var files = await Request.Content.ReadAsMultipartAsync(multipartFileStreamProvider).ContinueWith(task =>
{
if (task.IsFaulted)
throw task.Exception;

return task.Result.FileData.ToList();
});

//travail sur les fichiers
foreach (var file in files)
{
//chemin du fichier en local :
string path = file.LocalFileName;

//nom du fichier envoyé :
string fileName = file.Headers.ContentDisposition.FileName;
}

return Request.CreateResponse(HttpStatusCode.OK);
}
}

###

Code côté client

Côté client, pour envoyer un fichier, nous allons utiliser une application console et la classe HttpClient. Il est nécessaire d’ajouter une référence vers System.Net.Http.dll :

image

Ensuite, il faut tout d’abord créer un FileStream sur le fichier à Upload, puis un StreamContent. Ce StreamContent est alors rajouté à un MultipartFormDataContent, qui représente le contenu qui sera envoyé par le client http, en mode multipart/form-data.

Il suffit alors de créer un HttpClient et de poster le contenu à l’adresse adéquate.

private static async Task UploadAsync()
{
int bufferSize = 1024;

//création du file stream
using (var fs = new FileStream("Fichier à envoyer.txt", FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, true))
{
//création d'un stream StreamContent qui représente le fichier
StreamContent streamContent = new StreamContent(fs, bufferSize);

//création d'un MultipartFormDataContent et ajout du fichier
MultipartFormDataContent formDataContent = new MultipartFormDataContent();
formDataContent.Add(streamContent, "fichier", "Fichier à envoyer.txt");

//création du http client
HttpClient client = new HttpClient();

var uri = new Uri("http://localhost:17831/api/fileupload");

//envoie de la requête HTTP POST
HttpResponseMessage response = await client.PostAsync(uri, formDataContent);

//récupération du status
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
Console.WriteLine("fichier envoyé");
}
else
{
string description = await response.Content.ReadAsStringAsync();
Console.WriteLine("une erreur s'est produite : " + description);
}

Console.ReadLine();
}
}

Et voilà, le tour est joué ! Le code source de démonstration est dispo ici : http://juliencorioland.blob.core.windows.net/publicfiles/UploadHttpSample.zip

Enjoy Winking smile

Julien

read more

[ASP.NET MVC] Utilisation des préfixes pour la génération des noms et id des champs

Il n’est pas rare d’avoir plusieurs formulaires sur une même page HTML, et d’autant plus lorsque l’on compose une vue dynamiquement à partir de vues partielles. Du coup, il est vite possible de se retrouver avec des inputs qui possèdent les mêmes noms et mêmes id. Dans ce cas, c’est l’arrachage de cheveux assuré lors du debug de la manipulation du DOM avec jQuery Smile

Heuresement, la nature ASP.NET MVC est bien fait ! Il est en effet possible de préciser un préfixe à appliquer lors de la génération des inputs à l’aide des html helpers. Au sein d’une action de contrôleur, il suffit d’affecter la propriété HtmlFieldPrefix de la propriété TemplateInfo du ViewData, comme suivant :

public class ClientController : Controller
{
public ActionResult Ajouter()
{
ViewData.TemplateInfo.HtmlFieldPrefix = "AjouterClient";
return View(new Client());
}
}

Du coup, en utilisant correctement les Html Helpers pour générer les champs de formulaires (TextBox, CheckBox, DropDownList, Editor etc…), ASP.NET MVC tient compte de ce préfixe pour nommer les champs.

@model MvcApplication2.Models.Client

@{
ViewBag.Title = "Ajouter un client";
}

## Ajouter un client

@using (Html.BeginForm()) {

<div class="editor-label">
@Html.LabelFor(model => model.Prenom)

<div class="editor-field">
@Html.EditorFor(model => model.Prenom)

<div class="editor-label">
@Html.LabelFor(model => model.Nom)

<div class="editor-field">
@Html.EditorFor(model => model.Nom)

<div class="editor-label">
@Html.LabelFor(model => model.Adresse)

<div class="editor-field">
@Html.EditorFor(model => model.Adresse)

<input type="submit" value="Create" />

}

@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}

Le template razor précédent génère alors le markup HTML qui suit :

<div class="editor-field">
<input id="AjouterClient_Prenom" name="AjouterClient.Prenom" type="text" value="" />

<div class="editor-label">
<label for="AjouterClient_Nom">Nom :</label>

<div class="editor-field">
<input id="AjouterClient_Nom" name="AjouterClient.Nom" type="text" value="" />

<div class="editor-label">
<label for="AjouterClient_Adresse">Adresse :</label>

<div class="editor-field">
<input id="AjouterClient_Adresse" name="AjouterClient.Adresse" type="text" value="" />

Comme vous pouvez le remarquer, les attributs name et id ont bien été préfixés.

Se pose maintenant le problème de la résolution du modèle par le ModelBinder d’ASP.NET MVC lorsque le formulaire sera posté. En effet, par défaut le ModelBinder va chercher dans les champs postés des propriétés nommées exactement comme les propriétés de l’objet que l’on récupère en paramètre de l’action (ici un Client). Du coup, il est nécessaire d’indiquer au ModelBinder qu’un préfixe a été utilisé. Ceci se fait à l’aide de l’attribut Bind, comme le montre le code ci-dessous :

[HttpPost]
public ActionResult Ajouter([Bind(Prefix="AjouterClient")]Client client)
{
//ajout du client
return View("Index");
}

Et voilà, le tour est joué, le ModelBinder retombe sur ses pattes :

image

Enjoy Winking smile

Julien

read more

[Techdays 2013] Quoi de neuf dans ASP.NET MVC 4 et Introduction à Windows Phone 8

L’info ne vous a surement pas échapée, les Techdays 2013 commencent dans un peu plus d’une semaine maintenant, le 12 Février ! Smile

J’aurais la chance d’animer deux sessions lors de cette nouvelle édition :

  • Introduction au développement Windows Phone 8 : avec Introduction au développement Windows Phone 8 (Microsoft), nous vous donnerons tous les pointeurs pour bien démarrer vos applications Windows Phone ! De l’installation des outils, en passant par la création d’un premier projet jusqu’à la création de votre compte développeur et à la publication d’une application sur le Windows Phone store, nous essayerons de vous donner une bonne vue d’ensemble de la plateforme Windows Phone, pour les développeurs !
  • Quoi de neuf dans ASP.NET MVC 4 : avec Quoi de neuf dans ASP.NET MVC 4 (Infinite Square), nous ferons un tour d’horizon des nouveautés d’ASP.NET MVC 4, avec de nombreuses démonstrations au programme : async/await, templates mobile, bundling, open auth et bien d’autres ! Un coktail de démos pour bien prendre en main toutes ces nouveautés !

Rendez-vous au Palais des Congrès le 12 !

Julien

read more

[ASPNET MVC] Utilisation du plugin jQuery BBQ pour la gestion du bouton back et la navigation par fragment dans une SPA

Lorsque l’on développe une Single Page Application (SPA) en JavaScript, il est souvent recommandé de mettre en place la gestion du bouton back pour l’historique de navigation, ainsi que de la navigation par fragment, permettant de conserver des URL propres à chaque ressource et permettant ainsi aux utilisateur d’utiliser la SPA comme n’importe qu’elle autre application.

Comprendre la navigation par fragment

Il s’agit en fait des éléments que l’on rajoute à une URL de page, après le caractère #. On parle aussi d’ancre dans la page. Par exemple, le lien suivant http://www.monsite.com/page1.html#menu, permet de rediriger l’utilisateur directement vers une section de le page.

Ce système existant dans HTML depuis de nombreuses années a été ré-exploité en JavaScript pour de la navigation par fragment. Par exemple, l’équivalent de la page http://www.monsite.com/customers/32, qui affiche le détail du client ayant 32 pour id, peut se retrouver sous la forme de http://www.monsite.com/customers/32 afin d’être exploité directement depuis du code JavaScript. Les éléments qui suivent le caractère # représente le hash de l’URL.

Il suffit donc d’avoir un mécanisme en JavaScript pour détecter les changements de hash et permettre à l’utilisateur de passer de ressource en ressource à l’aide des boutons précédent / suivant du navigateur, de mettre une ressource en favoris, d’envoyer une ressource par mail à un autre utilisateur, etc…

C’est ce que permet de faire le plugin jQuery BBQ : http://benalman.com/projects/jquery-bbq-plugin/

Mise en place de jQuery BBQ

Commencez par télécharger les sources du plugin ici ou ici (en version minified). Ajoutez ensuite le fichier à votre projet de développement et référencez le dans la/les page(s) dans la(les)quelle(s) vous souhaitez l’utiliser.

<script src="/Scripts/jquery.ba-bbq.js"></script>

Une fois ceci fait, il est possible de s’abonner à un événement hashchange sur la window. Cet événement sera levé à chaque fois que le hash de l’URL sera modifié :

<div id="menu">

- @Html.ActionLink("Clients", "Index", "Customers", null, new { @class = "menu-link" })

- @Html.ActionLink("Achats", "Index", "Orders", null, new { @class = "menu-link" })

<div id="contentDiv">

<div id="currentFragment">

@section scripts{
<script type="text/javascript">
$(document).ready(function () {
$(window).bind("hashchange", function (e) {
//récupération du fragment
var fragment = e.fragment;
$("#currentFragment").html("" + fragment + "

");
});
});
</script>
}

Dès lors, dès que le fragment change dans l’URL, le hash est affiché sur la page, dans le conteneur currentFragment :

image

A présent, il suffit de remplacer les liens directs des différentes sections de l’application par des liens fragmentés :

$(".menu-link").each(function(index, item) {
var href = $(item).attr("href");
var newHref = thisUrl + "#" + href;
$(item).attr("href", newHref);
});

Et de charger le contenu représenter par le fragment lorsque le hash change :

$(window).bind("hashchange", function (e) {
//récupération du fragment
var fragment = e.fragment;
$("#currentFragment").html("" + fragment + "

");

//chargement du contenu
$("#contentDiv").load(fragment);
});

Enfin, pour faire en sorte que le contenu soit également chargé lorsque l’utilisateur accède directement à votre application web via une URL fragmentée, il est nécessaire de forcer l’évènement hashchange sur le document ready :

$(window).trigger("hashchange");

Et voilà, le tour est joué. Vous pouvez récupérer les sources de l’exemple ici.

Enjoy Clignement d'œil

Julien

read more

Utilisez Razor dans vos scripts JavaScript

Dans certains cas, il peut être intéressant de pouvoir formater du JavaScript avec Razor, pour tirer profit des données du modèle directement dans vos scripts JavaScript.

Pour cela, il suffit d’utiliser la notation <text></text> comme ci-dessous :

<script type="text/javascript">

//code JavaScript
var customers = new Array();

//code Razor/C#
@foreach(var customer in Model.Customers)
{
//code javascript + razor/c#
<text>
customers.push({
name: "@customer.Name",
firstname: "@customer.FirstName",
birth: "@customer.BirthDate"
});
</text>
}

$(document).ready(function(){
//ect...
});

</script>

Et voilà : simple, mais efficace !

A+

Julien

read more