Création d’un service Web REST avec Java et Spring (partie 1) – Gratuit : Audit complet de votre site internet

Avant de commencer !

Titre de l
Share on facebook
Share on twitter
Share on pinterest

Dans le monde moderne des logiciels interconnectés, les applications Web sont devenues un atout indispensable. Au premier rang de ces applications Web se trouve le service Web REST (Representational State Transfer), Java devenant l’un des langages d’implémentation les plus populaires. Au sein de l’écosystème Java REST, il existe deux concurrents populaires: Java Enterprise Edition (JavaEE) et Spring. Bien que les deux aient leurs forces et leurs faiblesses, cet article se concentrera sur Spring et créera une application Web RESTful simple de gestion des commandes à l’aide de Spring 4. Bien que ce système de gestion soit simple par rapport aux services RESTful à grande échelle trouvés aujourd’hui, il démontrera néanmoins la processus de réflexion de base, décisions de conception et tests d’implémentation requis pour créer un service Web Spring REST de niveau 3 (piloté par hypermédia).

À la fin de cet article, nous aurons créé un système de gestion des commandes Spring REST entièrement fonctionnel. Bien que le code source illustré dans cet article couvre les aspects essentiels du système de gestion des commandes, il existe d’autres composants et du code (tels que les cas de test) qui prennent en charge le service principal qui ne sont pas affichés. Tout le code source, y compris ces aspects de prise en charge, se trouve dans le référentiel GitHub suivant:

Titre de l'image

Les extraits de code source de cet article ne sont pas suffisants en soi pour créer un service Web REST pleinement fonctionnel. Au lieu de cela, ils servent d’instantané ou de réflexion du code source contenu dans le référentiel ci-dessus. Par conséquent, lorsque nous parcourons chaque étape de la création de notre service REST, le code source dans le référentiel ci-dessus doit être visité de manière cohérente et utilisé comme référence faisant autorité pour tous les choix de conception et d’implémentation. Pour le reste de cet article, lorsque nous faisons référence à notre système de gestion des commandes, nous faisons en fait référence au service Spring REST contenu dans le référentiel ci-dessus.

Connaissances préalables

Il est prévu que le lecteur ait au moins une compréhension novice de l’injection de dépendance (DI), en particulier DI en utilisant le cadre Spring. Bien que nous explorions les configurations de l’infrastructure DI utilisées et les composants DI utilisés dans notre système de gestion des commandes, il est supposé que le lecteur a au moins une compréhension conceptuelle du besoin et de la prémisse de DI. Pour plus d’informations sur DI dans Spring, reportez-vous au Spring Framework Guide et à la carte de configuration de printemps de DZone.

Cet article suppose également que le lecteur possède une compréhension fondamentale des services Web REST et RESTful. Alors que nous approfondirons les subtilités de conception et d’implémentation de la création d’un service Web REST au printemps, nous ne nous concentrerons pas sur les aspects conceptuels de REST (tels que l’utilisation d’un appel HTTP GET ou POST). Pour plus d’informations sur les services Web RESTful, voir Fondations DZone de Refcard Architecture RESTful et services Web RESTful par Leonard Richardson et Sam Ruby.

Cycle de développement et outils

