Comment concevoir une recherche / filtrage RESTful?

Je suis actuellement en train de concevoir et d’implémenter une API RESTful en PHP. Cependant, j’ai échoué dans la mise en œuvre de ma conception initiale.

GET /users # list of users GET /user/1 # get user with id 1 POST /user # create new user PUT /user/1 # modify user with id 1 DELETE /user/1 # delete user with id 1 

Jusqu’à présent assez standard, non?

Mon problème est avec le premier GET /users . J’envisageais d’envoyer des parameters dans le corps de la requête pour filtrer la liste. C’est parce que je veux pouvoir spécifier des filtres complexes sans obtenir une URL super longue, comme:

 GET /users?parameter1=value1&parameter2=value2&parameter3=value3&parameter4=value4 

Au lieu de cela, je voulais avoir quelque chose comme:

 GET /users # Request body: { "parameter1": "value1", "parameter2": "value2", "parameter3": "value3", "parameter4": "value4" } 

ce qui est beaucoup plus lisible et vous offre de grandes possibilités pour définir des filtres complexes.

Quoi qu’il en soit, file_get_contents('php://input') n’a pas renvoyé le corps de la requête pour GET requêtes GET . J’ai aussi essayé http_get_request_body() , mais l’hébergement partagé que j’utilise n’a pas pecl_http . Pas sûr que cela aurait aidé de toute façon.

J’ai trouvé cette question et réalisé que GET n’est probablement pas censé avoir un corps de requête. C’était peu concluant, mais ils l’ont déconseillé.

Alors maintenant je ne sais pas quoi faire. Comment concevez-vous une fonction de recherche / filterng RESTful?

Je suppose que je pourrais utiliser POST , mais cela ne semble pas très efficace.

La meilleure façon d’implémenter une recherche RESTful consiste à considérer la recherche elle-même comme une ressource. Ensuite, vous pouvez utiliser le verbe POST parce que vous créez une recherche. Vous n’avez pas besoin de créer littéralement quelque chose dans une firebase database pour utiliser un POST.

Par exemple:

 Accept: application/json Content-Type: application/json POST http://example.com/people/searches { "terms": { "ssn": "123456789" }, "order": { ... }, ... } 

Vous créez une recherche du sharepoint vue de l’utilisateur. Les détails de la mise en œuvre de ceci ne sont pas pertinents. Certaines API RESTful peuvent même ne pas nécessiter de persistance. C’est un détail d’implémentation.

Si vous utilisez le corps de la requête dans une requête GET, vous ne respectez pas le principe REST, car votre requête GET ne pourra pas être mise en cache, car le système de cache utilise uniquement l’URL.

Et pire, votre URL ne peut pas être mise en signet, car l’URL ne contient pas toutes les informations nécessaires pour redirect l’utilisateur vers cette page.

Utilisez les parameters URL ou Query au lieu des parameters du corps de la requête.

par exemple:

 /myapp?var1=xxxx&var2=xxxx /myapp;var1=xxxx/resource;var2=xxxx 

En fait, le HTTP RFC 7231 dit que:

Une charge utile dans un message de requête GET n’a pas de sémantique définie; l’envoi d’un corps de charge utile sur une requête GET peut entraîner le rejet de certaines demandes par certaines implémentations existantes.

Pour plus d’informations, regardez ici

Il semble que le filtrage / la recherche de ressources puisse être implémenté de manière REST. L’idée est d’introduire un nouveau noeud final appelé /filters/ ou /api/filters/ .

L’utilisation de ce filtre de sharepoint terminaison peut être considérée comme une ressource et donc créée via la méthode POST . Cette façon de procéder peut être utilisée pour transporter tous les parameters et des structures complexes de recherche / filtrage peuvent être créées.

Après avoir créé un tel filtre, il existe deux possibilités pour obtenir le résultat de la recherche / filtre.

  1. Une nouvelle ressource avec un identifiant unique sera renvoyée avec le code d’état 201 Created . Ensuite, en utilisant cet ID, une requête GET peut être faite à /api/users/ like:

     GET /api/users/?filterId=1234-abcd 
  2. Après la création d’un nouveau filtre via POST il ne répondra pas avec 201 Created mais immédiatement avec 303 SeeOther avec l’en-tête Location pointant vers /api/users/?filterId=1234-abcd . Cette redirection sera automatiquement gérée via la bibliothèque sous-jacente.

Dans les deux scénarios, deux demandes doivent être faites pour obtenir les résultats filtrés – cela peut être considéré comme un inconvénient, en particulier pour les applications mobiles. Pour les applications mobiles, j’utiliserais un seul appel POST à /api/users/filter/ .

Comment conserver les filtres créés?

Ils peuvent être stockés dans la firebase database et utilisés ultérieurement. Ils peuvent également être stockés dans un espace de stockage temporaire, par exemple redis, et avoir un TTL après lequel ils expireront et seront supprimés.

Quels sont les avantages de cette idée?

Les filtres, les résultats filtrés peuvent être mis en cache et peuvent même être mis en signet.

