Routing In Play Applications en Java

Routage des applications de lecture en Java

1. Vue d'ensemble

Le routage est un concept courant qui apparaît dans la plupart des frameworks de développement Web, y comprisSpring MVC.

Un itinéraire est un modèle d'URL mappé à un gestionnaire. Le gestionnaire peut être un fichier physique, tel qu'un actif téléchargeable dans l'application Web, ou une classe qui traite la demande, telle qu'un contrôleur dans une application MVC.

Dans ce didacticiel, nous allons explorer l'aspect du routage dans le développement d'applications Web avec lesPlay Framework.

2. Installer

Tout d'abord, nous devons créer une application Java Play. Les détails sur la façon de configurer Play Framework sur une machine sont disponibles dans nosintroductory article.

À la fin de la configuration, nous devrions avoir une application Play fonctionnelle à laquelle nous pouvons accéder à partir d’un navigateur.

3. Routage HTTP

Comment Play sait-il quel contrôleur consulter chaque fois que nous envoyons une requête HTTP? La réponse à cette question se trouve dans le fichier de configuration deapp/conf/routes.

Le routeur de Play traduit les requêtes HTTP en appels à l'action. HTTP requests are considered to be events in MVC architecture et le routeur y réagit en consultant le fichierroutes pour quel contrôleur et quelle action dans ce contrôleur exécuter.

Chacun de ces événements fournit un routeur avec deux paramètres: un chemin de requête avec sa chaîne de requête et la méthode HTTP de la requête.

4. Routage de base avec Play

Pour que le routeur fasse son travail, le fichierconf/routes doit définir les mappages des méthodes HTTP et des modèles d'URI vers les actions de contrôleur appropriées:

GET     /     controllers.HomeController.index
GET     /     assets/*file controllers.Assets.versioned(path="/public", file: Asset)

Tous les fichiers de routes doivent également mapper les ressources statiques dans le dossierplay-routing/public disponible pour le client sur le point de terminaison/assets. Notez la syntaxe de définition des routes HTTP et l'action du contrôleur du modèle d'URIspace de la méthode HTTPspace.

5. Modèles d'URI

Dans cette section, nous expliquerons un peu les modèles d’URI.

5.1. Modèles d'URI statiques

Les trois premiers modèles d'URI ci-dessus sont statiques. Cela signifie que le mappage des URL sur les ressources se produit sans autre traitement dans les actions du contrôleur.

Tant qu'une méthode de contrôleur est appelée, elle renvoie une ressource statique dont le contenu est déterminé avant la demande.

5.2. Modèles d'URI dynamiques

Le dernier modèle d'URI ci-dessus est dynamique. Cela signifie que l'action du contrôleur traitant une requête sur ces URI a besoin d'informations de la requête pour déterminer la réponse. Dans le cas ci-dessus, un nom de fichier est attendu.

La séquence normale des événements est que le routeur reçoit un événement, choisit le chemin de l'URL, décode ses segments et les transmet au contrôleur.

Les paramètres de chemin et de requête sont ensuite injectés dans l'action du contrôleur en tant que paramètres. Nous allons le démontrer avec un exemple dans les sections suivantes.

6. Routage avancé avec Play

Dans cette section, nous aborderons en détail les options avancées de routage à l'aide des modèles d'URI dynamiques.

6.1. Paramètres de chemin simple

Les paramètres de chemin simple sont des paramètres non nommés dans une URL de demande qui apparaissent après l'hôte et le port et qui sont analysés dans l'ordre d'apparition.

À l'intérieur deplay-routing/app/HomeController.java, créons une nouvelle action:

public Result greet(String name) {
    return ok("Hello " + name);
}

Nous voulons pouvoir sélectionner un paramètre de chemin d'accès à partir de l'URL de la demande et le mapper sur le nom de la variable.

Le routeur obtiendra ces valeurs d'une configuration de route.

Alors, ouvronsplay-routing/conf/routes et créons un mappage pour cette nouvelle action:

GET     /greet/:name     controllers.HomeController.greet(name: String)

Remarquez comment nous informons un routeur que ce nom est un segment de chemin dynamique avec la syntaxe du signe deux-points, puis le transmettons en tant que paramètre à l'appel d'action saluer.

Maintenant, chargeonshttp://locahost:9000/greet/john dans le navigateur, et nous serons accueillis par notre nom:

Hello john

Il se trouve queif our action parameter is of string type, we may pass it during the action call without specifying the parameter type, bien que ce ne soit pas la même chose pour les autres types.

Pimentons notre point de terminaison/greet avec des informations sur l'âge.

De retour à l'action de bienvenue deHomeController, nous allons la changer en:

public Result greet(String name, int age) {
    return ok("Hello " + name + ", you are " + age + " years old");
}

Et la route pour:

GET     /greet/:name/:age               controllers.HomeController.greet(name: String, age: Integer)

Notez également la syntaxe Scala pour déclarer une variable,age: Integer. En Java, nous utiliserions la syntaxeInteger age. Le cadre de jeu est construit en Scala. Par conséquent, il y a beaucoup de syntaxe scala.

Hello john, you are 26 years old

6.2. Caractères génériques dans les paramètres de chemin

Dans notre fichier de configuration des routes, le dernier mappage est:

GET     /assets/*file  controllers.Assets.versioned(path="/public", file: Asset)

Nous utilisons un caractère générique dans la partie dynamique du chemin. Ce que nous disons à Play, c'est que quelle que soit la valeur qui remplace*file dans la demande réelle, elle doit être analysée dans son ensemble et non décodée comme dans d'autres cas de paramètres de chemin.

Dans cet exemple, le contrôleur est un contrôleur intégré,Assets, qui permet au client de télécharger des fichiers à partir du dossierplay-routing/public. Lorsque nous chargeonshttp://localhost:9000/assets/images/favicon.png, nous devrions voir l'image du favicon Play dans le navigateur car il est présent dans le dossier/public/images.

Créons notre propre exemple d’action dansHomeController.java:

public Result introduceMe(String data) {
    String[] clientData = data.split(",");
    return ok("Your name is " + clientData[0] + ", you are " + clientData[1] + " years old");
}

Notez que dans cette action, nous recevons un paramètre String et appliquons notre logique pour le décoder. Dans ce cas, la logique consiste à diviser unString délimité par des virgules en un tableau. Auparavant, nous dépendions d'un routeur pour décoder ces données pour nous.

Avec les wildcards, nous sommes seuls. Nous espérons que le client obtiendra notre syntaxe correcte tout en transmettant ces données. Idéalement,we should validate the incoming String before using it.

Créons un itinéraire vers cette action:

GET   /*data   controllers.HomeController.introduceMe(data)

Chargez maintenant l'URLhttp://localhost:9000/john,26. Cela va imprimer:

Your name is john, you are 26 years old

6.3. Regex dans les paramètres de chemin

Tout comme les caractères génériques, nous pouvons utiliser des expressions régulières pour la partie dynamique. Ajoutons une action qui reçoit un nombre et renvoie son carré:

public Result squareMe(Long num) {
    return ok(num + " Squared is " + (num * num));
}

Nous allons maintenant ajouter son itinéraire:

GET   /square/$num<[0-9]+>   controllers.HomeController.squareMe(num:Long)

Plaçons cette route sous la routeintroduceMe pour introduire un nouveau concept. Avec cette configuration de routage, nous ne pouvons gérer que les routes dont la partie regex est un entier positif.

Maintenant, si nous avons placé la route comme indiqué dans le paragraphe précédent, et que nous chargeonshttp://localhost:9000/square/2, nous devrions être accueillis avec unArrayIndexOutOfBoundsException:

image

Si nous vérifions les journaux d'erreurs dans la console du serveur, nous nous rendrons compte que l'appel à l'action a en fait été effectué sur l'actionintroduceMe plutôt que sur l'actionsquareMe. Comme indiqué précédemment à propos des caractères génériques, nous sommes autonomes et nous n'avons pas validé les données entrantes.

Au lieu d'une chaîne délimitée par des virgules, la méthodeintroduceMe a été appelée avec la chaîne «square/2». Par conséquent, après l'avoir divisé, nous avons obtenu un tableau de taille un. Essayer d'atteindre l'indexpuis a jeté l'exception.

Naturellement, nous nous attendons à ce que l'appel soit acheminé vers la méthodesquareMe. Pourquoi a-t-il été acheminé versintroduceMe? La raison en est une fonctionnalité de lecture que nous aborderons ensuite appeléeRouting Priority.

7. Priorité de routage

S'il y a un conflit entre les routes comme il y en a entresquareMe etintroduceMe, alorsPlay picks the first route in declaration order.

Pourquoi y a-t-il un conflit? En raison du chemin de contexte générique,/data correspond à n'importe quelle URL de demande à l'exception du chemin de base/. Ainsi, * chaque route dont le modèle URI utilise des caractères génériques devrait apparaître en dernier dans l'ordre.

Modifions maintenant l'ordre de déclaration des routes pour que la routeintroduceMe vienne aprèssquareMe et rechargions:

2 Squared is 4

Pour tester la puissance des expressions régulières dans une route, essayez de chargerhttp://locahost:9000/square/-1, un routeur ne parviendra pas à correspondre à la routesquareMe. Au lieu de cela, il correspondra àintroduceMe, et nous obtiendrons à nouveau lesArrayIndexOutOfBoundsException.

Cela est dû au fait que-1 ne correspond pas à l'expression régulière fournie, ni aucun caractère alphabétique.

8. Paramètres

Jusqu'à présent, nous avons couvert la syntaxe de déclaration des types de paramètres dans le fichier de routes.

Dans cette section, nous examinerons plus d'options qui s'offrent à nous lorsque nous traitons des paramètres dans les itinéraires.

8.1. Paramètres avec des valeurs fixes

Parfois, nous souhaitons utiliser une valeur fixe pour un paramètre. C'est notre façon de dire à Play d'utiliser le paramètre de chemin fourni ou si le contexte de la requête est le chemin/, alors utilisez une certaine valeur fixe.

Une autre façon de voir les choses consiste à avoir deux points de terminaison ou chemins de contexte menant à la même action de contrôleur - un point de terminaison nécessitant un paramètre de l'URL de la demande et passant par défaut à l'autre au cas où ledit paramètre serait absent.

Pour illustrer cela, ajoutons une actionwriter() auxHomeController:

public Result writer() {
    return ok("Routing in Play by example");
}

En supposant que nous ne voulons pas toujours que notre API renvoie unString:

Routing in Play by example

Nous voulons le contrôler en envoyant le nom d'un auteur de l'article avec la requête, par défaut à la valeur fixeexample uniquement si la requête n'a pas le paramètreauthor.

Modifions donc davantage l'actionwriter en ajoutant un paramètre:

public Result writer(String author) {
    return ok("REST API with Play by " + author);
}

Voyons également comment ajouter un paramètre à valeur fixe à l'itinéraire:

GET     /writer           controllers.HomeController.writer(author = "example")
GET     /writer/:author   controllers.HomeController.writer(author: String)

Remarquez comment nous avons maintenant deux routes distinctes menant toutes à l'actionHomeController.index au lieu d'une.

Lorsque nous chargeons maintenanthttp://localhost:9000/writer depuis le navigateur, nous obtenons:

Routing in Play by example

Et quand on chargehttp://localhost:9000/writer/john, on obtient:

Routing in Play by john

8.2. Paramètres avec des valeurs par défaut

En plus d'avoir des valeurs fixes, les paramètres peuvent également avoir des valeurs par défaut. Les deux fournissent des valeurs de secours aux paramètres d'action du contrôleur dans le cas où la demande ne fournit pas les valeurs requises.

La différence entre les deux est quefixed values are used as a fallback for path parameters while default values are used as a fallback for query parameters.

Les paramètres de chemin sont de la formehttp://localhost:9000/param1/param2 et les paramètres de requête sont de la formehttp://localhost:9000/?param1=value1¶m2=value2.

La seconde différence réside dans la syntaxe de déclaration des deux dans une route. Les paramètres de valeur fixe utilisent l'opérateur d'affectation comme dans:

author = "example"

Alors que les valeurs par défaut utilisent un type d'affectation différent:

author ?= "example"

Nous utilisons l'opérateur?= qui assigne conditionnellementexample àauthor au cas oùauthor ne contiendrait aucune valeur.

Pour avoir une démonstration complète, créons l’actionHomeController.writer. Supposons que, outre le nom de l'auteur qui est un paramètre de chemin, nous souhaitons également transmettre l'auteurid en tant que paramètre de requête qui devrait par défaut être1 s'il n'est pas passé dans la requête.

Nous allons changer l'action dewriter en:

public Result writer(String author, int id) {
    return ok("Routing in Play by: " + author + " ID: " + id);
}

et les routeswriter vers:

GET     /writer           controllers.HomeController.writer(author="example", id: Int ?= 1)
GET     /writer/:author   controllers.HomeController.writer(author: String, id: Int ?= 1)

Maintenant en train de chargerhttp://localhost:9000/writer, nous voyons:

Routing in Play by: example ID: 1
Routing in Play by: example ID: 10
Routing in Play by: john ID: 1
Routing in Play by: john ID: 5

9. Conclusion

Dans cet article, nous avons exploré la notion de routage dans les applications de lecture. Nous avons également un article surbuilding a RESTful API with Play Framework où les concepts de routage de ce tutoriel sont appliqués dans un exemple pratique.

Comme d'habitude, le code source de ce tutoriel est disponibleover on GitHub.