Notre système de gestion des commandes a été créé à l’aide de Test Driven Development (TDD), où les tests ont été créés en premier et chaque décision de conception et composant mis en œuvre était axé sur la réussite des cas de test créés. Cela a non seulement abouti à un ensemble simple de classes, mais à un ensemble de composants plus facilement reconnaissable. Par exemple, la logique de persistance et la logique de domaine ne sont pas liées. Outre le processus utilisé pour créer le service, il existe également de nombreux outils utilisés pour créer, tester et déployer le système, notamment:

  • Spring Model-View-Controller (MVC): le cadre principal de notre service Web; ce cadre fournit les annotations et la structure nécessaires pour créer nos points de terminaison REST et servir ces points de terminaison sur HTTP.
  • Spring Boot: un cadre de convention sur configuration qui supprime la majorité du code et de la configuration Spring du passe-partout; ce cadre nous permet de développer et de lancer un service Web avec une fraction des tracas d’un service Web Spring standard.
  • Apache Maven: un outil de gestion de génération et de dépendance utilisé pour créer, exécuter des tests et empaqueter notre service Web dans un fichier Java Archive (JAR) qui sera exécuté pour exécuter notre service Web RESTful.
  • JUnit: un framework automatisé de tests unitaires qui sera utilisé pour les tests unitaires et d’intégration, ainsi que pour automatiser l’exécution de nos tests d’acceptation.
  • Concombre: un cadre automatisé de tests d’acceptation qui nous permet de créer des critères d’acceptation textuels et d’exercer notre service Web pour garantir que toutes les fonctionnalités de haut niveau sont correctes pendant le développement du service.
  • Java 8: au moment de la rédaction, la dernière version de Java; nous utiliserons l’API streams pour réduire le code nécessaire pour filtrer les objets de domaine dans notre système.
  • GitHub: un service d’hébergement gratuit pour les projets basés sur Git; notre service Web sera hébergé ici et l’historique des validations du service Web peut être consulté ici.
  • Travis Continuous Integration (CI): une plate-forme d’intégration continue gratuite qui exécute les tests automatisés par ordre de service Web chaque fois qu’un commit est poussé vers notre référentiel GitHub; l’historique de construction et de test de notre service Web peut être consulté ici.

Bien que nous utilisions un large éventail de cadres et d’outils, chacun a une tâche très importante lors de la création de notre service Web. Avant de passer à l’implémentation, nous devons d’abord concevoir une conception pour notre système de gestion des commandes.

Conception du service Web

La première étape de la conception de notre service Web consiste à décider de ce que nous voulons que le service accomplisse. Étant donné que nous sommes tenus de traiter les commandes, la définition d’une commande (c’est-à-dire la création d’un modèle de domaine) est un bon point de départ. Chaque commande que nous traitons doit avoir un identifiant (ID) afin que nous puissions y pointer de manière unique, une description qui décrit la commande sous une forme lisible par l’homme, un coût et un statut d’achèvement.

Notez que le coût n’est pas un problème trivial à gérer dans les logiciels: il est très facile de perdre la trace des centimes et de faire de simples erreurs d’arrondi. Afin d’éviter ces problèmes subliminaux, nous stockerons simplement le coût de notre commande en cents. Cela nous permet de supprimer la décimale et d’effectuer une arithmétique simple sans craindre que nous perdions un sou dans le mélange.

En utilisant cette simple définition d’une commande, nous concevons le modèle de domaine suivant:

Titre de l'image

Une fois notre commande conçue, nous pouvons passer à la conception de la manière dont nous exposerons nos commandes via notre service Web RESTful. En utilisant les verbes HTTP standard, nous pouvons rapidement trouver un ensemble de Points de terminaison REST qui couvrent les opérations habituelles de création, lecture, mise à jour et suppression (CRUD):

Verbe HTTP URL La description
GET /order

Obtient la liste de toutes les commandes actuellement créées dans le système. Si la liste est obtenue avec succès, la liste des commandes existantes est renvoyée, avec un HTTP 200(OK) état.

POST /order Crée une nouvelle commande. Cette demande doit contenir un corps de demande qui inclut les données qui doivent être associées à la commande nouvellement créée. Si la commande est créée, un HTTP 201 Le statut (Créé) est renvoyé avec l’ordre nouvellement créé dans le corps de la réponse.
GET /order/{id} Obtient la commande associée à l’ID donné. Si aucune commande n’existe, un HTTP 404 (Introuvable) est retourné. Si la commande peut être trouvée, un HTTP 200 est retourné et le corps de la réponse contient les informations associées à la commande.
PUT /order/{id} Met à jour une commande existante. Si aucune commande avec l’ID donné ne peut être trouvée, un HTTP 404 le statut est retourné. Si une commande existe avec l’ID donné et que le corps de la demande contient des mises à jour valides de la commande, la commande est mise à jour et la commande mise à jour est renvoyée dans le corps de la réponse, avec un HTTP 200 statut.
DELETE /order/{id} Supprime une commande avec l’ID donné. Si aucune commande n’existe, un HTTP 404 le statut est retourné. Si la commande existe, elle est supprimée et un HTTP 204 (Aucun contenu) est retourné.

