Home - About me - Browse by categories

[ASP.NET MVC] Premiers pas avec les ASP.NET Web API - Partie 2

Dans mon article précédent, je vous expliquais comment mettre en place les nouveaux contrôleurs Web API pour exposer vos données de manière RESTful. Je m’étais arrêté à la partie ASP.NET MVC Web API pure. Dans cet article, je souhaite fournir un exemple d’interface graphique venant consommer ces APIs.

J’ai choisi de développer l’interface en HTML/jQuery pur au sein du même projet que celui hébergeant mon API contrôleur. Ce qu’il faut bien comprendre ici, c’est que j’aurai pu faire le choix de n’importe quelle technologie capable d’envoyer une requête HTTP et de traiter du JSON (du texte, au final) : Windows Phone, Windows 8, iPhone, PHP… Bref, n’importe quelle plateforme de développement suffisamment récente (ou presque!).

###

Définition de l’interface graphique

L’interface graphique que j’ai choisi est ultra simple :

<div style="margin: 20px">
## Consommation des WEB Api

### Liste des requêtes

<a id="showRequestsLink" title="afficher les requêtes" href="#">afficher les requêtes</a>

<div id="requestsList">

### Ajouter une requête

<div id="addFormContainer">
<form id="addRequestForm">
Titre : <input type="text" name="Title" id="Title" />
Description : <input type="text" name="Description" id="Description" />
<input type="hidden" name="Id" id="Id" value="" />
<input type="button" id="addRequestButton" value="Ajouter" />
</form>

Voilà ce que cela donne dans un navigateur :

image

Ensuite, tout se passe en ajax (et en HTTP!!).

Affichage de la liste des requêtes

Lorsque l’utilisateur clique sur le lien pour afficher la liste des requêtes, il est nécessaire de faire un GET sur l’URL /api/featurerequest, ce qui aura pour effet d’appeler l’action Get de l’ApiController développé dans l’article précédent.

