ASP.NET - Tutoriel sur OWIN

| Léonard LABAT (Soat) | Soat |

Les annonces récentes concernant ASP.NET vNext sont telles que nous savons maintenant que notre usage des technologies Web de Microsoft va prendre un nouvel essor et radicalement se redynamiser. Je souhaite revenir sur ces annonces dans mes prochains billets de blog, mais, avant de commencer, je voudrais revenir sur la genèse de ces annonces et sur deux concepts moteurs annoncés il y a maintenant un an et qui sont pourtant encore trop méconnus des développeurs : OWIN et Katana.

N'hésitez pas à donner vos avis sur cet article : Commentez

- Titre : ASP.NET - Tutoriel sur OWIN

- Auteurs : Léonard LABAT (Soat), Soat

- Parution : 1 octobre 2014

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2014 Léonard LABAT. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Pourquoi OWIN ?

- II. Qu'est-ce qu'OWIN ?

- II-A. Le dictionnaire d'environnement

- II-B. Le délégué applicatif

- III. OWIN plus concrètement…

- IV. Pour résumer

- V. Remerciements

I - Pourquoi OWIN ?

Pour pouvoir expliquer au mieux ce qu'est OWIN et comment Microsoft et sa communauté de développeurs y sont arrivés, il convient tout d'abord de revenir sur l'évolution des technologies ASP.NET de ces dernières années.

Historiquement, le Web Microsoft était réalisé en ASP. Une technologie de scripting où l'on pouvait mixer allègrement script ASP et markup HTML. L'arrivée des premières versions du Framework .NET a été l'occasion pour Microsoft de faire avancer le développement Web avec ASP vers une nouvelle ère en le combinant à un langage orienté objet (C# ou VB.NET) et en offrant la possibilité aux développeurs d'utiliser la BCL. C'est ainsi que ASP.NET WebForms est né.

Depuis maintenant 13 ans, le dénominateur commun de tous les projets ASP.NET est l'assembly System.Web. Cette dernière présente pourtant plusieurs inconvénients.

- Elle a été conçue à la base pour ASP.NET WebForms. Les nouvelles technologies Web de Microsoft (ASP.NET MVC, ASP.NET WebAPI ou encore SignalR) ne suivent plus forcément ce modèle historique. En conséquence, chacun de ces nouveaux Frameworks doit s'adapter à l'historique de System.Web et inversement.