Il est important de noter que nous ne devons pas simplement énumérer les points de terminaison REST que nous avions l’intention de créer, mais également inclure le comportement attendu si le point de terminaison réussit la demande ou s’il ne le fait pas. Par exemple, si un client demande de mettre à jour une commande inexistante, nous devons retourner un 404 erreur pour informer le client que cette ressource n’existe pas. Si nous réussissons à mettre à jour la ressource, nous devons renvoyer un 200 pour informer le client que sa demande a été traitée avec succès.

À ce stade, il est également utile de penser à quoi ressembleront les corps de réponse pour les différents points de terminaison REST. En raison de sa simplicité, nous consommerons des objets JSON (Javascript Object Notation) dans les corps de requête que nous recevons et produirons des objets JSON dans les corps de réponse que nous enverrons, respectivement. Par exemple, si nous suivons notre modèle de domaine, le corps de réponse pour obtenir une commande avec un ID spécifié ressemblerait à:

{
    "id": 1,
    "description": "Some sample order",
    "costInCents": 250,
    "complete": false
}

Nous verrons plus loin dans cet article que d’autres attributs, tels que les liens hypermédia, seront également inclus. Indépendamment du lien hypermédia, la réflexion sur les organes de demande et de réponse attendus pour nos points de terminaison REST nous permet de concevoir à l’avance des cas de test qui garantissent que nous traitons et produisons les résultats attendus lorsque nous implémentons nos points de terminaison REST.

Avec notre modèle de domaine et nos points de terminaison REST définis, nous pouvons passer à la dernière pièce du puzzle: comment stocker nos commandes. Par exemple, lorsque nous créons une nouvelle commande, nous avons besoin d’un moyen de stocker cette commande afin qu’un client, à un moment ultérieur, puisse récupérer la commande créée.

Dans un véritable service Web REST, nous déciderions de la meilleure base de données ou du cadre de persistance qui prend en charge notre modèle de domaine et concevrions une interface de couche de persistance à utiliser pour interagir avec cette base de données. Par exemple, nous pourrions sélectionner une base de données Neo4j si nos données étaient bien adaptées à un modèle de domaine graphique, ou MongoDB si notre modèle de domaine s’intègre bien dans les collections. Dans le cas de notre système, pour plus de simplicité, nous utiliserons une couche de persistance en mémoire. Bien qu’il existe diverses bases de données en mémoire utiles, notre modèle est assez simple pour créer nous-mêmes la base de données en mémoire. Ce faisant, nous verrons les fonctionnalités de base d’une base de données attachée à un service REST, ainsi que comprendre les interfaces simples qui sont courantes parmi les référentiels dans les services RESTful.

Abstraction de notre conception

À ce stade de notre conception, nous avons trois sections distinctes de notre système: (1) un modèle de domaine, (2) une série de points de terminaison REST, et (3) un moyen de stocker nos objets de domaine, ou une couche de persistance. Cet ensemble de trois sections est si commun qu’il a son propre nom: A Application à 3 niveaux. De plus, Martin Fowler a écrit un livre entier, Patterns of Enterprise Architecture, sur les modèles qui entourent cette architecture d’application. Les trois niveaux de cette architecture sont (1) la présentation, (2) le domaine et (3) la source de données (utilisée de manière interchangeable avec couche de persistance). Dans notre cas, nos points de terminaison REST sont mappés à la couche de présentation, notre modèle de domaine d’ordre est mappé à la couche de domaine et notre base de données en mémoire est mappée à la couche de source de données.

Bien que ces trois couches soient généralement représentées l’une empilée l’une sur l’autre, avec la couche de présentation en haut, la plus proche de l’utilisateur, la couche de domaine au milieu et la couche de source de données en bas, cela peut être plus utile de regarder cette architecture en termes d’interactions, comme illustré dans le schéma suivant.

Titre de l'image

Un ajout important est apporté à notre architecture: les objets de domaine ne sont pas envoyés directement à l’utilisateur. Au lieu de cela, ils sont enveloppés dans des ressources et les ressources sont fournies à l’utilisateur. Cela fournit un niveau d’indirection entre l’objet de domaine et la façon dont nous présentons l’objet de domaine à l’utilisateur. Par exemple, si nous souhaitons présenter à l’utilisateur un nom différent pour un champ de notre modèle de domaine (disons orderName au lieu de simplement name), nous pouvons le faire en utilisant une ressource. Bien que ce niveau d’indirection soit très utile pour découpler notre présentation du modèle de domaine, il permet à la duplication de se faufiler. Dans la plupart des cas, la ressource ressemblera à l’interface de l’objet domaine, avec quelques ajouts mineurs. Ce problème est résolu plus tard lorsque nous implémentons notre couche de présentation.