function updateRequests() {
$.get("/api/featurerequests", function (requests) {
if (requests.length == 0) {
$("#requestsList").html("aucune requête dans la base de données

");
}
else {
$("#requestsList").html("");
$("#requestTemplate").tmpl(requests).appendTo("#requestsList");

handleLinksClicks();
}
});
}

$(document).ready(function () {

$("#showRequestsLink").click(function (evt) {
updateRequests();
});
});

L’appel en GET de l’url /api/featurerequests retourne directement un tableau d’objet JavaScript qui possède les mêmes propriétés que l’objet server FeatureRequest, celui-ci ayant automatiquement été sérialisé en JSON par le moteur Web API. Notez au passage que j’utilise ici le plugin jQuery tmpl, permettant d’effectuer un rendu à partir d’un template HTML. Vous pouvez récupérer directement ce plugin via la console Nuget de Visual Studio :

image

Voilà la structure du template que j’ai défini :

<script id="requestTemplate" type="text/html">
 - [Détail](#) | [Détail](#) | [Détail](#)

</script>

Note : chacune des propriétés de l’objet JSON peut être appelé à l’aide de la notation au sein du template ! (au passage, il s’agit d’un plugin jQuery développé par Microsoft image, on sent l’influence du binding XAML)

###

Ajouter/Mettre à jour une requête dans la base de données

Pour ajouter une nouvelle requête dans la base de données, il suffit de poster le formulaire sur l’URL /api/featurerequests. La requête sera alors automatiquement traitée par l’action qui lui correspond dans le contrôleur développé. Pour mettre à jour une requête, il suffit d’envoyer une requête HTTP PUT sur la même URL, avec les données du formulaire, sérialisées :

$("#addRequestButton").click(function (evt) {
evt.preventDefault();
var addRequestForm = $("#addRequestForm");
if ($("#Id").val() != "") {
$.ajax({
url: "/api/featurerequests/" + $("#Id").val(),
type: "PUT",
data: addRequestForm.serialize(),
success: function () {
updateRequests();
$("#Id").val("");
$("#Title").val("");
$("#Description").val("");
$("#addRequestButton").val("Ajouter");
},
error: function () {
alert("Erreur lors de la mise à jour");
}
});
}
else {
$.post("/api/featurerequests", addRequestForm.serialize())
.success(function (result) {
$("#Title").val("");
$("#Description").val("");
updateRequests();
})
.error(function () {
alert("Une erreur s'est produite!");
});
}
});

###

Récupérer les informations propres à une requête

Pour récupérer une information propre à une requête, il suffit d’envoyer une requête HTTP GET à l’url /api/featurerequests/id :

$(".request-detail-link").click(function () {
var dataId = $(this).data("id");
var url = "/api/featurerequests/" + dataId;
$.get(url, function (jsonResult) {
alert(JSON.stringify(jsonResult));
});
});

Suppression d’une requête

Cette fonctionnalité est similaire à la récupération d’informations propres à une requête, à la différence près qu’il faut envoyer une requête HTTP DELETE :

$(".request-delete-link").click(function () {
var dataId = $(this).data("id");
if (confirm("Êtes-vous sûr de supprimer cette requête ?")) {
$.ajax({
url: "/api/featurerequests/" + dataId,
type: "DELETE",
success: function () {
alert("La requête a été supprimée");
updateRequests();
}
});
}
});

Conclusion / Résumé

L’important ici n’était pas tant le code JavaScript / HTML, mais plutôt de voir comment consommer très simplement les données à l’aide de requête HTTP et donc potentiellement depuis n’importe quelle plateforme récente image

Ci-dessous un résumé des différentes requêtes / verbes et de leurs actions respectives dans le contrôleur :

  • GET /api/featurerequests : GetFeatureRequests –> retourne la liste des requêtes

  • GET /api/featurerequests/id : GetFeatureRequest –> retourne une requête en particulier

  • POST /api/featurerequests : PostFeatureRequest –> ajouter une requête dans la base

  • PUT /api/featurerequests : PutFeatureRequest –> mise à jour d’une requête dans la base

  • DELETE /api/featurerequests/id : DeleteFeatureRequest –> suppression d’une requête de la base

Vous pouvez récupérer le code source ici.

A bientôt ! image

read more

[ASP.NET MVC] Premiers pas avec les ASP.NET Web API - Partie 1

Introduction

ASP.NET MVC 4 a été rendu disponible par Microsoft il y a peu et apporte son lot de nouveautés. Parmi celles-ci, on constate l’apparition d’une nouvelle brique : ASP.NET Web API. Il s’agit d’un Framework qui permet de développer rapidement et simplement des services HTTP pour la mise à disposition de données cross-plateforme et le développement d’application RESTful !

Dans ce post, je vous propose de créer une application reposant sur ce Framework et permettant de poster des requêtes de fonctionnalités / feedback pour un produit, comme le fait par exemple Uservoice.com (en beaucoup plus simple, évidemment).

Avant de commencer, il vous faut installer ASP.NET MVC 4. Pour cela, rendez-vous sur cette page et cliquez sur Install ASP.NET MVC 4.

Note : il est possible de développer des applications ASP.NET MVC 4 / ASP.NET Web API aussi bien dans Visual Studio 2010 que 2012, avec le .NET Framework 4.0 ou 4.5. Pour ma part, j’utiliserai Visual Studio 2012 et le .NET Framework 4.5.

Création du projet

Dans Visual Studio, créez un nouveau projet Web, de type ASP.NET MVC 4 Web Application :

image

Dans la fenêtre qui suit, choisissez un modèle de projet de type Web API. Gardez Razor comme moteur de vue, et validez la création du projet :

image

Analyse de la solution

Avant d’ajouter la moindre ligne de code au projet, nous allons nous intéresser à ce qui a été généré par Visual Studio. S’il s’agit de votre premier projet ASP.NET MVC 4, vous devez remarquer qu’un nouveau dossier App_Start est présent dès la création du projet. Celui-ci contient un ensemble de classes utilisées pour l’initialisation de l’application, dans la méthode Application_Start du fichier Global.asax!

Visual Studio a également créé deux contrôleurs :

HomeController : il s’agit d’un contrôleur ASP.NET MVC classique, composé d’une action Index pour renvoyer l’utilisateur sur la page d’accueil lorsqu’il se connecte sur le site. C’est ce contrôleur et cette action qui sont appelés lorsque vous lancez le site web en débogue.

ValuesController : il s’agit d’un contrôleur ASP.NET Web API d’exemple. Celui-ci est composé de 5 méthodes permettant d’adresser différentes actions : récupérer une liste de valeur, récupérer une valeur par son id, ajouter une valeur, modifier une valeur, supprimer une valeur. Remarquez qu’à chacune de ces actions est associé un verbe HTTP : GET pour la récupération, POST pour l’ajout, PUT pour la modification et DELETE pour la suppression.

// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}

// GET api/values/5
public string Get(int id)
{
return "value";
}

// POST api/values
public void Post([FromBody]string value)
{
}

// PUT api/values/5
public void Put(int id, [FromBody]string value)
{
}

// DELETE api/values/5
public void Delete(int id)
{
}

Lancez l’application en débogue en appuyant sur F5.

Dans la barre d’adresse du navigateur, connectez-vous sur [/api/values”>http://localhost:/api/values](http://localhost:/api/values) :

image

Vous pourrez alors constater que votre navigateur vous propose de télécharger un fichier .json contenant la liste de valeurs retournées par la méthodes Get du contrôleur ValuesController, au format JSON : [“value1”,”value2”].

ASP.NET Web API utilise le même principe de routes qu’ASP.NET MVC, par défaut, toutes les URLs contenant /api/quelque chose sont redirigé vers un contrôleur de type ApiController. Ensuite, la résolution de l’action (méthode) et de ses éventuels paramètres fonctionne comme ASP.NET MVC. Le verbe HTTP utilisé est également très important puisque déterminant la méthode à appeler.

La route api par défaut est définie dans le fichier WebApiConfig du dossier** App_Start** et est initialisée dans le fichier Global.asax, au démarrage de l’application :

public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}

Création du modèle

Dans cette partie, nous allons créer le modèle chargé de représenter une requête de fonctionnalité dans l’application d’exemple. Faites un clic droit sur le dossier Models dans l’explorateur de solution et ajoutez une classe FeatureRequest :

public class FeatureRequest
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
}

Compilez simplement l’application afin que les outils ASP.NET MVC puissent détecter cette nouvelle classe dans la suite.

Ajout d’un nouveau contrôleur Web API

Faites un clic droit sur le dossier Controllers dans l’explorateur de solution et choisissez d’ajouter un nouveau contrôleur. Dans la fenêtre qui s’ouvre, nommez le FeatureRequestsController. Dans les options de scaffholding, choisissez le template API controller with read/write actions, using Entity Framework, le Model FeatureRequest et de créer un nouveau DataContext EntityFramework pour accéder aux données :

image

Nommez votre contexte EF dans la fenêtre qui s’affiche :

image

Validez en cliquant sur OK puis Add.

Les outils ASP.NET MVC génèrent alors deux classes pour vous : FeatureRequestController.cs (dossier Controllers) et FeatureRequestWebAPIContext.cs (dossier Models). La première représente le nouveau contrôleur alors que la seconde représente le contexte Entity Framework pour l’accès aux données. Celui-ci est utilisé dans le contrôleurs, dans les différentes méthodes générées pour la récupération de la liste des requêtes, d’une requête en particulier, la création d’une requête, la modification d’une requête et enfin la suppression d’une requête.

ublic class FeatureRequestsController : ApiController
{
private FeatureRequestWebAPIContext db = new FeatureRequestWebAPIContext();

// GET api/FeatureRequests
public IEnumerable<FeatureRequest> GetFeatureRequests()
{
return db.FeatureRequests.AsEnumerable();
}

// GET api/FeatureRequests/5
public FeatureRequest GetFeatureRequest(int id)
{
FeatureRequest featurerequest = db.FeatureRequests.Find(id);
if (featurerequest == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}

return featurerequest;
}

// PUT api/FeatureRequests/5
public HttpResponseMessage PutFeatureRequest(int id, FeatureRequest featurerequest)
{
if (ModelState.IsValid && id == featurerequest.Id)
{
db.Entry(featurerequest).State = EntityState.Modified;

try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}

return Request.CreateResponse(HttpStatusCode.OK);
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}

// POST api/FeatureRequests
public HttpResponseMessage PostFeatureRequest(FeatureRequest featurerequest)
{
if (ModelState.IsValid)
{
db.FeatureRequests.Add(featurerequest);
db.SaveChanges();

HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, featurerequest);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = featurerequest.Id }));
return response;
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}

