Essayer de concevoir une API pour des applications externes avec une vision du changement n'est pas facile, mais un peu de réflexion à l'avance peut vous faciliter la vie plus tard. J'essaie d'établir un schéma qui prendra en charge les modifications futures tout en restant rétrocompatible en laissant les gestionnaires de versions précédents en place.
La principale préoccupation de cet article est de savoir quel modèle doit être suivi pour tous les points finaux définis pour un produit / une entreprise donnée.
Schéma de base
Étant donné un modèle d'URL de base que https://rest.product.com/
j'ai mis au point que tous les services résident dans le /api
long avec /auth
et d' autres points d' extrémité à base non de repos tels que /doc
. Par conséquent, je peux établir les points de terminaison de base comme suit:
https://rest.product.com/api/...
https://rest.product.com/auth/login
https://rest.product.com/auth/logout
https://rest.product.com/doc/...
Points de terminaison de service
Maintenant pour les points de terminaison eux-mêmes. Préoccupation au sujet POST
, GET
, DELETE
n'est pas l'objectif principal de cet article et est la préoccupation de ces actions elles - mêmes.
Les points de terminaison peuvent être décomposés en espaces de noms et actions. Chaque action doit également se présenter de manière à prendre en charge les modifications fondamentales du type de retour ou des paramètres requis.
Prenant un service de chat hypothétique où les utilisateurs enregistrés peuvent envoyer des messages, nous pouvons avoir les points de terminaison suivants:
https://rest.product.com/api/messages/list/{user}
https://rest.product.com/api/messages/send
Maintenant, pour ajouter la prise en charge de la version pour les futurs changements d'API qui pourraient se casser. Nous pourrions ajouter la signature de la version après /api/
ou après /messages/
. Étant donné le send
point final, nous pourrions alors avoir ce qui suit pour v1.
https://rest.product.com/api/v1/messages/send
https://rest.product.com/api/messages/v1/send
Donc ma première question est, quel est un endroit recommandé pour l'identifiant de version?
Gestion du code du contrôleur
Donc, maintenant que nous avons établi que nous devons prendre en charge les versions antérieures, nous devons donc gérer le code pour chacune des nouvelles versions qui peuvent devenir obsolètes au fil du temps. En supposant que nous écrivons des points de terminaison en Java, nous pourrions gérer cela via des packages.
package com.product.messages.v1;
public interface MessageController {
void send();
Message[] list();
}
Cela a l'avantage que tout le code a été séparé par des espaces de noms où toute modification de rupture signifierait qu'une nouvelle copie des points de terminaison de service. Le désavantage de cela est que tout le code doit être copié et que les corrections de bogues souhaitées doivent être appliquées aux versions nouvelles et antérieures doivent être appliquées / testées pour chaque copie.
Une autre approche consiste à créer des gestionnaires pour chaque point de terminaison.
package com.product.messages;
public class MessageServiceImpl {
public void send(String version) {
getMessageSender(version).send();
}
// Assume we have a List of senders in order of newest to oldest.
private MessageSender getMessageSender(String version) {
for (MessageSender s : senders) {
if (s.supportsVersion(version)) {
return s;
}
}
}
}
Cela isole maintenant la version de chaque point de terminaison lui-même et rend les correctifs de bogues compatibles avec le port en ne devant dans la plupart des cas être appliqués qu'une seule fois, mais cela signifie que nous devons faire un peu plus de travail sur chaque point de terminaison individuel pour le prendre en charge.
Il y a donc ma deuxième question "Quelle est la meilleure façon de concevoir le code de service REST pour prendre en charge les versions précédentes."