Les services Web, sous une forme ou une autre, existent depuis plus de deux décennies. Par exemple, les services XML-RPC sont apparus à la fin des années 1990, suivis de peu par ceux écrits dans la branche SOAP. Les Services dans le style architectural de repos ont également fait la scène il y a environ deux décennies, peu de temps après les pionniers XML-RPC et SOAP. Les services de style REST (ci-après, Restful) dominent désormais les sites populaires tels qu’eBay, Facebook et Twitter. Malgré les alternatives aux services web pour l’informatique distribuée (par exemple, les services web Restful restent attractifs pour plusieurs raisons:

  • Les services Restful s’appuient sur l’infrastructure et les protocoles existants, en particulier les serveurs web et les protocoles HTTP / HTTPS. Une organisation qui a des sites web basés sur HTML peut facilement ajouter des services web pour les clients intéressés plus par les données et les fonctionnalités sous-jacentes que par la présentation HTML., Amazon, par exemple, a été le pionnier de la mise à disposition des mêmes informations et fonctionnalités via des sites Web et des services web, basés sur SOAP ou Restful.

  • Les services Restful traitent HTTP comme une API, évitant ainsi la superposition logicielle compliquée qui caractérise l’approche SOAP des services web. Par exemple, L’API Restful prend en charge les opérations CRUD (Create-Read-Update-Delete) standard via les verbes HTTP POST-GET-PUT-DELETE, respectivement; les codes D’état HTTP indiquent au demandeur si une demande a réussi ou pourquoi elle a échoué.,

  • Les services web Restful peuvent être aussi simples ou compliqués que nécessaire. Restful est un style—en fait, un style très flexible-plutôt qu’un ensemble de prescriptions sur la façon dont les services devraient être conçus et structurés. (L’inconvénient est qu’il peut être difficile de déterminer ce qui ne compte pas comme un service Restful.)

  • pour un consommateur ou un client, les services web Restful sont neutres en termes de langue et de plate – forme. Le client effectue des requêtes en HTTP (S) et reçoit des réponses texte dans un format adapté à l’échange de données moderne (par exemple, JSON).,

  • presque tous les langages de programmation À Usage général ont un support au moins adéquat (et souvent fort) pour HTTP / HTTPS, ce qui signifie que les clients de services web peuvent être écrits dans ces langages.

Cet article explore les services Restful légers en Java à travers un exemple de code complet.

le service Web Restful novels

Le service Web Restful novels se compose de trois classes définies par le programmeur:

  • La classeNovel représente un roman avec seulement trois propriétés: un ID généré par la machine, un auteur et un titre., Les propriétés pourraient être étendues pour plus de réalisme, mais je veux garder cet exemple simple.
  • la classeNovelsse compose d’utilitaires pour diverses tâches: conversion d’un codage en texte brut d’unNovel ou d’une liste d’entre eux en XML ou JSON; prise en charge des opérations CRUD sur la collection Romans; et initialisation de la collection La classe Novels Sert de médiateur entre les instances Novel et le servlet.,
  • la classeNovelsServletdérive deHttpServlet, un logiciel robuste et flexible qui existe depuis les tout premiers Java d’entreprise de la fin des années 1990. le servlet agit comme un point de terminaison HTTP pour les requêtes CRUD client. Le code de servlet se concentre sur le traitement des demandes des clients et la génération des réponses appropriées, laissant les détails diaboliques aux utilitaires de la classe Novels.

certains frameworks Java, tels que Jersey (JAX-RS) et Restlet, sont conçus pour les services Restful., Néanmoins, HttpServlet fournit à lui seul une API légère, flexible, puissante et bien testée pour fournir de tels services. Je vais le démontrer avec l’exemple des romans.

déployer le service web romans

le déploiement du service web romans nécessite bien sûr un serveur web. Mon choix est Tomcat, mais le service devrait fonctionner (célèbres derniers mots!) s’il est hébergé sur, par exemple, Jetty ou même un serveur D’applications Java. Le code et un README qui résume comment installer Tomcat sont disponibles sur mon site web., Il existe également un script Apache Ant documenté qui construit le service romans (ou tout autre service ou site web) et le déploie sous Tomcat ou l’équivalent.

Tomcat est disponible en téléchargement sur son site web. Une fois que vous l’installer localement, laissez TOMCAT_HOME être le répertoire d’installation., Il existe deux sous-répertoires d’intérêt immédiat:

  • le répertoire TOMCAT_HOME/bin contient des scripts de démarrage et d’arrêt pour les systèmes de type Unix (startup.sh Et shutdown.sh) et Windows (startup.bat Et shutdown.bat). Tomcat fonctionne comme une application Java. Le conteneur de servlet du serveur web est nommé Catalina. (Dans Jetty, le serveur web et le conteneur ont le même nom.) Une fois que Tomcat démarre, entrez dans un navigateur pour voir une documentation complète, y compris des exemples.,

  • le répertoireTOMCAT_HOME/webapps est la valeur par défaut pour les sites Web et les services web déployés. Le moyen simple de déployer un site web ou un service web est de copier un fichier JAR avec une extension .war (donc un fichier WAR) dans TOMCAT_HOME/webapps ou un sous-répertoire de celui-ci. Tomcat décompresse ensuite le fichier WAR dans son propre répertoire. Par exemple, Tomcat serait décompresser novels.war dans un sous-répertoire nommé novels, laissant novels.war comme-est., Un site web ou un service peut être supprimé en supprimant le fichier WAR et mis à jour en écrasant le fichier WAR avec une nouvelle version. Soit dit en passant, la première étape du débogage d’un site web ou d’un service consiste à vérifier que Tomcat a décompressé le fichier WAR; sinon, le site ou le service n’a pas été publié en raison d’une erreur fatale dans le code ou la configuration.,

  • parce que Tomcat écoute par défaut sur le port 8080 les requêtes HTTP, une URL de requête pour Tomcat sur la machine locale commence:

    Access a programmer-deployed WAR file by adding the WAR file’s name but without the .war extension:

    If the service was deployed in a subdirectory (e.g., myapps) of TOMCAT_HOME, this would be reflected in the URL:

    I’ll offer more details about this in the testing section near the end of the article.

As noted, the ZIP file on my homepage contains an Ant script that compiles and deploys a website or service. (A copy of novels.war is also included in the ZIP file.) For the novels example, a sample command (with % as the command-line prompt) is:

% ant -Dwar.name=novels deploy

Cette commande compile les fichiers source Java, puis crée un fichier déployable nommé novels.war, laisse ce fichier dans le répertoire courant= »ca876fbb3b »>., Si tout se passe bien, une requête GET (à l’aide d’un navigateur ou d’un utilitaire de ligne de commande, tel que curl) Sert de premier test:

% curl http://localhost:8080/novels/

Tomcat est configuré, par défaut, pour les déploiements à chaud: le serveur web mettre à jour ou supprimer une application web.

le service romans au niveau code

revenons à l’exemple romans mais au niveau code. Considérez la classeNovel ci-dessous:

exemple 1., La classe Novel

Cette classe implémente la méthodecompareTo à partir de l’interfaceComparable car les instancesNovel sont stockées dans un thread-safeConcurrentHashMap, qui n’applique pas un ordre trié. En répondant aux demandes d’affichage de la collection, le service romans trie une collection (un ArrayList) extrait de la carte; l’implémentation de compareTo applique un ordre de tri croissant Par Novel ID.,

La classe Novels contient diverses fonctions utilitaires:

Exemple 2. La classe utilitaire Romans

la méthode la plus compliquée est populate, qui lit à partir d’un fichier texte contenu dans le fichier WAR déployé. Le fichier texte contient la collection initiale de romans. Pour ouvrir le fichier texte, la méthodepopulate a besoin deServletContext, une carte Java qui contient toutes les informations critiques sur la servlet incorporée dans le conteneur de servlet., Le fichier texte, à son tour, contient des enregistrements tels que celui-ci:

Jane Austen!Persuasion

la ligne est analysée en deux parties (auteur et titre) séparées par le symbole bang (!). La méthode construit ensuite une instanceNovel, définit les propriétés author et title et ajoute le roman à la collection, qui agit comme un magasin de données en mémoire.

la classe Novels a également des utilitaires pour encoder la collection de romans en XML ou JSON, en fonction du format que le demandeur préfère., XML est la valeur par défaut, mais JSON est disponible sur demande. Un package XML-to-JSON léger fournit le JSON. Plus de détails sur l’encodage sont ci-dessous.

Exemple 3. La classe NovelsServlet

rappelle que la classe NovelsServlet ci-dessus étend la classe HttpServlet, qui à son tour étend la classe GenericServlet, qui implémente l’interface Servlet:

NovelsServlet extends HttpServlet extends GenericServlet implements Servlet

comme son nom l’indique clairement, le HttpServlet est conçu pour les servlets livrés sur http(s)., La classe fournit des méthodes vides nommé d’après la norme de la requête HTTP verbes (officiellement, méthodes):

  • doPost Post (=Créer)
  • doGet (Get = Lire)
  • doPut (A = mise à Jour)
  • doDelete (delete = Supprimer)

Quelques autres verbes sont ainsi couverts. Une extension de la HttpServlet, telle que la NovelsServlet, remplace toute do méthode d’intérêt, laissant les autres comme no-ops., La balise NovelsServlet remplace sept de la balise do méthodes.

chacune des méthodesHttpServlet CRUD prend les mêmes deux arguments. Voici doPost à titre d’exemple:

public void doPost(HttpServletRequest request, HttpServletResponse response) {

l’argument request est une carte des informations de requête HTTP, et le response fournit un flux de sortie au demandeur., Une méthode telle que doPost est structurée comme suit:

  • lire les informations request, en prenant toutes les mesures appropriées pour générer une réponse. Si des informations sont manquantes ou déficientes, générez une erreur.
  • utilisez les informations de requête extraites pour effectuer L’opération CRUD appropriée (dans ce cas, créez unNovel), puis encodez une réponse appropriée au demandeur en utilisant le flux de sortieresponse pour ce faire., Dans le cas de doPost, la réponse est une confirmation qu’un nouveau roman a été créé et ajouté à la collection. Une fois la réponse envoyée, le flux de sortie est fermé, ce qui ferme également la connexion.

en savoir Plus sur la méthode remplace

Une requête HTTP est relativement simple de la structure. Voici un croquis dans le http 1 familier.,1 format, avec des commentaires introduits par des signes doubles pointus:

la ligne de départ commence par le verbe HTTP (dans ce cas,GET) et L’URI (Uniform Resource Identifier), qui est le nom (dans ce cas,novels) qui nomme la ressource ciblée. Les en-têtes sont constitués de paires clé-valeur, avec un deux-points séparant la clé à gauche de la ou des valeurs à droite., L’en-tête avec la cléHost(insensible à la casse) est requis; le nom d’hôtelocalhostest l’adresse symbolique de la machine locale sur la machine locale, et le numéro de port8080est la valeur par défaut pour le serveur Web Tomcat (Par défaut, Tomcat écoute sur le port 8443 les requêtes HTTPS.) Les éléments d’en-tête peuvent se produire dans un ordre arbitraire. Dans cet exemple, la valeur de l’en-tête

Accept-typeest le type MIMEtext/plain.,

Certaines demandes (en particulier, POST et PUT) ont des corps, tandis que d’autres (en particulier, GET et DELETE) ne le font pas. S’il y a un corps (peut-être vide), deux sauts de ligne séparent les en-têtes du corps; le corps HTTP est constitué de paires clé-valeur. Pour les requêtes sans corps, des éléments d’en-tête, tels que la chaîne de requête, peuvent être utilisés pour envoyer des informations., Voici une demande à GET la ressource /novels avec L’ID de 2:

GET /novels?id=2

la chaîne de requête commence par le point d’interrogation et, en général, se compose de paires clé-valeur, bien qu’une clé sans valeur soit possible.

Le HttpServlet, avec des méthodes telles que getParameter et getParameterMap, joliment cache la distinction entre les requêtes HTTP avec et sans corps., Dans les romans exemple, la balise getParameter méthode est utilisée pour extraire les informations requises à partir de la balise GET, POST et DELETE demandes. (La gestion d’une requêtePUT nécessite un code de niveau inférieur car Tomcat ne fournit pas de carte de paramètres réalisable pour les requêtesPUT., Ici, pour l’illustration, est une tranche de la balise doPost méthode dans le NovelsServlet remplacer:

Pour les sans corps DELETE demande, l’approche est essentiellement le même:

Le doGet méthode doit faire la distinction entre deux types de GET demande: une « saveur » signifie « obtenir toutes », alors que les autres moyens d’obtenir une indiqué., Si l’URL de la requête GET contient une chaîne de requête dont la clé est un ID, alors la requête est interprétée comme « get a specified one »:

http://localhost:8080/novels?id=2 ## GET specified

S’il n’y a pas de chaîne de requête, la requête GET 435659b0db « >

quelques détails diaboliques

La conception du service romans reflète le fonctionnement d’un serveur web basé sur Java tel que Tomcat. Au démarrage, Tomcat crée un pool de threads à partir duquel les gestionnaires de requêtes sont tirés, une approche connue sous le nom de modèle un thread par requête., Les versions modernes de Tomcat utilisent également des e/s non bloquantes pour améliorer les performances.

le service romans s’exécute en tant qu’instance unique de la classeNovelsServlet, qui à son tour conserve une seule collection de romans. En conséquence, une condition de concurrence surviendrait, par exemple, si ces deux demandes étaient traitées simultanément:

  • Une demande modifie la collection en ajoutant un nouveau roman.
  • L’autre demande reçoit tous les romans de la collection.

le résultat est indéterminé, en fonction de la façon dont les opérations de lecture et d’écriture se chevauchent., Pour éviter ce problème, le service romans utilise un thread-safe ConcurrentMap. Les clés de cette carte sont générées avec un thread-safe AtomicInteger. Voici le segment de code correspondant:

public class Novels {
private ConcurrentMap<Integer, Novel> novels;
private AtomicInteger mapKey;
...

par défaut, une réponse à une requête client est codée en XML. Le programme romans utilise l’ancienne classeXMLEncoder pour plus de simplicité; une option beaucoup plus riche est la bibliothèque JAX-B., Le code est simple:

Le Object le paramètre est une triés ArrayList de romans (en réponse à un « obtenir toutes » demande); ou un Novel exemple (en réponse à une obtenir une demande); ou un String (un message de confirmation).

Si un en-tête de requête HTTP fait référence à JSON en tant que type souhaité, le XML est converti en JSON., Voici le vérifier dans le doGet méthode de la balise NovelsServlet:

String accept = request.getHeader("accept"); // "accept" is case insensitive
if (accept != null && accept.contains("json")) json = true;

Le Novels classe de maisons de la balise toJson méthode qui convertit XML en JSON:

Le NovelsServlet vérifie les erreurs de différents types. Par exemple, une demande POST doit inclure un auteur et un titre pour le nouveau roman., Si est absent, la balise doPost méthode lève une exception:

if (author == null || title == null)
throw new RuntimeException(Integer.toString(HttpServletResponse.SC_BAD_REQUEST));

Le SC dans un SC_BAD_REQUEST représente le code d’état, et le BAD_REQUEST a la norme HTTP numérique de la valeur de 400. Si le verbe HTTP dans une requête est TRACE, un code d’état différent est renvoyé:

tester le service romans

tester un service web avec un navigateur est délicat., Parmi les verbes CRUD, les navigateurs modernes ne génèrent que des requêtesPOST (créer) etGET (Lire). Même une requêtePOST est difficile pour un navigateur, car les valeurs clés du corps doivent être incluses; cela se fait généralement via un formulaire HTML. Un utilitaire de ligne de commande tel que curl est une meilleure façon de procéder, comme l’illustre cette section avec certaines commandes curl, qui sont incluses dans le ZIP de mon site web.,

Voici quelques exemples de tests sans la sortie correspondante:

% curl localhost:8080/novels/
% curl localhost:8080/novels?id=1
% curl --header "Accept: application/json" localhost:8080/novels/

la première commande demande tous les romans, qui sont encodés par défaut en XML. La deuxième commande demande le roman avec un ID de 1, qui est codé en XML. La dernière commande ajoute un élément d’en-têteAccept avecapplication/json comme type MIME souhaité. La commandeget one peut également utiliser cet élément d’en-tête. Ces requêtes ont JSON plutôt que les réponses XML.,

Les deux prochaines commandes de créer un nouveau roman dans la collection et de confirmer l’ajout:

% curl --request POST --data "author=Tolstoy&title=War and Peace" localhost:8080/novels/
% curl localhost:8080/novels?id=4

PUT commande: curl ressemble à un POST commande sauf que la balise PUT le corps n’utilise pas la syntaxe standard. La documentation pour la doPutméthode dans leNovelsServletva dans le détail, mais la version courte est que Tomcat ne génère pas une carte appropriée surPUT demandes., Voici un exemple: PUT commande et confirmation de commande:

% curl --request PUT --data "id=3#title=This is an UPDATE" localhost:8080/novels/
% curl localhost:8080/novels?id=3

La deuxième commande confirme la mise à jour.

Enfin, la balise DELETE commande fonctionne comme prévu:

% curl --request DELETE localhost:8080/novels?id=2
% curl localhost:8080/novels/

La demande est pour le roman avec l’ID de 2 à être supprimé. La deuxième commande montre les romans restants.

Le web.fichier de configuration xml

bien qu’il soit officiellement facultatif, un fichier de configuration web.xml est un pilier d’un site web ou d’un service de production., Le fichier de configuration permet de spécifier le routage, la sécurité et d’autres fonctionnalités d’un site ou d’un service indépendamment du code d’implémentation. La configuration du service romans gère le routage en fournissant un modèle D’URL pour les requêtes envoyées à ce service:

l’élément servlet-name fournit une abréviation (novels) pour le nom de classe complet du servlet (novels.NovelsServlet), et ce nom est= »f0d4722fe0″> élément ci-dessous.,

rappelez-vous qu’une URL pour un service déployé a le nom de fichier WAR juste après le numéro de port:

la barre oblique immédiatement après le numéro de port commence L’URI connu comme le chemin d’accès à la ressource demandée, dans ce cas, le service romans; par conséquent, le termenovels se produit après la première barre oblique.

Dans le web.xml fichier, la balise url-pattern est spécifié /*, ce qui signifie n’importe quel chemin qui commence par /romans., Supposons que Tomcat rencontre une URL de requête artificielle, telle que celle-ci:

la configuration web.xml spécifie que cette demande doit également être envoyée au servlet romans car le modèle /* couvre /foobar. L’URL artificielle a donc le même résultat que celui légitime indiqué ci-dessus.

un fichier de configuration de niveau production peut inclure des informations sur la sécurité, à la fois au niveau du fil et au niveau des utilisateurs., Même dans ce cas, le fichier de configuration ne ferait que deux ou trois fois la taille de l’échantillon.

enveloppant

leHttpServlet est au centre des technologies Web de Java. Un site web ou un service web, tel que le service romans, étend cette classe, en remplaçant les verbes d’intérêt do. Un framework Restful tel que Jersey (JAX-RS) ou Restlet fait essentiellement la même chose en fournissant un servlet personnalisé, qui agit ensuite comme le point de terminaison HTTP(S) pour les requêtes contre une application web écrite dans le framework.,

Une application basée sur un servlet a bien sûr accès à toute bibliothèque Java requise dans l’application web. Si l’application suit le principe de séparation des préoccupations, le code de servlet reste d’une simplicité attrayante: le code vérifie une requête, émet l’erreur appropriée s’il y a des lacunes; sinon, le code appelle toutes les fonctionnalités requises (par exemple, interroger une base de données, encoder une réponse dans un format spécifié), puis envoie la réponse au demandeur., Les typesHttpServletRequest EtHttpServletResponse facilitent l’exécution du travail spécifique au servlet de lecture de la demande et d’écriture de la réponse.

Java a des API qui vont du très simple au très compliqué. Si vous avez besoin de fournir des services Restful en utilisant Java, mon conseil est de faire un essai avant toute autre chose au HttpServlet.