Cercare di progettare un'API per applicazioni esterne con lungimiranza per il cambiamento non è facile, ma un po 'di pensiero iniziale può semplificare la vita in seguito. Sto cercando di stabilire uno schema che supporterà le modifiche future pur rimanendo compatibile con le versioni precedenti lasciando in posizione gestori di versioni precedenti.
La preoccupazione principale di questo articolo è quale modello dovrebbe essere seguito per tutti gli endpoint definiti per un determinato prodotto / azienda.
Schema di base
Dato un modello di URL di base, https://rest.product.com/
ho ideato che tutti i servizi risiedono /api
insieme ad /auth
altri endpoint non basati su riposo come /doc
. Pertanto posso stabilire gli endpoint di base come segue:
https://rest.product.com/api/...
https://rest.product.com/auth/login
https://rest.product.com/auth/logout
https://rest.product.com/doc/...
Endpoint di servizio
Ora per gli stessi endpoint. La preoccupazione POST
, GET
, DELETE
non è l'obiettivo primario di questo articolo ed è la preoccupazione su quelle azioni stesse.
Gli endpoint possono essere suddivisi in spazi dei nomi e azioni. Ogni azione deve inoltre presentarsi in modo tale da supportare cambiamenti fondamentali nel tipo di ritorno o nei parametri richiesti.
Prendendo un ipotetico servizio di chat in cui gli utenti registrati possono inviare messaggi, potremmo avere i seguenti endpoint:
https://rest.product.com/api/messages/list/{user}
https://rest.product.com/api/messages/send
Ora per aggiungere il supporto della versione per future modifiche alle API che potrebbero non funzionare. Potremmo aggiungere la firma della versione dopo /api/
o dopo /messages/
. Dato l' send
endpoint, potremmo quindi avere quanto segue per v1.
https://rest.product.com/api/v1/messages/send
https://rest.product.com/api/messages/v1/send
Quindi la mia prima domanda è: qual è un posto consigliato per l'identificatore di versione?
Gestione del codice del controller
Quindi ora abbiamo stabilito che dobbiamo supportare le versioni precedenti, quindi in qualche modo dobbiamo gestire il codice per ciascuna delle nuove versioni che potrebbero svalutarsi nel tempo. Supponendo che stiamo scrivendo endpoint in Java, potremmo gestirlo tramite pacchetti.
package com.product.messages.v1;
public interface MessageController {
void send();
Message[] list();
}
Ciò ha il vantaggio che tutto il codice è stato separato attraverso spazi dei nomi in cui qualsiasi modifica errata significherebbe che una nuova copia degli endpoint del servizio. Lo svantaggio è che tutto il codice deve essere copiato e che le correzioni di bug hanno voluto essere applicate a versioni nuove e precedenti che devono essere applicate / testate per ogni copia.
Un altro approccio è quello di creare gestori per ciascun endpoint.
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;
}
}
}
}
Questo ora isola il controllo delle versioni per ogni endpoint stesso e rende le correzioni dei bug compatibili con la porta posteriore, nella maggior parte dei casi devono essere applicate solo una volta, ma significa che dobbiamo fare un po 'più di lavoro su ogni singolo endpoint per supportare questo.
Quindi c'è la mia seconda domanda "Qual è il modo migliore per progettare il codice del servizio REST per supportare le versioni precedenti."