2011-11-28 14 views
32

SfondoQuali sono le migliori pratiche per aggiungere metadati a una risposta JSON RESTful?

Stiamo costruendo un'API riposante che deve restituire oggetti dati come JSON. Nella maggior parte dei casi va bene solo restituire l'oggetto dati, ma in alcuni casi, f.ex. impaginazione o validazione, dobbiamo aggiungere alcuni metadati alla risposta.

Quello che abbiamo finora

Abbiamo avvolto tutte le risposte JSON come in questo esempio:

{ 
    "metadata" :{ 
     "status": 200|500, 
     "msg": "Some message here", 
     "next": "http://api.domain.com/users/10/20" 
     ... 
    }, 
    "data" :{ 
     "id": 1001, 
     "name": "Bob" 
    } 
} 

Pro

  • possiamo aggiungere metadati utili alla risposta

Contro

  • Nella maggior parte dei casi non abbiamo bisogno del campo di metadati, e si aggiunge complessità al formato JSON
  • Dal momento che non è un oggetto di dati più, ma più come una risposta avvolto , non possiamo usare immediatamente la risposta in f.ex backbone.js senza estrarre l'oggetto dati.

Domanda

Qual è la best practice per aggiungere i metadati a una risposta JSON?

UPDATE

quello che ha così lontano da risposte di seguito:

  • Rimuovere il metadata.status un rendimento del codice di risposta HTTP nel protocollo http invece (200, 500 ...
  • Aggiungere un messaggio di errore al corpo di un risponditore http 500
  • Per l'impaginazione, è naturale avere alcuni metadati che descrivono la struttura di impaginazione e dati annidati in quella struttura
  • piccola quantità di metadati possono essere aggiunti alla intestazione http (X-qualcosa)
+0

Sei consapevole che il tuo esempio non è JSON? – JeremyP

+0

grazie, è aggiornato –

+1

Puoi approfondire i tuoi potenziali casi d'uso e perché il codice di stato HTTP e l'impostazione di intestazioni di risposta appropriate non sarebbero adatti? – Charlie

risposta

0

Abbiamo avuto lo stesso caso d'uso, in cui avevamo bisogno di aggiungere metadati impaginazione ad una risposta JSON. Abbiamo finito per creare un tipo di raccolta in Backbone che potesse gestire questi dati e un wrapper leggero sul lato Rails. Questo esempio aggiunge semplicemente i metadati all'oggetto di raccolta per riferimento dalla vista.

Così abbiamo creato una classe Backbone Collection qualcosa di simile

// Example response: 
// { num_pages: 4, limit_value: 25, current_page: 1, total_count: 97 
// records: [{...}, {...}] } 

PageableCollection = Backbone.Collection.extend({ 
    parse: function(resp, xhr) { 
    this.numPages = resp.num_pages; 
    this.limitValue = resp.limit_value; 
    this.currentPage = resp.current_page; 
    this.totalCount = resp.total_count; 
    return resp.records; 
    } 
}); 

E poi abbiamo creato questa semplice classe sul lato Rails, per emettere i metadati quando impaginato con Kaminari

class PageableCollection 
    def initialize (collection) 
    @collection = collection 
    end 
    def as_json(opts = {}) 
    { 
     :num_pages => @collection.num_pages 
     :limit_value => @collection.limit_value 
     :current_page => @collection.current_page, 
     :total_count => @collection.total_count 
     :records => @collection.to_a.as_json(opts) 
    } 
    end 
end 

È usalo in un controller come questo

class ThingsController < ApplicationController 
    def index 
    @things = Thing.all.page params[:page] 
    render :json => PageableCollection.new(@things) 
    end 
end 

Divertiti. Spero che tu lo trovi utile

+0

Non sarebbe meglio aggiungere collegamenti di paginazione nella risposta piuttosto che incorporare la logica di richiesta di paginazione nel client? –

4

Lungo le linee di @ commento di Charlie: per la parte impaginazione della tua domanda è ancora necessario per cuocere i metadati nel somhow risposta, ma le status e message attributi qui sono un po 'ridondante, dal momento che sono già coperti dalla HTTP protocollo stesso (stato 200 - modello trovato, 404 - modello non trovato, 403 - privilegi insufficienti, si ottiene l'idea) (vedere spec). Anche se il server restituisce una condizione di errore, è comunque possibile inviare la parte message come corpo della risposta. Questi due campi copriranno gran parte dei tuoi bisogni di metadati.

Personalmente, ho teso verso (ab) utilizzando intestazioni HTTP personalizzate per piccoli pezzi di metadati (con un prefisso X-), ma immagino che il limite in cui ciò non sia pratico sia piuttosto basso.

ho expanded un po 'su questo in una domanda con un ambito più piccolo, ma penso che i punti sono ancora validi per questa domanda.

+0

Apparentemente il prefisso con 'X-' è [non più raccomandato] (http://stackoverflow.com/a/3561399/320036). – z0r

+1

@ z0r Il reclamo è controverso, secondo le risposte dal link. –

8

avete diversi mezzi per passare metadati in un API RESTful:

  1. codice di stato HTTP
  2. intestazioni
  3. corpo della risposta

Per la metadata.status, utilizzare lo stato HTTP Codice, ecco a cosa serve! Se i metadati si riferiscono all'intera risposta, è possibile aggiungerli come campi di intestazione. Se i metadati si riferiscono solo a parte della risposta, è necessario incorporare i metadati come parte dell'oggetto. NON avvolgere l'intera risposta in una busta artificiale e dividere il wrapper in dati e metadati.

E, infine, essere coerente in tutta l'API con le scelte che si fanno.

Un buon esempio è un GET su un'intera collezione con paginazione. GET/items È possibile restituire la dimensione della raccolta e la pagina corrente nelle intestazioni personalizzate. E link di paginazione in intestazione standard link:

Link: <https://api.mydomain.com/v1/items?limit=25&offset=25>; rel=next 

Il problema di questo approccio è quando è necessario aggiungere i metadati riferimento a elementi specifici nella risposta. In tal caso, basta incorporarlo nell'oggetto stesso. E per avere un approccio coerente ... aggiungi sempre tutti i metadati alla risposta.Quindi, tornando alle GET/elementi, immaginate che ogni elemento ha creato e metadati aggiornati:

{ 
    items:[ 
    { 
     "id":"w67e87898dnkwu4752igd", 
     "message" : "some content", 
     "_created": "2014-02-14T10:07:39.574Z", 
     "_updated": "2014-02-14T10:07:39.574Z" 
    }, 
    ...... 
    { 
     "id":"asjdfiu3748hiuqdh", 
     "message" : "some other content", 
     "_created": "2014-02-14T10:07:39.574Z", 
     "_updated": "2014-02-14T10:07:39.574Z" 
    } 
    ], 
    "_total" :133, 
    "_links" :[ 
    { 
     "next" :{ 
      href : "https://api.mydomain.com/v1/items?limit=25&offset=25" 
     } 
    ] 
} 

Nota che una risposta collezione è un caso speciale. Se aggiungi metadati a una raccolta, la raccolta non può più essere restituita come array, deve essere un oggetto con una matrice al suo interno. Perché un oggetto? perché vuoi aggiungere alcuni attributi di metadati.

Confronto con i metadati nei singoli articoli. Niente è vicino al confezionamento dell'entità. Devi solo aggiungere alcuni attributi alla risorsa.

Una convenzione è di differenziare i campi di controllo o di metadati. Potresti inserire come prefisso quei campi con un trattino basso.

Problemi correlati