// DELETE api/FeatureRequests/5
public HttpResponseMessage DeleteFeatureRequest(int id)
{
FeatureRequest featurerequest = db.FeatureRequests.Find(id);
if (featurerequest == null)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}

db.FeatureRequests.Remove(featurerequest);

try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}

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

protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}

Note : remarquez à nouveau que le verbe HTTP utilisé entre en jeu dans la convention de nommage des actions du contrôleur !

Si vous exécutez le site web en débogue et vous connectez à l’adresse [/api/featurerequests”>http://localhost:/api/featurerequests](http://localhost:/api/featurerequests), votre navigateur devrait vous proposer de télécharger un fichier JSON contenant la liste des requêtes de fonctionnalités (vide pour le moment, évidemment !) :

image

Conclusion

Dans cette première partie, vous avez mis en place la structure de l’application ASP.NET Web API pour la gestion des requêtes de fonctionnalités.

Dans le prochain article, nous développeront l’interface graphique qui sera en charge de consommer ce service RESTful !

A bientôt image

read more

[ASP.NET MVC] Bonnes pratiques : création d’un Bootstrapper ou comment isoler l’initialisation d’une application

Dans ce post, je souhaite revenir sur une bonne pratique que Rui et moi-même avons évoqué lors de notre session sur Rui le mois dernier : l’isolation de l’initialisation d’une application ASP.NET MVC.

En effet, le template par défaut de création de projet ASP.NET MVC de Visual Studio concentre tout le code dans un seul et même projet ce qui peut poser des problèmes de maintenabilité et/ou d’organisation, notamment sur un projet conséquent.

L’idée est de créer un Bootstrapper, c’est à dire une entité qui soit capable d’initialiser l’application, charger les différentes dépendances via un conteneur IoC (Unity, dans cet exemple), enregistrer les différentes routes, areas (etc…) qui composent l’application.

Voilà l’architecture globale de ma solution d’exemple (sources disponibles en bas de page!) :

image

  • Sample.Bootstrapper est une bibliothèque de classe qui référence : System.Web, System.Web.Mvc et System.Web.Routing et Microsoft.Practices.Unity, afin d’être en capacité d’initialiser l’application
  • Sample.Services est une bibliothèque de classes qui défini un service tout simple, représenté par une interface et une implémentation concrète.
  • Sample.Web est l’application ASP.NET MVC

Vous l’avez surement compris, l’essentiel du travail va se concentrer dans le projet Sample.Bootstrapper.

En premier lieu, le UnityBootstrapper : il s’agit d’une classe qui expose un conteneur Unity au reste de l’application, c’est celui-ci qui est utilisé pour la résolution des dépendances au runtime :

public class UnityBootstrapper
{
private UnityBootstrapper()
{

}

private static void InitializeContainer(IUnityContainer container)
{
container.RegisterType<ISampleService, LoremIpsumSampleService>();
}

private static readonly object _syncRoot = new object();
private static IUnityContainer _container;

public static IUnityContainer Container
{
get
{
if (_container == null)
{
lock (_syncRoot)
{
if (_container == null)
{
_container = new UnityContainer();
InitializeContainer(_container);
}
}
}

return _container;
}
}
}

On expose ici le conteneur Unity racine de l’application sous la forme d’un Singleton. Les registers sont fait dans le code ou dans le fichier Web.config, selon le besoin.

Afin de pouvoir utiliser l’injection de dépendance dans l’application, il faut configurer le moteur MVC pour utiliser ce conteneur Unity lors de la résolution des contrôleur. Pour cela, on passe par une implémentation de l’interface IControllerActivator, ici la classe UnityControllerActivator :

public class UnityControllerActivator : IControllerActivator
{
private readonly IUnityContainer _container;
public UnityControllerActivator(IUnityContainer unityContainer)
{
_container = unityContainer;
}

public IController Create(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
return _container.Resolve(controllerType) as IController;
}
}

Il ne reste plus qu’à définir la bonne controller factory afin que le UnityControllerActivator soit utilisé.

Avant cela, j’attire votre attention sur la dernière classe qui se trouve dans le projet Sample.Bootstrapper : MvcApplication. Il s’agit en réalité de la classe d’application (qui dérive de System.Web.HttpApplication) et qui se trouve par défaut liée au fichier Global.asax, lors de la création du projet. Le fait de déporter cette classe dans un projet externe permet de bien isoler le chargement de l’application et d’y définir les différentes routes, areas ou comportement du Framework, par exemple, demander au ControllerBuilder d’aller utiliser notre propre IControllerActivator :

public class MvcApplication : HttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}