L’objet ressource fournit également un endroit approprié pour nous de présenter nos liens hypermédia. Selon le modèle Richardson pour les services Web REST, les services basés sur l’hypermédia sont le niveau de capacité le plus élevé d’une application REST et fournissent des informations importantes associées aux données de ressource. Par exemple, nous pouvons fournir des liens pour supprimer ou mettre à jour la ressource, ce qui évite au client qui consomme notre service Web REST de connaître les points de terminaison REST pour ces actions. En pratique, la ressource retournée (désérialisée en JSON) peut ressembler à ce qui suit:

{
    "id": 1,
    "description": "Some sample order",
    "costInCents": 250,
    "complete": false
    "_links": {
        "self": {
            "href": "http://localhost:8080/order/1"
        },
        "update": {
            "href": "http://localhost:8080/order/1"
        },
        "delete": {
            "href": "http://localhost:8080/order/1"
        }
    }
}

Compte tenu de ces liens, le consommateur n’est plus tenu de créer les URL des points de terminaison REST de mise à jour, de suppression ou d’auto-référence. Au lieu de cela, il peut simplement utiliser les liens fournis dans notre réponse basée sur l’hypermédia. Non seulement cela réduit la logique nécessaire pour interagir avec notre service Web REST (il n’est plus nécessaire de créer les URL), mais cela encapsule également la logique pour la construction des URL. Par exemple, supposons que nous modifions notre service REST pour exiger un paramètre de requête, tel que le tri: si nous fournissons les liens vers le consommateur, nous pouvons nous adapter à cette modification, sans apporter aucune modification au consommateur:

{
    "id": 1,
    "description": "Some sample order",
    "costInCents": 250,
    "complete": false
    "_links": {
        "self": {
            "href": "http://localhost:8080/order/1?sorting=default"
        },
        "update": {
            "href": "http://localhost:8080/order/1?sorting=default"
        },
        "delete": {
            "href": "http://localhost:8080/order/1?sorting=default"
        }
    }
}

Bien que la génération de ces liens puisse être fastidieuse et sujette à un grand nombre de bogues (c.-à-d. Que se passe-t-il si l’adresse IP de la machine hébergeant le service Web change?), Spring Hypermedia en tant que moteur de l’état de l’application (HATEOAS, couramment prononcé hay-tee-os) framework fournit de nombreuses classes et constructeurs qui nous permettent de créer facilement ces liens. Ce sujet sera approfondi lorsque nous approfondirons la mise en œuvre de notre couche de présentation.

Avant de passer à la mise en œuvre de notre service Web, nous devons rassembler notre conception et concevoir un plan d’action pour le créer. À l’heure actuelle, nous avons un seul objet de domaine, Order, dont les instances seront conservées dans une base de données en mémoire et servies (au sein d’une ressource) aux clients utilisant nos points de terminaison REST. Cette conception nous laisse avec quatre étapes principales:

  1. Implémenter le modèle de domaine
    • Créer la classe de domaine Order
  2. Implémenter la couche de source de données
    • Créer une base de données en mémoire
    • Implémenter les opérations CRUD pour la classe de domaine Order
  3. Implémenter la couche de présentation
    • Créer les points de terminaison REST
    • Créer la ressource Order
    • Créer un assembleur pour construire une ressource Order avec des liens HATEOAS appropriés
  4. Regroupez l’application
    • Créez la méthode principale qui exécutera l’application

Dans la partie 2, nous travaillerons sur la mise en œuvre de la source de données et des couches de domaine, qui jetteront les bases de la mise en œuvre de l’interface RESTful de notre service Web.

Publié à l’origine en septembre 2017

Lectures complémentaires

Création d’un service Web REST avec Java et Spring (partie 2)

Créer un service Web REST à l’aide de Spring

Exemple complet de service Web RESTful d’amorçage pas à pas

Inscris-toi à notre newsletter !

Partage cet article avec tes amis !

Share on facebook
Share on google
Share on twitter
Share on linkedin