09 Apr 2013 in
ASP.NET MVC
|
Microsoft Azure
C’est assez fréquent d’avoir à générer des URLs absolues dans une application ASP.NET MVC.
cela, il est possible de faire appel à l’UrlHelper, disponible sur n’importe quelle page ou n’importe quel contrôleur, via la propriété Url
:
@Url.Action("About", "Home", null, "http")
Si vous êtes sur un applicatif ayant un seul frontal web, aucun problème l’url qui sera générée sera complètement exploitable. Par contre, dès lors que vous serez sur un applicatif hébergée dans une ferme derrière un NLB, vous récupèrerez l’adresse de la machine (IP + port) et non le DNS qui va bien pour contacter votre site. Le code ci-dessus executé dans la fabrique Azure locale donne :
Bien que l’on appelle l’adresse sur le port 81 (NLB de la fabrique locale) on récupère bien une URL sur le port 82 !
Pour corriger ce problème, il faut générer l’URL relative en utilisant l’UrlHelper, puis se baser sur le Host header de la requête http courante pour récupérer la bonne adresse. Aussi, il est possible de créer des méthodes d’extensions à l’UrlHelper pour faire cela :
public static class UrlHelperExtensions
{
public static string ToAzureComplientAbsoluteUrl(this UrlHelper helper, string action, string controller)
{
return helper.ToAzureComplientAbsoluteUrl(action, controller, null);
}
public static string ToAzureComplientAbsoluteUrl(this UrlHelper helper, string action, string controller, object routeValues)
{
var relativeUrl = helper.Action(action, controller, routeValues);
var host = helper.RequestContext.HttpContext.Request.Headers["Host"];
var scheme = helper.RequestContext.HttpContext.Request.Url.Scheme;
return string.Format("{0}://{1}{2}", scheme, host, relativeUrl);
}
}
Du coup, il est possible de l’utiliser de la manière suivante :
@Url.ToAzureComplientAbsoluteUrl("About", "Home")
Et voilà le résultat :
A présent, plus de problème !
Enjoy
Julien
read more
04 Apr 2013 in
General
Un post rapide pour partager avec vous le fait que j’ai été renouvelé MVP par Microsoft le 01/04, cette fois dans la catégorie ASP.NET / IIS !
Après 3 années de MVP plutôt orienté sur le développement client (Client App Dev) puis mobile (Device App Dev), j’ai choisi cette année de postuler dans la catégorie ASP.NET / IIS, de manière assez logique étant donné le contenu sur lequel je travaille depuis maintenant plusieurs mois (et qui alimente pas mal ce blog, d’ailleurs !)
Je continuerai donc à parler de toute les technos qui touchent de près ou de loin la plateforme Web Microsoft.
J’en profite pour remercier les personnes qui ont appuyé ma candidature chez Microsoft France.
A bientôt
Julien
read more
05 Mar 2013 in
ASP.NET MVC
ASP.NET MVC fait appel au JavaScriptSerializer lorsque l’on retourne un JsonResult, contrairement à ASP.NET Web API qui lui utilise nativement le Framework Json.NET de Newtonsoft. Cela peut entraîner des comportements assez étranges dans le cas d’intéropérabilité entre une application MVC et un service Web API.
Concrètement, le framework Json.NET est beaucoup plus riche que le JavaScriptSerializer pour la manipulation d’objets JSON, du coup, il est clairement recommandé de l’utiliser, même en ASP.NET MVC. Pour le récupérer, il suffit de télécharger le package NuGet Json.NET :
La première étape consiste à créer un ActionResult personnalisé qui utilise Json.NET dans la méthode ExecuteResult :
public class JsonNetResult : JsonResult
{
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
var response = context.HttpContext.Response;
response.ContentType = !String.IsNullOrEmpty(ContentType) ? ContentType : "application/json";
if (ContentEncoding != null)
response.ContentEncoding = ContentEncoding;
if (Data == null)
return;
var serializedObject = JsonConvert.SerializeObject(Data, Formatting.Indented);
response.Write(serializedObject);
}
}
Ensuite, pour pouvoir faire en sorte que ce soit ce JsonNetResult là qui soit automatiquement renvoyé par la méthode Json de vos contrôleurs, il suffit de passer par un contrôleur intermédiaire dans lequel vous pouvez redéfinir les différentes surcharges de la méthode Json, et notamment :
protected override JsonResult Json(object data, string contentType, Encoding contentEncoding, JsonRequestBehavior behavior)
{
var result = new JsonNetResult()
{
Data = data,
ContentType = contentType,
ContentEncoding = contentEncoding,
JsonRequestBehavior = behavior
};
return result;
}
Désormais, tous les contrôleurs qui dérivent de votre contrôleur de base utiliseront automatiquement Json.NET pour sérialiser des objets en Json. Au passage, c’est toujours une bonne pratique de créer votre propre contrôleur au démarrage d’un projet (ou dans un Framework) pour pouvoir à tout moment influer sur le comportement de tous vos contrôleurs sans impacter votre code !
Enjoy
Julien
read more
28 Feb 2013 in
ASP.NET Web API
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 :
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
Julien
read more
06 Feb 2013 in
ASP.NET MVC
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
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 :
Enjoy
Julien
read more