Qual è il modello consigliato per gli endpoint REST che pianificano le modifiche previsionali

25

Cercare di progettare un'API per applicazioni esterne con previsione per il cambiamento non è facile, ma un po 'di riflessione può rendere la vita più facile in seguito. Sto provando a stabilire uno schema che supporterà le modifiche future rimanendo compatibile con le versioni precedenti lasciando in sospeso i gestori delle versioni precedenti.

La preoccupazione principale di questo articolo riguarda il modello da seguire per tutti gli endpoint definiti per un determinato prodotto / azienda.

Schema base

Dato un modello di URL di base di https://rest.product.com/ ho ideato che tutti i servizi risiedono sotto /api insieme a /auth e altri endpoint non rimanenti 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 del servizio

Ora per gli endpoint stessi. La preoccupazione per POST , GET , DELETE non è l'obiettivo principale di questo articolo ed è la preoccupazione per quelle azioni stesse.

Gli endpoint possono essere suddivisi in spazi dei nomi e azioni. Ogni azione deve anche 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 alla versione per le future modifiche API che potrebbero essere in fase di interruzione. Potremmo aggiungere la firma della versione dopo /api/ o dopo /messages/ . Dato l'endpoint send , potremmo avere il seguente per v1.

https://rest.product.com/api/v1/messages/send
https://rest.product.com/api/messages/v1/send

Quindi la mia prima domanda è, qual è il posto consigliato per l'identificativo della versione?

Gestione del codice controller

Quindi ora abbiamo stabilito che abbiamo bisogno di supportare versioni precedenti di cui abbiamo bisogno per gestire in qualche modo il codice per ognuna delle nuove versioni che potrebbero deprecare nel tempo. Supponendo che stiamo scrivendo endpoint in Java, potremmo gestirlo attraverso i pacchetti.

package com.product.messages.v1;
public interface MessageController {
    void send();
    Message[] list();
}

Questo ha il vantaggio che tutto il codice è stato separato attraverso spazi dei nomi in cui qualsiasi cambiamento di rottura significherebbe una nuova copia degli endpoint del servizio. Il danno è che tutto il codice deve essere copiato e le correzioni dei bug che si desidera applicare a nuove e versioni precedenti devono essere applicate / testate per ogni copia.

Un altro approccio consiste nel 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 su ciascun endpoint stesso e rende le correzioni dei bachi sulla porta compatibile nella maggior parte dei casi che devono essere applicate una sola volta, ma ciò 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 di servizio REST per supportare versioni precedenti."

    
posta Brett Ryan 06.03.2012 - 01:53
fonte

3 risposte

13

So there's my second question "What's the best way to design REST service code to support prior versions."

Un'API ortogonale progettata con molta attenzione probabilmente non avrà mai bisogno di essere cambiata in modi incompatibili con le versioni precedenti, quindi davvero il modo migliore è di non avere versioni future.

Naturalmente, probabilmente non lo otterrete davvero al primo tentativo; Quindi:

  • Crea la tua API, proprio come stai pianificando (ed è l'API che è versionata, non i singoli metodi all'interno), e fai molto rumore a riguardo. Assicurati che i tuoi partner sappiano che l'API può cambiare e che le loro applicazioni dovrebbero verificare se stanno utilizzando la versione più recente; e consigliare agli utenti di aggiornare quando ne è disponibile uno nuovo. Supportare due vecchie versioni è difficile, il supporto di cinque è insostenibile.
  • Resistere all'impulso di aggiornare la versione dell'API con ogni "rilascio". Le nuove funzionalità di solito possono essere implementate nella versione corrente senza rompere i client esistenti; sono nuove funzionalità. L'ultima cosa che vuoi è che i client ignorino il numero di versione, poiché è comunque compatibile per lo più con le versioni precedenti. Aggiorna la versione dell'API solo quando non puoi assolutamente andare avanti senza rompere l'API esistente.
  • Quando arriva il momento di creare una nuova versione, il primo client dovrebbe essere l'implementazione compatibile con le versioni precedenti della versione precedente. La "manutenzione API" dovrebbe essere implementata sull'API corrente. In questo modo non sei agganciato per mantenere diverse implementazioni complete; solo la versione corrente e diverse "shell" per le vecchie versioni. Esecuzione di un test di regressione per l'API ormai deprecata contro il client backwards comaptible è un buon modo per testare sia la nuova API che il livello di compatibilità.
risposta data 06.03.2012 - 16:32
fonte
3

La prima opzione di progettazione URI esprime meglio l'idea che stai facendo il versionamento dell'intera API. Il secondo potrebbe essere interpretato come versione dei messaggi stessi. Quindi questo è meglio IMO:

rest.product.com/api/v1/messages/send

Per la libreria client, penso che l'utilizzo di due implementazioni complete in pacchetti Java separati sia più pulito, più facile da usare e più facile da mantenere.

Detto questo, ci sono metodi migliori per evolvere le API rispetto al controllo delle versioni. Sono d'accordo con te sul fatto che dovresti essere preparato per questo, ma penso al versioning come al metodo di ultima istanza, da usare con attenzione e con parsimonia. È un grosso sforzo per i clienti aggiornare. Qualche tempo fa ho inserito alcuni di questi pensieri in un post del blog:

link

In particolare per il versioning delle API REST, troverai questo post di Mark Nottingham utile:

link

    
risposta data 06.03.2012 - 15:58
fonte
3

Un altro approccio per gestire il controllo delle versioni dell'API consiste nell'utilizzare la versione nelle intestazioni HTTP. Come

POST /messages/list/{user} HTTP/1.1
Host: http://rest.service.com
Content-Type: application/json
API-Version: 1.0      <----- like here
Cache-Control: no-cache

Puoi analizzare l'intestazione e gestirla appropriatamente nel back-end.

Con questo approccio i clienti non devono cambiare l'URL, solo l'intestazione. E anche questo rende gli endpoint REST più puliti, sempre.

Se qualcuno dei client non ha inviato l'intestazione della versione, invii 400 - Richiesta non valida oppure puoi gestirla con la versione compatibile con le versioni precedenti della tua API .

    
risposta data 17.11.2015 - 09:41
fonte

Leggi altre domande sui tag