webentwicklung-frage-antwort-db.com.de

Was sind die bewährten Methoden zum Hinzufügen von Metadaten zu einer RESTful-JSON-Antwort?

Hintergrund

Wir erstellen eine Restful-API, die Datenobjekte als JSON zurückgeben soll. In den meisten Fällen reicht es aus, nur das Datenobjekt zurückzugeben, in einigen Fällen jedoch z. Bei der Paginierung oder Validierung müssen der Antwort einige Metadaten hinzugefügt werden.

Was wir bisher haben

Wir haben alle json-Antworten wie folgt zusammengefasst:

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

Profis

  • Wir können der Antwort hilfreiche Metadaten hinzufügen

Nachteile

  • In den meisten Fällen wird das Metadatenfeld nicht benötigt, wodurch das json-Format komplexer wird
  • Da es sich nicht mehr um ein Datenobjekt handelt, sondern eher um eine umhüllte Antwort, können wir die Antwort nicht sofort in f.ex backbone.js verwenden, ohne das Datenobjekt zu extrahieren.

Frage

Was sind die bewährten Methoden zum Hinzufügen von Metadaten zu einer JSON-Antwort?

UPDATE

Was ich bisher noch nicht beantwortet habe:

  • Entfernen Sie den metadata.status und geben Sie stattdessen den http-Antwortcode im http-Protokoll zurück (200, 500 ...)
  • Fügen Sie dem Hauptteil einer http 500-Antwort eine Fehlermeldung hinzu
  • Für die Paginierung müssen natürlich einige Metadaten über die Paginierungsstruktur und die in dieser Struktur verschachtelten Daten vorhanden sein
  • Eine kleine Menge von Metadaten kann zum http-Header hinzugefügt werden (X-something)
36
user920041

Sie haben mehrere Möglichkeiten, Metadaten in einer RESTful-API zu übergeben:

  1. HTTP-Statuscode
  2. Kopfzeilen
  3. Response Body

Verwenden Sie für den Status metadata.status den HTTP-Statuscode. Dies ist der Zweck von! Wenn Metadaten auf die gesamte Antwort verweisen, können Sie sie als Headerfelder hinzufügen. Wenn Metadaten nur einen Teil der Antwort, müssen Sie die Metadaten als Teil des Objekts einbetten. DON'T verpackt die gesamte Antwort in einen künstlichen Umschlag und teilt den Wrapper in Daten und Metadaten auf.

Und schließlich - über Ihre API konsistent sein mit den von Ihnen getroffenen Entscheidungen.

Ein gutes Beispiel ist ein GET für eine ganze Sammlung mit Paginierung. GET /items[.____.(Sie können die Größe der Sammlung und die aktuelle Seite in benutzerdefinierten Kopfzeilen zurückgeben. Und Paginierungs-Links im Standard-Link-Header:

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

Das Problem bei diesem Ansatz besteht darin, dass Sie Metadaten hinzufügen müssen, die auf bestimmte Elemente in der Antwort verweisen. In diesem Fall binden Sie es einfach in das Objekt selbst ein. Und um einen einheitlichen Ansatz zu haben ... fügen Sie der Antwort immer alle Metadaten hinzu. Um zum GET/items zurückzukehren, stellen Sie sich vor, dass jedes Element Metadaten erstellt und aktualisiert hat:

{
  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"
         } 
   ]
}

Beachten Sie, dass eine Sammlungsantwort ein Sonderfall ist. Wenn Sie einer Auflistung Metadaten hinzufügen, kann die Auflistung nicht mehr als Array zurückgegeben werden. Es muss sich um ein Objekt mit einem Array handeln. Warum ein Objekt? weil Sie einige Metadatenattribute hinzufügen möchten.

Vergleichen Sie mit den Metadaten in den einzelnen Elementen. Nichts in der Nähe der Entity. Sie fügen der Ressource einfach einige Attribute hinzu.

Eine Konvention besteht darin, Kontroll- oder Metadatenfelder zu unterscheiden. Sie können diesen Feldern einen Unterstrich voranstellen.

9
Daniel Cerecedo

Ganz im Sinne von @ Charlies Kommentar: Für den Teil der Paginierung Ihrer Frage müssen Sie die Metadaten trotzdem irgendwie in die Antwort einfrieren, aber die status- und message-Attribute sind hier etwas überflüssig, da sie bereits durch das HTTP-Protokoll selbst (Status) abgedeckt sind 200 - Modell gefunden, 404 - Modell nicht gefunden, 403 - unzureichende Berechtigungen, Sie bekommen die Idee) (siehe spec ). Selbst wenn Ihr Server eine Fehlerbedingung zurückgibt, können Sie den message-Teil dennoch als Antworttext senden. Diese beiden Felder decken einen Großteil Ihres Metadatenbedarfs ab.

Ich persönlich habe eher (ab) versucht, benutzerdefinierte HTTP-Header für kleinere Metadaten (mit einem X--Präfix) zu verwenden, aber ich denke, das Limit, bei dem dies unpraktisch wird, ist ziemlich niedrig.

Ich habe erweitert ein wenig darüber in einer Frage mit einem kleineren Umfang, aber ich denke, die Punkte sind für diese Frage noch gültig.

4
Jacob Oscarson

Ich empfehle Ihnen, diese Seite zu lesen https://www.odata.org/ Sie sind nicht gezwungen, OData zu verwenden, aber die Art und Weise, wie sie die Arbeit erledigen, ist ein gutes Beispiel für eine gute Praxis mit REST.

0

Wir hatten den gleichen Anwendungsfall, in dem wir einer JSON-Antwort Paginierungsmetadaten hinzufügen mussten. Wir haben am Ende einen Sammlungstyp in Backbone erstellt, der diese Daten verarbeiten kann, und einen leichten Wrapper auf der Rails-Seite. In diesem Beispiel werden die Metadaten dem Auflistungsobjekt lediglich als Referenz für die Ansicht hinzugefügt.

So haben wir eine Backbone Collection-Klasse erstellt

// 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;
  }  
});

Und dann haben wir diese einfache Klasse auf der Rails-Seite erstellt, um die Metadaten auszusenden, wenn sie mit Kaminari paginiert werden

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

Sie verwenden es in einem Controller wie diesem

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

Genießen. Ich hoffe, Sie finden es nützlich.

0
maxl0rd