- Elle est monolithique, trop lourde et peu flexible. La tendance est au modulaire, or, son vieil âge (13 ans à la date de l'écriture de ce billet) lui impose le poids des années et la nécessité du support des applicatifs les plus anciens comme les plus récents.

- Elle contient la logique nécessaire au traitement des requêtes HTTP (pipeline HTTP) et est fortement couplée à un serveur en particulier : IIS.

- Elle est distribuée comme partie intégrante du Framework .NET. Cela signifie qu'elle n'est mise à jour qu'avec les releases du Framework .NET et donc, qu'il peut s'écouler plusieurs années entre chaque nouvelle version. Dans le monde du développement Web qui a explosé ces dernières années, c'est un désavantage face aux technologies concurrentes.

Pour répondre à ces différentes problématiques, Microsoft a profité de l'engouement autour d'ASP.NET MVC et des autres nouvelles technologies ASP.NET pour modifier sa stratégie autour de l'assemblage System.Web. Celle qui nous intéresse aujourd'hui est apparue dans un premier temps avec ASP.NET Web API. En effet, le Framework que nous pouvons utiliser pour construire des API REST est, dès sa première version, utilisable sans IIS (à l'époque, j'en avais parlé dans un billet intitulé : Héberger des WEB API hors d'un site ASP.NET MVC).

En effet, ASP.NET Web API est en fait livré sous la forme de plusieurs modules, le cœur du Framework étant clairement séparé de celui nécessaire à l'hébergement de l'application. Le socle d'ASP.NET Web API ne référence pas System.Web, n'utilise aucun de ses types et n'est donc pas fortement couplé à IIS. En pratique, il est même très facile d'héberger une application Web API dans une application console ou un service Windows.

Nous avons donc avec Web API la première pierre vers un nouveau mode de pensée pour le développement et l'hébergement d'une application sur son serveur : une application doit être fabriquée en assemblant des composants les plus légers possible, adaptés au besoin de l'application (et pas plus), et surtout, capable de s'adapter à n'importe quel type de serveur d'application sans être couplé explicitement à IIS. Microsoft y est parvenue avec ASP.NET Web API, il fallait ensuite le généraliser à ses autres technologies… Et c'est à ce moment qu'OWIN entre en scène.

II - Qu'est-ce qu'OWIN ?

OWIN - Open Web Interface for .NET. Ce nom représente en fait un standard, une norme, qui définit une couche d'abstraction entre le code de vos applications et les serveurs d'applications. Le but étant clairement de pouvoir porter une application d'un serveur pour l'héberger vers un autre type de serveur. Une application écrite en respectant les standards définis par OWIN ne sera plus fortement couplée à IIS.

Lorsque vous créez une application ASP.NET MVC (c'est surtout vrai pour ASP.NET MVC 4 et les versions précédentes), vous pouvez tout de suite constater le très fort couplage entre votre application et IIS. En effet, à peine le wizard de création de projet fermé, vous vous retrouvez instantanément avec un tas de références vers des assemblages System.Web.* et des modules et gestionnaires HTTP inscrits dans un fichier de configuration web.config.

Ainsi, avec OWIN (et avec les nouvelles annonces que Microsoft a faites sur ASP.NET vNext), lors de la création d'une nouvelle application, le but n'est pas de partir de quelque chose déjà fortement couplé à IIS, mais plutôt de partir du plus strict minimum et d'ajouter au fur et à mesure les fonctionnalités réellement requises par l'application. C'est une approche que l'on peut également trouver dans d'autres technologies Web à la mode, node.js ou Rack par exemple.

Toutes les informations sur OWIN sont disponibles sur la page officielle du projet. Retenez bien qu'OWIN n'est qu'une spécification et pas une implémentation.

La spécification est assez simple et n'est constituée que de deux éléments importants.

II-A - Le dictionnaire d'environnement

Le premier de ces deux éléments est le dictionnaire d'environnement. Il s'agit tout simplement d'une structure de données (clé/valeur) qui permet d'échanger des informations entre le serveur et votre application. C'est via cette structure que le serveur va passer à l'application les informations sur les requêtes HTTP entrantes. Et c'est également au travers de cette structure que l'application va pouvoir générer une réponse HTTP et la communiquer au serveur pour que celui-ci se charge de l'envoyer à son client.

OWIN définit également les différentes clés qui doivent obligatoirement être passées entre serveur et applicatif pour définir une requête ou une réponse HTTP. Ces différentes clés sont toutes préfixées de owin. Citons par exemple owin.RequestMethod qui indique le verbe HTTP utilisé par la requête, ou encore owin.RequestQueryString qui est destiné à contenir les différents paramètres passés à l'application via les QueryString (c'est-à-dire sous la forme param1=value1&param2=value2).

Il est tout à fait possible de créer un niveau d'abstraction au-dessus de ce dictionnaire (qui serait difficilement utilisable en l'état). Katana par exemple propose des interfaces qui encapsulent les éléments nécessaires au bon traitement des requêtes ou des réponses HTTP. Ci-dessous par exemple, la signature de l'interface IOwinRequest. Techniquement parlant, elle est implémentée par une classe OwinRequest qui se contente d'aller chercher les informations dans le dictionnaire (et donc en utilisant les clés owin.xxx).

public interface IOwinRequest
{
T Get<T>(string key);
Task<IFormCollection> ReadFormAsync();
IOwinRequest Set<T>(string key, T value);

string Accept { get; set; }
Stream Body { get; set; }
string CacheControl { get; set; }
CancellationToken CallCancelled { get; set; }
string ContentType { get; set; }
IOwinContext Context { get; }
RequestCookieCollection Cookies { get; }
IDictionary<string, object> Environment { get; }
IHeaderDictionary Headers { get; }
HostString Host { get; set; }
bool IsSecure { get; }
string LocalIpAddress { get; set; }
int? LocalPort { get; set; }
string MediaType { get; set; }
string Method { get; set; }
PathString Path { get; set; }
PathString PathBase { get; set; }
string Protocol { get; set; }
IReadableStringCollection Query { get; }
Microsoft.Owin.QueryString QueryString { get; set; }
string RemoteIpAddress { get; set; }
int? RemotePort { get; set; }
string Scheme { get; set; }
System.Uri Uri { get; }
IPrincipal User { get; set; }
}

II-B - Le délégué applicatif

Le deuxième élément d'OWIN est le délégué applicatif. Il s'agit simplement de la signature d'une fonction qui peut être utilisée comme interface entre un serveur et un applicatif. Ce délégué est sous la forme Func<IDictionary<string, object>, Task>

Il s'agit donc d'une fonction qui prend un dictionnaire d'environnement en paramètre et qui doit retourner une Task.

Il est toutefois à noter qu'OWIN est une spécification. La signature présentée ici utilise Func et Task qui sont des éléments assez spécifiques et pas forcément présents dans tous les langages disponibles au travers de la CLR. Une implémentation d'OWIN réalisée en F# pourrait très bien utiliser un FSharpFunc. Le plus important est de fournir une signature sous la forme d'un délégué (il est d'ailleurs également possible de considérer la chose en utilisant un delegate plutôt qu'une Func). OWIN n'est vraiment rien de plus qu'une spécification pour faire dialoguer applicatifs et serveurs.

L'écriture d'un composant respectant OWIN passe donc par l'écriture d'une méthode respectant cette signature. Cette dernière a deux avantages :

- elle est atomique, cela signifie qu'elle est conçue pour que plusieurs composants OWIN soient imbriqués et exécutés les uns à la suite des autres ;

- elle est extrêmement simple. Le paramètre d'entrée est unique et d'une forme très basique. Il est donc très aisé pour un développeur d'écrire un composant fonctionnant avec OWIN.

III - OWIN plus concrètement…

Tout ce que nous venons de voir est très théorique et il serait intéressant de se pencher sur l'aspect pratique d'OWIN. Je l'ai dit plus tôt, OWIN est une spécification, seulement une spécification. Libre aux développeurs de créer leurs propres implémentations du moment que celles-ci respectent la norme.

Dans les faits, il existe plusieurs implémentations.

- La plus connue est celle de Microsoft et se nomme Katana. J'y reviendrai plus largement dans mon prochain billet. Elle peut fonctionner via IIS, en self host ou via un petit programme portable. Le projet est disponible ici.

- Nowin, un serveur Web réalisé avec .NET 4.5 et sans se baser sur HttpListener, très léger et très performant (mais pas encore certifié pour un environnement de production). Il est disponible directement via Nuget. Le tout est développé par Boris Letocha, n'hésitez pas à aller voir comment c'est fait, c'est toujours formateur. Le projet est disponible sur Github.

- connect-owin, c'est assez psychédélique, mais intéressant pour en parler. Le but ici est de fournir une implémentation d'OWIN pour node.js. Là encore, c'est assez intéressant de regarder comment le projet est construit (notamment sur l'utilisation du dictionnaire d'environnement et du délégué applicatif). Le projet est également disponible sur Github.

Le projet owin-hosting, fournit une implémentation basique et très légère des types OWIN. Il est utilisé comme dépendance par Katana et j'en parlerai dans le prochain billet. Vous pouvez y jeter un coup d'œil rapide, il n'y a qu'une interface sur laquelle vous pourrez retrouver le fameux dictionnaire d'environnement (IDictionnary<string, object>).

IV - Pour résumer

L'essentiel à retenir de ce billet en version très concise :

- l'objectif d'OWIN est de découpler l'application du serveur et donc de pouvoir exécuter et livrer dans des environnements plus légers, plus exotiques ou tout simplement plus adaptés que le traditionnel IIS (avec Mono sur votre Raspberry à la maison par exemple) ;

- OWIN est une spécification. L'implémentation de Microsoft est Katana ;

- comment l'applicatif communique-t-il avec le serveur ? Simplement via le délégué applicatif qui manipule un dictionnaire d'environnement.

Nous venons de voir ici un aspect théorique très important pour le futur d'ASP.NET. Dans mon prochain billet, je parlerai de l'implémentation d'OWIN qui est réalisée par Microsoft, Katana, et nous pourrons réellement entrer dans la pratique. En attendant, retenez bien les concepts qui ont été abordés ici, car les annonces de Microsoft sur ASP.NET vNext continuent à aller dans ce sens (même si les implémentations bougent un peu).

À bientôt.

V - Remerciements

L'équipe de la rédaction .NET remercie sincèrement Soat de nous avoir autorisés à publier cet article sur Developpez.com. Soat est une société de conseil spécialisée dans l'accompagnement de ses clients, tout au long du cycle de vie de leurs projets et le développement de technologies Java et Web.

Nous remercions également Malick SECK pour la mise au gabarit et Claude LELOUP pour sa relecture orthographique.