Contexte
Nous construisons une API reposante qui devrait renvoyer des objets de données au format JSON. Dans la plupart des cas, il suffit de renvoyer l'objet de données, mais dans certains cas, f.ex. pagination ou validation, nous devons ajouter des métadonnées à la réponse.
Ce que nous avons jusqu'à présent
Nous avons enveloppé toutes les réponses Json comme dans cet exemple:
{
"metadata" :{
"status": 200|500,
"msg": "Some message here",
"next": "http://api.domain.com/users/10/20"
...
},
"data" :{
"id": 1001,
"name": "Bob"
}
}
Avantages
Les inconvénients
Question
Quelles sont les meilleures pratiques pour ajouter des métadonnées à une réponse json?
METTRE À JOUR
Ce que j'ai si loin des réponses ci-dessous:
metadata.status
et retournez le code de réponse http dans le protocole Http à la place (200, 500 ...)Vous avez plusieurs moyens de transmettre des métadonnées dans une API RESTful:
Pour le fichier metadata.status, utilisez le code d’état Http, c’est le but de! Si métadonnées fait référence à la réponse entière, vous pouvez l’ajouter sous forme de champs d’en-tête . Si les métadonnées ne font référence qu’à une partie de la réponse, vous doivent incorporer les métadonnées dans l’objet. NE PAS FAIRE envelopper toute la réponse dans une enveloppe artificielle et diviser l’enveloppe en données et métadonnées.
Et enfin, soyez cohérent dans votre API avec les choix que vous faites.
Un bon exemple est un GET sur toute une collection avec pagination. GET /itemsVous pouvez renvoyer la taille de la collection et la page en cours dans des en-têtes personnalisés. Et des liens de pagination en en-tête de lien standard:
Link: <https://api.mydomain.com/v1/items?limit=25&offset=25>; rel=next
Le problème avec cette approche est lorsque vous devez ajouter des métadonnées référençant des éléments spécifiques dans la réponse. Dans ce cas, il suffit de l'intégrer à l'objet lui-même. Et pour avoir une approche cohérente ... ajoutez toujours toutes les métadonnées à la réponse. Pour revenir à l'élément GET/items, imaginons que chaque élément a créé et mis à jour des métadonnées:
{
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"
}
]
}
Notez qu'une réponse à la collecte est un cas particulier. Si vous ajoutez des métadonnées à une collection, celle-ci ne peut plus être renvoyée sous forme de tableau. Il doit s'agir d'un objet contenant un tableau. Pourquoi un objet? parce que vous voulez ajouter des attributs de métadonnées.
Comparez avec les métadonnées dans les éléments individuels. Rien n'est proche d'envelopper l'entité. Vous ajoutez juste quelques attributs à la ressource.
Une convention consiste à différencier les champs de contrôle ou de métadonnées. Vous pouvez préfixer ces champs avec un trait de soulignement.
Dans le sens du commentaire de @ Charlie: pour la partie relative à la pagination de votre question, vous devez toujours intégrer les métadonnées dans la réponse, mais les attributs status
et message
sont quelque peu redondants, car ils sont déjà couverts par le protocole HTTP
200
- modèle trouvé, 404
- modèle non trouvé, 403
- nombre de privilèges insuffisant, vous avez une idée) (voir spec ). Même si votre serveur renvoie une condition d'erreur, vous pouvez toujours envoyer la partie message
en tant que corps de la réponse. Ces deux champs couvriront une grande partie de vos besoins en métadonnées.
Personnellement, j'ai tendance à utiliser (ab) des en-têtes HTTP personnalisés pour des métadonnées plus petites (avec un préfixe X-
), mais j'imagine que la limite lorsque cela devient impraticable est assez basse.
J'ai développé un peu à ce sujet dans une question avec une portée plus petite, mais je pense que les points sont toujours valables pour cette question.
Nous avions le même cas d'utilisation, dans lequel nous devions ajouter des métadonnées de pagination à une réponse JSON. Nous avons fini par créer un type de collection dans Backbone capable de gérer ces données, ainsi qu'un wrapper léger côté Rails. Cet exemple ajoute simplement les métadonnées à l'objet collection pour référence par la vue.
Nous avons donc créé une classe Backbone Collection qui ressemble à ceci
// 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;
}
});
Et puis nous avons créé cette classe simple du côté de Rails, pour émettre les métadonnées quand paginé avec 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
Vous l'utilisez dans un contrôleur comme celui-ci
class ThingsController < ApplicationController
def index
@things = Thing.all.page params[:page]
render :json => PageableCollection.new(@things)
end
end
Prendre plaisir. J'espère que vous le trouverez utile.