Quantcast

comments edit

The source repository for this article is on Github.

Le routing

Une nouveauté dans Web API 2.0 est le routing par attribut. Auparavant il était nécessaire de déclarer les routes selon des conventions :

routes.MapHttpRoute(
    name: "API Default",
    routeTemplate: "api/{controller}/{action}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

Voici le même exemple en utilisant les attributs :

[RoutePrefix("api/names")]
public class NamesController : ApiController
{
	[HttpGet]
	[Route("{prefix}")]
	public IHttpActionResult GetNames(string prefix)
	{
		var names = this.dataProvider.GetFirstNames().Where(x => x.StartsWith(prefix));

		return this.Ok(names);
	}
}

Il n’est plus nécessaire de conformer toutes les routes à des conventions qui pouvaient devenir fastidieuses à maintenir, comme par exemple les routes que l’on peut rencontrer dans une API de type REST :

  • /clients
  • /clients/123
  • /clients/123/orders
  • /clients/123/orders/456

La serialisation

La sérialisation se fait de manière transparente : en fonction du content-type de la requête HTTP, la réponse sera sérialisée en JSON ou en XML par défaut. Des points d’entrée sont présents dans Web API pour personnaliser ce comportement : http://www.asp.net/web-api/overview/formats-and-model-binding/media-formatters

Les filtres

Tout comme ASP.NET MVC, les filtres permettent l’interception des actions des controlleurs, avant ou après l’exécution de celles ci.

Les usages courants sont :

  • la sécurisation des actions
  • la journalisation
  • la modification des en-têtes http avant envoi de la réponse
public class CustomHeaderAttribute : ActionFilterAttribute
{
	public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
	{
		actionExecutedContext.Response.Headers.Add("Hello-Header", "Hello world !");
	}
}

Les tests unitaires

La séparation des rôles apportée par Web API permet de facilement mettre en place des tests unitaires, avec ou sans mocking.

Il est également possible de faire des tests de bout en bout : Tests unitaires avec WebApi, Castle Windsor et OWIN

comments edit

The source repository for this article is on Github.

Wanting to change a bit, I had a look at Xamarin. First issue, have my HTC One X working with the debugger.

I had to :

; HTC One X
%SingleAdbInterface% = USB_Install, USB\VID_0BB4&PID_0CED
%CompositeAdbInterface% = USB_Install, USB\VID_0BB4&PID_0CED&MI_01
  • Execute shutdown /r /o
  • After reboot go to Troubleshooting > Advanced > Startup options > Reboot > F7
  • Connect the phone
  • Activate USB debugging in Android
  • Run devmgmt.msc
  • Right click Android phone, then Update Driver > Find a driver on my computer > Choose in a driver list... > Disk
  • Insert path to android_winusb.inf
  • Accept security warning

Execute adb devices to check the phone presence :

C:\adt-bundle-windows-x86_64-20131030\sdk\platform-tools>adb devices
* daemon not running. starting it now on port 5037 *
* daemon started successfully *
List of devices attached
HTxxxxxxxxxx    device

Easy, thanks to following links:

comments edit

The source repository for this article is on Github.

Un petit peu d’archéologie

  • 1996 : ASP
  • 2002 : ASP.NET (Visual Studio 2003)
  • 2005 : ASP.NET 2.0 (Visual Studio 2005
  • 2007 : ASP.NET 3.5 (Visual Studio 2008)
  • 2009 : ASP.NET MVC 1
  • 2010 : ASP.NET MVC 2
  • 2011 : ASP.NET MVC 3
  • 2012 : ASP.NET 4.5 (Visual Studio 2012)
  • 2012 : ASP.NET MVC 4 + Web API 1
  • 2013 : ASP.NET MVC 5 + Web API 2

Le point interessant a noter, est que depuis 1996, le nombre d’utilisateurs d’internet a radicalement augmenté :

Croissance des utilisateurs d'internet, par 100 habitants Source : Wikipedia

De ce fait, les frameworks permettant de développer les applications ont donc du suivre un rythme soutenu. Jusqu’en 2009, Microsoft mettait a jour son framework ASP.NET en même temps que Visual Studio, l’outil de développement. ASP.NET MVC change la donne, car il est distribué en tant que complément, et du coup ne subit par les contraintes temporelles de Visual Studio. Depuis sa sortie, le framework a été mis à jour à raison de une fois par an.

En meme temps que le nombre d’utilisateurs d’application web augmente, leurs attentes envers ses applications augmentent également : interfaces plus riches, plus de réactivité…

Oui, et ?

L’avènement des framework tels ASP.NET ont permi de simplifier une bonne partie du code pour développer des applications de plus en plus dynamiques, mais dans une certaine limite, tant d’un point de vue de maintenabilité que de performance.

Traditionnelement, les interactions navigateur serveur suivent deux “canaux” :

  • un flux html a chaque changement de page
  • un ou plusieurs flux AJAX.

Avec ASP.NET, les services fournissant les données a des appels ajax peuvent être implémentés de différentes manières :

  • services asmx / webmethods : obsolete
  • IHttpHandlers : implémentation “manuelle” fastidieuse
  • Services WCF : implémentation complexe dans le cas de services non SOA

ASP.NET MVC a apporté deux élements clés pour simplifier ce probleme

  • le routing, pour découpler facilement la localisation du fichier source représentant le service, et sont adresse http
  • le ModelbBinding, pour simplifier la sérialisation/désérialisation des données dans le flux HTTP

Et Web API dans tout ca ?

Pendant ce temps la, d’autres patterns et habitudes commencaient a prendre de l’ampleur…

L’ouverture des données et des API des applications web, donnant naissance aux premiers mashups : mix de données issues de différents services pour en fournir un nouveau, comme par exemple http://www.housingmaps.com/, qui reprend les annonces immobilieres de Craigslist, et les places sur une carte Google maps. Et c’etait en 2005 ! REpresentational State Transfer (ou REST), définit une sémantique pour exposer des services sur le protocole HTTP. Enfin, le templating client a également pris de l’essor, toujours pour améliorer la réactivité de nos chères applications.

Du coup, certains utilisateurs de ASP.NET MVC ont souhaité pour disposer des capacités de Routing, ModelBinding, etc, de ASP NET MVC, sans utiliser la partie templating. D’où la naissance de ASP.NET Web API.

Et concrètement, ca fait quoi ?

La suite, au prochain épisode !

comments edit

The source repository for this article is on Github.

Une des grosses forces de ASP.NET Web API est sa modularité. Celle ci facilite la mise en place des principes SOLID, le S, pour Separation of Concerns, en particulier.

Pour la suite de l’article, nous allons tester un controller assez basique, dépendant d’un service injecté par le constructeur :

[RoutePrefix("api/names")]
public class NamesController : ApiController
{
	private const string PrefixRegex = "[a-zA-Z]+";
	
	private readonly IDataProvider dataProvider;

	public NamesController(IDataProvider dataProvider)
	{
		this.dataProvider = dataProvider;
	}

	[Route("{prefix}")]
	public IHttpActionResult GetNames(string prefix)
	{
		var names = this.dataProvider.GetFirstNames().Where(x => x.StartsWith(prefix));

		if (!Regex.IsMatch(prefix, PrefixRegex))
		{
			return StatusCode(HttpStatusCode.BadRequest);
		}

		if (!names.Any())
		{
			return NotFound();
		}

		return Ok(names);
	}
}

Afin de pouvoir utiliser ce controller dans Web API, il faut remplacer le DependencyResolver par défaut, par une implémentation spécifique à Windsor. Habituellement, cet enregistrement se passe dans une classe de configuration, appelée dans la méthode MvcApplication.Application_Start. Les différents composants seront crées et nettoyés à chaque requete HTTP.

public class WindsorConfig
{
	public static void Register<T>(HttpConfiguration config) where T : IScopeAccessor, new()
	{
		var container = new WindsorContainer();

		config.DependencyResolver = new WindsorResolver(container);

		// when we begin registering many components, we should use installers here
		container.Register(Component.For<IDataProvider, DataProvider>().LifestylePerWebRequest());
		container.Register(Component.For<NamesController>().LifestylePerWebRequest());
		container.Register(Component.For<ClientsController>().LifestylePerWebRequest());
	}
}

public class MvcApplication : System.Web.HttpApplication
{
	protected void Application_Start()
	{
		WebApiConfig.Register(GlobalConfiguration.Configuration);
		BundleConfig.Register(BundleTable.Bundles);
		WindsorConfig.Register(GlobalConfiguration.Configuration);

		GlobalConfiguration.Configuration.EnsureInitialized();
	}
}

Une fois cette infrastructure en place, nous pouvons appeler normalement notre API REST.

Supposons que nous souhaitions maintenant lancer des tests d’intégration, afin de valider le bon fonctionnement de cette API. Il est possible d’héberger une application WebAPI dans un process OWIN, sans serveur IIS. Ceci devient extrêmement pratique dans le cadre de tests automatisés.

Nous pouvons donc écrire un premier test :

[TestClass]
public class NamesControllerTest : ControllerTestBase
{
	protected static readonly string baseAddress = "http://localhost:9000/";
	protected static IDisposable app;

	/// <summary>
	/// Start the self hosted server once per assembly
	/// </summary>
	/// <param name="context"></param>
	[AssemblyInitialize]
	public static void AssemblyInitialize(TestContext context)
	{
		app = WebApp.Start<Startup>(baseAddress);
	}

	/// <summary>
	/// Do not forget to clean up !
	/// </summary>
	[AssemblyCleanup]
	public static void AssemblyCleanup()
	{
		if (app != null)
		{
			app.Dispose();
		}
	}
	
	[TestMethod]
	public void Valid_prefix_must_return_OK()
	{
		using (var client = new HttpClient())
		{
			var response = client.GetAsync("http://localhost:9000/api/names/A").Result;

			Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
		}
	}
}

public class Startup
{
	public void Configuration(IAppBuilder appBuilder)
	{
		var config = new HttpConfiguration();

		var container = WindsorConfig.Register<LifetimeScopeAccessor>(config);
		WebApiConfig.Register(config);

		appBuilder.Use<OwinRequestLifeTimeManager>(container);
		appBuilder.UseWebApi(config);
		
		config.EnsureInitialized();
	}
}

Le point interessant est la classe Startup : celle ci agira de manière similaire au Global.asax, dans les deux cas nous initialisons WebApi et Windsor. Les différences étant :

  • la création de l’objet HttpConfiguration est à notre charge dans le cas d’un hébergement OWIN Self-hosted
  • le IScopeAccessor est différent, le serveur OWIN Self-hosted ne disposant pas de la notion d’HttpModule

Pour pouvoir tout de même limiter la durée de vie des composants enregistrés dans Windsor, il a fallu implémenter un middleware OWIN :

/// <remarks>
/// Inspired from : https://github.com/castleproject/Windsor/blob/master/src/Castle.Windsor/MicroKernel/Lifestyle/PerWebRequestLifestyleModule.cs
/// </remarks>
public class OwinRequestLifeTimeManager : OwinMiddleware
{
	private readonly IWindsorContainer container;

	public OwinRequestLifeTimeManager(OwinMiddleware next, IWindsorContainer container)
		: base(next)
	{
		this.container = container;
	}

	public override Task Invoke(IOwinContext context)
	{
		using (new CallContextLifetimeScope(container))
		{
			// subsequent middlewares are executed inside this scope
			return Next.Invoke(context);
		}
	}
}

Ce middleware OWIN encapsule l’execution des middleware suivants (dont Web API) dans un scope spécifique, afin de “simuler” le comportement du PerWebRequestLifestyle.

Du coup maintenant nous pouvons exécuter de jolis tests d’intégration automatisés, sans passer par IIS.

comments edit

The source repository for this article is on Github.

Avant de rentrer dans le vif du sujet, petit rappel sur le fonctionnement du cache du navigateur.

Lorsque le navigateur demande une ressource (index.html par exemple) au serveur, celui ci lui renvoie plusieurs éléments :

  • un statut HTTP (200 SUCCESS ici)
  • le contenu du fichier index.html
  • un ensemble d’en-têtes http

Parmi ces en-têtes, on retrouvera (en fonction de la configuration du serveur) :

  • un ETAG, qui est un checksum calculé selon le contenu renvoyé au navigateur
  • une date d’expiration, qui indique au navigateur la durée de vie de la ressource demandée

Il est à noter que les valeurs dans l’en-tête sont purement prescriptives, charge au navigateur de les respecter.

Lorsque le navigateur refait une requête au serveur pour le fichier index.html, il adjoindra également des en-têtes à sa requête. L’en-tête ETAG permettra de répondre au navigateur par un statut 304 NOT MODIFIED, dans le cas ou le fichier n’aurait pas changé.

L’en-tête date d’expiration permet au navigateur d’éviter d’envoyer des requêtes au serveur : tant que la date d’expiration n’est pas atteinte, le navigateur utilise la copie en cache.

Les templates AngularJS sont chargés au travers du service $http, qui se base sur xmlHttpRequest, qui lui même bénéficie du cache du navigateur.

Imaginons le scénario suivant :

  • le matin a 8h, le navigateur du client A demande le fichier index.html
  • le serveur lui renvoie, avec un ETAG “ABCD”, et une date d’expiration fixée au lendemain 8h
  • lors de l’utilisation de l’application, le navigateur va se baser sur la version en cache du fichier, tant que la date d’expiration est atteinte
  • a 12h, une nouvelle version de l’application est mise en ligne

Jusqu’au lendemain 8h, le client A utilisera une version potentiellement obsolète du fichier index.html.

Pour eviter cela, je vais mettre en place un mécanisme permettant d’assurer le bon fonctionnement du cache, tout en utilisant les derniers fichiers disponibles de l’application.

Dans un premier temps, il faut fournir un numéro identifiant l’application déployée. J’ai choisi d’utiliser la date de création d’un assembly du projet Web. Pour cela, j’ai intégré une simple balise script déclarant une constante AngularJs :

<script type="text/javascript"> 
    angular.module('SampleApplication.Config', []) 
        .constant('SampleApplicationVersion', '<%: Version %>'); 
</script>

La suite de l’implémentation est aisée, grâce au système de HttpInterceptors fourni par AngularJs. Ces HttpInterceptors permettent de modifier les requêtes envoyées et reçues par le service $http.

.factory('SmartCacheInterceptor', ['$q', 'SampleApplicationVersion', function ($q, SampleApplicationVersion) { 
    return { 
        request: function (config) { 
            if (config.url.indexOf(".htm") > -1) { 
                var separator = config.url.indexOf("?") === -1 ? "?" : "&"; 
                config.url = config.url + separator + "v=" + SampleApplicationVersion; 
            } 
            return config || $q.when(config); 
        } 
    }; 
}]);

Voici les résultats dans fiddler. Les requêtes sur l’url “/” correspondent a un rafraichissement de la page du navigateur. Le serveur web utilisé est IIS Express, sans paramétrage spécifique.

Capture fiddler