Concevoir une API de requête RESTful avec une longue liste de paramètres de requête [fermé]

153

J'ai besoin de concevoir une API de requête RESTful, qui renvoie un ensemble d'objets basé sur quelques filtres. La méthode HTTP habituelle pour cela est GET. Le seul problème est qu'il peut avoir au moins une douzaine de filtres, et si nous les passons tous en tant que paramètres de requête, l'URL peut devenir assez longue (assez longue pour être bloquée par un pare-feu).

La réduction du nombre de paramètres n'est pas une option.

Une alternative à laquelle je pourrais penser est d'utiliser la méthode POST sur l'URI et d'envoyer les filtres dans le cadre du corps POST. Est-ce contre RESTfull (faire un appel POST pour interroger des données)

Quelqu'un a-t-il de meilleures suggestions de conception?

missionE46
la source
2
Utiliser des noms de paramètres courts (1-caractère, etc.)?
Madbreaks
2
Ce n'est peut-être pas vraiment REST, mais je pense que vous devez être pratique en ce qui concerne les GET et les POST. Si vous avez autant de variables à envoyer et que vous ne pouvez pas les réduire, je les POSTERIE. Je n'aime pas trop remplir l'URL, mais ce n'est que moi.
Doug Dawson
Merci. Même si cette question est fermée, c'est EXACTEMENT la question à laquelle j'avais besoin d'une réponse. Je suis ravi que vous posiez cette question.
Casey Crookston le

Réponses:

142

N'oubliez pas qu'avec une API REST, tout dépend de votre point de vue.

Les deux concepts clés d'une API REST sont les points de terminaison et les ressources (entités). En gros, un point de terminaison retourne des ressources via GET ou accepte des ressources via POST et PUT et ainsi de suite (ou une combinaison des éléments ci-dessus).

Il est admis qu'avec POST, les données que vous envoyez peuvent ou non entraîner la création d'une nouvelle ressource et de son ou ses points de terminaison associés, qui ne «vivront» probablement pas sous l'URL POSTée. En d'autres termes, lorsque vous POSTEZ, vous envoyez des données quelque part pour les manipuler. Le point de terminaison POST n'est pas l'endroit où la ressource peut normalement être trouvée.

Citant la RFC 2616 (avec les parties non pertinentes omises et les parties pertinentes mises en évidence):

9.5 POST

La méthode POST est utilisée pour demander que le serveur d'origine accepte l'entité incluse dans la demande en tant que nouveau subordonné de la ressource identifiée par l'URI de demande dans la ligne de demande. POST est conçu pour permettre à une méthode uniforme de couvrir les fonctions suivantes:

  • ...
  • Fournir un bloc de données, tel que le résultat de la soumission d'un formulaire, à un processus de traitement de données;
  • ...

...

L'action effectuée par la méthode POST peut ne pas aboutir à une ressource identifiable par un URI . Dans ce cas, 200 (OK) ou 204 (Pas de contenu) est le statut de réponse approprié, selon que la réponse inclut ou non une entité qui décrit le résultat .

Si une ressource a été créée sur le serveur d'origine, la réponse DEVRAIT être 201 (Créé) ...

Nous nous sommes habitués aux points de terminaison et aux ressources représentant des «choses» ou des «données», que ce soit un utilisateur, un message, un livre - quel que soit le domaine du problème. Cependant, un point de terminaison peut également exposer une ressource différente, par exemple des résultats de recherche.

Prenons l'exemple suivant:

GET    /books?author=AUTHOR
POST   /books
PUT    /books/ID
DELETE /books/ID

Il s'agit d'un CRUD REST typique. Mais que faire si nous ajoutions:

POST /books/search

    {
        "keywords": "...",
        "yearRange": {"from": 1945, "to": 2003},
        "genre": "..."
    }

Il n'y a rien de non-RESTful à propos de ce point de terminaison. Il accepte des données (entité) sous la forme du corps de la requête. Ces données sont les critères de recherche - un DTO comme les autres. Ce point de terminaison produit une ressource (entité) en réponse à la demande: Résultats de la recherche . La ressource de résultats de recherche est temporaire, servie immédiatement au client, sans redirection et sans être exposée à partir d'une autre URL canonique.

Il s'agit toujours de REST, sauf que les entités ne sont pas des livres - l'entité de demande est des critères de recherche de livres et l'entité de réponse est des résultats de recherche de livres.