public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapRoute(
"Default",
"{controller}/{action}",
new { controller = "Sample", action = "Index" }
);

}

protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();

RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);

var controllerFactory = new DefaultControllerFactory(
new UnityControllerActivator(UnityBootstrapper.Container));
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
}
}

Il ne reste plus qu’à lier cette classe au fichier Global.asax afin qu’elle soit prise en compte au démarrage de l’application. Pour cela, il suffit d’ouvrir le fichier à l’aide de l’éditeur XML de Visual Studio, et de modifier la définition de la classe d’application :

<%@ Application Codebehind="Global.asax.cs" Inherits="Sample.Bootstrapper.MvcApplication" Language="C#" %>

Et voilà, le tour est joué! Les sources sont disponibles ici : http://juliencorioland.blob.core.windows.net/publicfiles/Sample-Bootstrapper.zip

A bientôt image

Julien

read more

[Techdays 2012] Webcasts en ligne, slides et codes source !

Vous n’êtes certainement pas passé à côté de cette information : les webcasts de l’édition 2012 des Microsoft Techdays sont en ligne ! Il est possible de les visionner à cette adresse : http://www.microsoft.com/france/mstechdays/programmes/parcours.aspx.

J’en profite pour vous donner les pointeurs vers les webcasts, slides et codes source des sessions que j’ai eu le plaisir d’animer cette année :

Architecture, bonnes pratiques et recettes pour la réussite de vos projets ASP.NET MVC

La dure lutte du développeur : 10 trucs pratiques pour une application mobile bien léchée

Enseigner les technologies Microsoft, un exemple avec Windows Phone

Bon visionnage !

Julien

read more

Azure SDK on Windows 8 Consumer preview : there was an error attaching the debugger…

When I was trying to debug a Windows Azure application in Visual Studio 2010 on Windows 8 Consumer Preview, I was getting the following error :

image

If you encounter this error, open the Control Panel, go to the Programs section and click the Turn Windows features on or off link. In the Windows features, check that the Internet Information Service Hostable Web Core feature is installed on your machine. If not, install it :

image

It solves my problem and now I can debug Windows Azure applications without any error ! It seems that the Web PI 4.0 preview does not detect this dependency while installing the Windows Azure SDK…

Hope this helps image !

Julien

read more