Je pense que vous devriez aller avec les parameters de demande, mais seulement tant qu’il n’y a pas d’en-tête HTTP approprié pour accomplir ce que vous voulez faire. La spécification HTTP ne dit pas explicitement que GET ne peut pas avoir de corps. Cependant, cet article indique:

Par convention, lorsque la méthode GET est utilisée, toutes les informations requirejses pour identifier la ressource sont codées dans l’URI. Il n’y a pas de convention dans HTTP / 1.1 pour une interaction sûre (par exemple, récupération) où le client fournit des données au serveur dans un corps d’entité HTTP plutôt que dans la partie requête d’un URI. Cela signifie que pour des opérations sécurisées, les URI peuvent être longs.

Ne vous inquiétez pas trop si votre API initiale est entièrement RESTful ou non (spécialement quand vous êtes juste dans les étapes alpha). Faites en sorte que la plomberie principale fonctionne en premier. Vous pouvez toujours faire une sorte de transformation / réécriture d’URL pour mapper les choses, en les affinant de manière itérative jusqu’à ce que vous obteniez quelque chose d’assez stable pour des tests étendus (“beta”).

Vous pouvez définir des URI dont les parameters sont encodés par position et par convention sur les URI eux-mêmes, préfixés par un chemin que vous savez toujours mapper sur quelque chose. Je ne connais pas PHP, mais je suppose qu’une telle installation existe (comme dans d’autres langages avec des frameworks Web):

.c’est à dire. Effectuez une recherche de type “utilisateur” avec param [i] = valeur [i] pour i = 1..4 sur le magasin # 1 (avec valeur1, valeur2, valeur3, … comme raccourci pour les parameters de requête URI):

 1) GET /store1/search/user/value1,value2,value3,value4 

ou

 2) GET /store1/search/user,value1,value2,value3,value4 

ou comme suit (bien que je ne le recommande pas, plus tard)

 3) GET /search/store1,user,value1,value2,value3,value4 

Avec l’option 1, vous /store1/search/user tous les URI préfixés par /store1/search/user sur le gestionnaire de recherche (ou sur la désignation PHP) pour effectuer des recherches sur les ressources sous store1 (équivalent à /search?location=store1&type=user .

Par convention documentée et appliquée par l’API, les parameters 1 à 4 sont séparés par des virgules et présentés dans cet ordre.

L’option 2 ajoute le type de recherche (dans ce cas, l’ user ) en tant que paramètre positionnel # 1. Chacune des deux options est juste un choix cosmétique.

L’option 3 est également possible, mais je ne pense pas que cela me plairait. Je pense que la capacité de recherche dans certaines ressources devrait être présentée dans l’URI lui-même précédant la recherche elle-même (comme si cela indiquait clairement dans l’URI que la recherche est spécifique dans la ressource).

L’avantage de ceci sur le passage de parameters sur l’URI est que la recherche fait partie de l’URI (traitant ainsi une recherche comme une ressource, une ressource dont le contenu peut – et va – changer dans le temps. .

Une fois que vous faites quelque chose comme ça, vous pouvez utiliser GET, et ce serait une ressource en lecture seule (puisque vous ne pouvez pas la lancer ou la mettre – elle est mise à jour lorsqu’elle est configurée). Ce serait aussi une ressource qui n’existe que lorsqu’elle est invoquée.

On pourrait également y append plus de sémantique en mettant en cache les résultats pendant un certain temps ou avec un DELETE provoquant la suppression du cache. Cela peut toutefois aller à l’encontre de ce que les utilisateurs utilisent habituellement pour DELETE (et parce que les utilisateurs contrôlent généralement la mise en cache avec des en-têtes de mise en cache).

La façon dont vous allez procéder serait une décision de conception, mais ce serait comme ça que je le ferais. Ce n’est pas parfait, et je suis sûr qu’il y aura des cas où ce n’est pas la meilleure chose à faire (spécialement pour des critères de recherche très complexes).

Comme j’utilise un backend laravel / php, j’ai tendance à utiliser quelque chose comme ceci:

 /resource?filters[status_id]=1&filters[city]=Sydney&page=2&include=relatedResource 

PHP transforme automatiquement [] params en un tableau, donc dans cet exemple, je vais me retrouver avec une variable $filter qui contient un tableau / object de filtres, avec une page et toutes les ressources associées que je veux impatient de charger.

Si vous utilisez un autre langage, cela peut toujours être une bonne convention et vous pouvez créer un parsingur pour convertir [] en tableau.

FYI: Je sais que c’est un peu tard, mais pour quiconque est intéressé. Cela dépend de votre RESTful, vous devrez implémenter vos propres stratégies de filtrage car les spécifications HTTP ne sont pas très claires à ce sujet. Je voudrais suggérer l’encodage d’url tous les parameters de filtre, par exemple

 GET api/users?filter=param1%3Dvalue1%26param2%3Dvalue2 

Je sais que c’est moche mais je pense que c’est la façon la plus REST de le faire et devrait être facile à parsingr du côté du serveur 🙂