Amir Abiri
la source
Pouvez-vous suggérer des conventions de dénomination de classes pour le DTO?
Kwadz
Personnellement, j'irais avec BooksSearchCriteriaDTOet BooksSearchResultsDTO.
Amir Abiri
quel serait le meilleur code de réponse HTTP pour ce cas de POST / books / search? 201 s'applique toujours?
L. Holanda
9
201 est le contraire - cela implique qu'une ressource a été créée. Une ressource qui devrait avoir son propre URI unique quelque part. 201 convient lorsque le POSTest utilisé pour la partie C de CRUD. J'irais avec le vieux 200, éventuellement avec 204 pour les résultats de recherche vides.
Amir Abiri
@AmirAbiri merci beaucoup.
mohamed-mhiri
84

Beaucoup de gens ont accepté la pratique selon laquelle un GET avec une chaîne de requête trop longue ou trop complexe (par exemple, les chaînes de requête ne gèrent pas facilement les données imbriquées) peut être envoyé en tant que POST à ​​la place, avec les données complexes / longues représentées dans le corps de la demande.

Recherchez la spécification POST dans la spécification HTTP. C'est incroyablement large. (Si vous voulez naviguer sur un cuirassé à travers une faille dans REST ... utilisez POST.)

Vous perdez certains des avantages de la sémantique GET ... comme les tentatives automatiques parce que GET est idempotent, mais si vous pouvez vivre avec cela, il pourrait être plus facile d'accepter le traitement de requêtes très longues ou compliquées avec POST.

(lol longue digression ... J'ai récemment découvert que par la spécification HTTP, GET peut contenir un corps de document. Il y a une section qui dit, paraphrasant, "Toute requête peut avoir un corps de document sauf ceux listés dans cette section" ... et la section à laquelle il se réfère n'en répertorie aucun. J'ai cherché et trouvé un fil de discussion où les auteurs HTTP en parlaient, et c'était intentionnel, de sorte que les routeurs et autres n'auraient pas à faire la différence entre les différents messages. Cependant, dans pratiquez beaucoup de pièces d'infrastructure font tomber le corps d'un GET. Vous pouvez donc GET avec des filtres représentés dans le corps, comme POST, mais vous lanceriez les dés.)

Rob
la source
11
Voir aussi cette question pour plus de discussion sur HTTP GET avec body.
RickyA
6

En un mot: créez un POST mais remplacez la méthode HTTP à l'aide de l'en- tête X-HTTP-Method-Override .

Vraie demande

POST / livres

Corps d'entité

{"title": "Ipsum", "year": 2017}

En-têtes

X-HTTP-Method-Override: GET

Du côté du serveur, vérifiez si l'en-tête X-HTTP-Method-Override existe, puis prenez sa valeur comme méthode pour construire la route vers le point de terminaison final dans le backend. Prenez également le corps de l'entité comme chaîne de requête. D'un point de vue backend, la requête est devenue un simple GET.

De cette façon, vous gardez la conception en harmonie avec les principes REST.

Edit: Je sais que cette solution était à l'origine destinée à résoudre le problème du verbe PATCH dans certains navigateurs et serveurs, mais cela fonctionne aussi pour moi avec le verbe GET dans le cas d'une URL très longue qui est le problème décrit dans la question.

Delmo
la source
2
En-têtes HTTP avec préfixe X obsolète par l'IETF: tools.ietf.org/html/rfc6648
jannis
@jannis Le RFC que vous liez reste 1.4. il ne fait aucune recommandation sur la X-suppression existante et 1.5. il ne remplace pas les spécifications existantes. ... X-l'OMI restera-t-elle ici.
Jan Molnar
-3

Si vous développez en Java et JAX-RS, je vous recommande d'utiliser @QueryParam avec @GET

J'avais la même question quand j'avais besoin de parcourir une liste.

Voir exemple:

import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;

@Path("/poc")
public class UserService {

    @GET
    @Path("/test/")
    @Produces(MediaType.APPLICATION_JSON)
    public Response test(@QueryParam("code") final List<Integer> code) {
                Integer int0 = codigo.get(0);
                Integer int1 = codigo.get(1);

        return Response.ok(new JSONObject().put("int01", int0)).build();
    }
}

Modèle d'URI: "poc / test? Code = 1 & code = 2 & code = 3

@QueryParam convertira automatiquement le paramètre de requête «orderBy = age & orderBy = name» en java.util.List.

acacio.martins
la source
Il vaudrait mieux que vous expliquiez votre exemple. Dans quel langage de programmation est-il écrit?
Aleks Andreev
Salut @AleksAndreev. Merci pour votre avis. Ça s'est amélioré? tks
acacio.martins
Cette question concerne la conception du service RESTful, pas la mise en œuvre. Cette réponse ne répond pas à la question.
Heretic Monkey
@ user1331413 IMHO oui, maintenant c'est mieux. Merci pour vos efforts. Cependant, comme l'a dit Mike McCaughan, la question porte sur le concept REST, plutôt que sur la mise en œuvre
Aleks Andreev