WCF REST Starter kit vs. WebAPI (ASP.NET MVC)

Publié le 06 mai 2015 par Olivier Duval

Avant, pour REST, il y avait WCF et le kit WCF REST Starter Kit, mais ça, c’était avant. WCF est un framework généraliste, avec ses avantages mais aussi ses défauts pour adresser de l’échange de données en tcp, http, SOAP 1.1, SOAP 2 et avec les nouvelles tendances, du REST avec les formats XML et surtout JSON.

Au fur et à mesure des versions, cela s’est compliqué, tant en configuration qu’en développement pour répondre aux besoins spécifiques. Mais depuis récemment, tout cela a changé, avec ASP.NET MVC qui embarque WebAPI, un framework dédié à REST, qui facilite le développement RESTful, on arrive enfin à une certaine maturité. Il est à souligner que les deux sont Opensource sous licence Apache 2.0, on peut espérer que l’état de l’art sera suivi

A une époque, on utilisait la librairie WCF REST Starter Kit pour “simplifier” le développement de services RESTful, pour de la consultation. J’en avais parlé brièvement dans ce billet qui traite de JSON-P. La librairie, développé au dessus de WCF, avait le mérite d’être simple d’utilisation et intégrait un ensemble de fonctionnalités : méthodes d’extension, cache, réponses HTTP, utilitaires pour la syndication (Atom/RSS), méthodes pour consommer un service REST,…cela pour ramener du XML ou du JSON, voire du JSON-P grâce à une extension sous format d’attribut. Utilisée conjointement avec WCF, nous pouvions développer des services REST de façon assez rapide même si jouer avec la configuration XML pour WCF (dans le web.config) pour exprimer le contrat du services avec ses formats de résultat (xml/json/json-p) se révélait / révèle parfois un peu sport (où l’on pense tout de suite au monde merveilleux d’en face et de l’ultra configuration XML, suivez mon regard).

Après être restée bien longtemps en version Preview 2, elle est devenue obsolète depuis avril 2012 et elle a été remplacée par WebAPI, fournie avec ASP.NET MVC, pour développer des services RESTful avec moins de configuration, beaucoup plus de souplesse pour étendre les comportements, de façon (presque) simple. J’ai eu à développer un service Web REST (GET, POST, DELETE), un module qui s’intègre dans un projet plus vaste.

Imaginons vouloir exposer un service pour gérer des utilisateurs. Ce service devra permettre la création, la consultation, voire la suppression (du CRUD ), bref, du REST, on aura : POST (création), GET (consultation), DELETE (suppression). On aura aussi la possibilité d’interroger en GET le référentiel en utilisant des filtres de recherche (cette fonctionnalité est fournie par OData, à base des conventions d’interrogations OData pour peu que le retour de votre méthode soit IQueryable et marquée d’un attribut Queryable).

Issu d’ASP.NET MVC, nous avons besoin d’un controller, qui doit hériter de ApiController. J’utilise un repository pour l’accès à la base de donnée (avec NHibernate).

Un service Restful n’utilise que les mots HTTP, que cela soit en entrée (GET, etc) ou en sortie via les codes HTTP de retour (2xx, 4xx, 5xx etc), les données renvoyées seront donc encapsulée dans un HttpResponseMessage avec le code HTTP associé.

  public class UserController : ApiController
  {
        IRepository<UserRef, int> _repoUser;
        public UserController(IRepository<UserRef, int> repository)
        {
            _repoUid = repository;
        }

        public UserController()
        {
            _repoUid = RepoHelper.getRepoUser();
        }

        [Queryable]
        public IQueryable<MemberUID> Get()
        {
            var repo = UIDHelper.getUIDManager();
            var res = repo.GetAll().AsQueryable();

            return res;
        }

        public HttpResponseMessage Get(int id)
        {
            HttpResponseMessage responseMessage;

            var member = _repoUid.Get(id);

            responseMessage = member!=null ?
                ControllerContext.Request.CreateResponse(HttpStatusCode.Found, member) :
                ControllerContext.Request.CreateResponse(HttpStatusCode.NotFound, "ID not found");

            return responseMessage;
        }

        public HttpResponseMessage Post(UserRef member)
        {
            HttpResponseMessage responseMessage;
            var message = string.Empty;

            if(!_checkParametersPost(member, out message))
            {
                responseMessage = ControllerContext.Request.CreateResponse(HttpStatusCode.BadRequest, message);
                return responseMessage;
            }

            // 1ère sélection / Prenom/Date Naissance/Dep Naissance/Sexe
            var r1 = _repoUid.Find(m => m.Prenom == member.Prenom &
                                   m.Sexe == member.Sexe &
                                   m.DateNaissance == member.DateNaissance &
                                   m.DepNaissance == member.DepNaissance);

            // 2ème filtre : Nom (selon M ou F)
            var res = member.Sexe == "M" ?
                r1.FirstOrDefault(m => m.NomPatronyme == member.NomPatronyme) :
                r1.FirstOrDefault(m => m.NomNaissance == member.NomNaissance);
            if (res == null)
            {
                member.DateCreation = DateTime.Now;
                member.UID = Guid.NewGuid().ToString();
                _repoUid.Save(member);
                responseMessage = ControllerContext.Request.CreateResponse(HttpStatusCode.Created, member.UID);
                return responseMessage;
            }

            responseMessage = ControllerContext.Request.CreateResponse(HttpStatusCode.BadRequest, string.Format("user already exists : {0}", res.UID));
            return responseMessage;
        }

        public HttpResponseMessage Delete(string id)
        {
            var responseMessage = ControllerContext.Request.CreateResponse(HttpStatusCode.BadRequest);
            if(!string.IsNullOrEmpty(id))
            {
                var luid = _repoUid.Find(m => m.UID == id);
                if(luid.Count==0)
                {
                    responseMessage = ControllerContext.Request.CreateResponse(HttpStatusCode.NotFound, string.Format("uid not found : {0}", id));
                    return responseMessage;
                }

                var uiddel = luid.FirstOrDefault();
                if(uiddel!=null)
                {
                    _repoUid.Delete(uiddel);
                    responseMessage = ControllerContext.Request.CreateResponse(HttpStatusCode.NoContent);
                    return responseMessage;
                }
            }

            return responseMessage;
        }

        /// <summary>
        ///     Disposable
        /// </summary>
        /// <param name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
            if (disposing & _repoUid != null)
            {
                _repoUid.Dispose();
                _repoUid = null;
            }
            base.Dispose(disposing);
        }
    }

Voilà, simple et efficace.

Quelques ressources utiles :

  • Stackoverflow : LE site de questions / réponses où il est assez facile d’avoir des exemples de codes
  • Matlus : de bons billets sur WebAPI, REST, HTTP & co