web-dev-qa-db-fra.com

REST Conception d'URL pour plus que, moins que opérations

J'ai un peu de difficulté à concevoir une URL pour un service de repos pouvant traiter les demandes de clients basées sur la pagination en tant que type d'opération ou en demandant à des opérateurs supérieurs ou inférieurs à un autre type d'opération. Par exemple:

Pagination:

GET /customers/0/100

Cela donnera 100 clients pour la page 0.

Supérieur/inférieur à:

J'ai également besoin d'une conception d'URL pour que les clients dont l'identifiant est supérieur à n (par exemple, 716). Comment voulez-vous incorporer "plus grand que" ou "moins que" dans une URL? Je dois garder à l'esprit que les caractères ">" et "<" sont illégaux dans les URL. Je pense que cette conception d'URL a l'air bizarre:

GET /customers/greaterthan/716
GET /customers/lessthan/716

Je ne peux pas utiliser une plage car cela serait en conflit avec le modèle de pagination spécifié ci-dessus et ce n'est pas une solution de Nice dans tous les cas, par exemple:

GET /customers/716/999999999999
GET /customers/0/716

Je suis sûr qu'il me manque quelque chose d'évident: quelqu'un a-t-il une meilleure solution?

23
Vidar

Pagination , supérieur à et lessthan , sonne comme un paramètre de requête pour moi, puisque vous interrogez votre ressource avec ces paramètres. Donc, vous devriez faire quelque chose comme:

/clients? page = 1 , ou
/clients? page = 1 & gt = 716 , ou
/clients? page = 1 & gt = 716 & lt = 819  

Vous pouvez même limiter la taille de la page:

/clients? page = 1 & gt = 716 & lt = 819 & maxpagesize = 100  

gt signifie supérieur à (identique à xml-escaping) et lt signifie inférieur à.

28
Tarlog

Si vous avez plusieurs paramètres et que vous devez appliquer certaines conditions à chaque paramètre, je vous recommande de transmettre un objet JSON à ce paramètre.

Considérez que vous souhaitez créer une condition pour id et la page:

/customers?id={"lt": 100, "gt": 30}&page={"start": 1, "size": 10}

Il dit que je veux les clients dont les identifiants inférieur à 100 et supérieur à 30 dans la page 1 et numéro de page de 10.

Alors maintenant, si vous voulez appliquer une autre condition pour d'autres paramètres, vous pouvez le faire en:

/customers?id={"lt": 100, "gt": 30}&children={"lt": 5, "gt": 2}&page={"start": 1, "size": 10}

et cette requête concerne les clients dont le ou les identifiants sont inférieurs à 100 et supérieurs à 30, enfants inférieurs à 5 et supérieurs à 2. dans le numéro de page 1 avec taille de la page sur 10.

Je vous recommande vivement de lire ce document sur la conception de l'API RESTful: http://blog.luisrei.com/articles/rest.html

16
Afshin Mehrabani

REST est un style architectural qui ne doit pas être considéré comme spécifique à HTTP. Le modèle des URI n'est pas ce qui rend une architecture RESTful.

Cela dit, vous voudrez probablement créer votre URI de sorte que ces requêtes viennent en tant que paramètres de requête à la fin de la chaîne, par exemple.

/customers?min=0&max=76
7
pc1oad1etter

@ Julio Faerman:

Eh bien, le problème commence lorsque vous obtenez plusieurs paramètres. Imaginez la chaîne de requête "Clients âgés de plus de 18 ans et de moins de 60 ans ayant plus de 2 enfants".

Vous pouvez définir les paramètres de requête de votre choix, tels que:

/customers?min-age=19&max-age=59&min-children=3

dans mon exemple, min et max sont des entiers et sont inclusifs. Vous pouvez changer cela si vous préférez. N'oubliez pas que tout ce qui est dans l'URI implique une partie de l'identifiant de la ressource. Mon opinion personnelle est que ce qui suit le ? équivaut à des clauses de la partie WHERE d'une requête SQL (plus ORDER BY et LIMIT, non affichées ici):

SELECT * FROM customers WHERE age>=19 AND age<=59 AND children>=3

Modifier:
Au lieu des préfixes min- et max-, vous pouvez autoriser >, < (et peut-être !) comme dernier caractère du nom du paramètre. Ainsi, au lieu de min-age, vous avez un paramètre appelé age>, qui, combiné à une valeur dans la chaîne de requête, finit par ressembler à age>=19 :-)
Évidemment, vous ne pouvez utiliser cette astuce que lorsque la comparaison comporte un signe égal.

7
Nicholas Shanks

Supposons que vous récupériez les journaux de votre serveur et supposons que vos données ressemblent à ceci:

{
    "protocol": "http",
    "Host": "example.domain.com",
    "path": "/apis/classified/server/logs",
    "method": "GET",
    "ip": "::ffff:127.0.0.1",
    "time": 1483066346338,
    "usingTime": 12,
    "status": 200,
    "headers": {
        "user-agent": "Mozilla/5.0 Chrome"
    }
}

Et vous voulez interroger comme ceci, où

  • protocol est égal à 'http'.
  • Host est égal à 'example.domain.com', ou non à 'example.domain.me'.
  • path est égal à '/apis/classified/server/logs', ou aime /*classified\/server*/.
  • method est égal à 'DELETE' ou n'est pas égal à 'GET' ou dans ['POST', 'PUT', 'DELETE'].
  • ip est égal à '127.0.0.1', ou non à '127.0.0.1'.
  • usingTime est supérieur à 3500 ou supérieur ou égal à 1500 et inférieur ou égal à 3500.
  • status est égal à 404, ou non à 200, ou est supérieur ou égal à 400 et est inférieur à 500.
  • headers.user-agent aime /*chrome*/i.

Ici les routes vont comme ceci:

  • /apis/classified/server/logs?path=/apis/classfied
  • /apis/classified/server/logs?path.regex=*classfied*
  • /apis/classified/server/logs?method.ne=GET
  • /apis/classified/server/logs?method=POST&method=PATCH&method=PUT
  • /apis/classified/server/logs?usingTime.gte=1500&usingTime.lte=2500
  • /apis/classified/server/logs?headers.user-agent.regex=*chrome*

Si vous utilisez express.js en tant que serveur, votre requête req.query sera structurée comme suit:

  • {"path": "/apis/classfied"}
  • {"path": {"regex": "*classfied*"}}
  • {"method": "DELETE"}
  • {"method": ["GET","POST","PUT"]}
  • {"usingTime": {"gte": "1500","lte": "2500"}} (lte: est inférieur ou égal à)
  • {"status": {"ne": "200"}}} (ne: n'est pas égal à)
  • {"path": {"regex": "*classfied*"}}
  • {"headers": {"user-agent": {"regex": "*chrome*", "flag": "i"}}}

Et vous allez utiliser beaucoup de if-else pour inventer votre méthode de requête de mangouste, ou une chaîne SQL peut-être.

2
Fisher

Je le mettrais en œuvre comme une plage, et si l’un des côtés est ouvert, ne le remplissez pas.

GET /customers/16-
GET /customers/-716

Lorsque vous demandez à tous les clients, n’ajoutez pas tout, laissez le champ vide.

GET /customers

Ou, lorsque vous avez besoin de signes dans votre numéro/code, utilisez ceci:

GET /customers/16/
GET /customers//716
GET /customers/16/716

Vous pouvez échapper aux barres obliques s'ils font partie du nombre.

1
RobAu

AVERTISSEMENT: lisez la clause de non-responsabilité ci-dessous

Je ne sais pas si cette requête est liée à c #. Mais si vous utilisez linq pour vos requêtes côté serveur, j'ai une autre suggestion:

GET /customers?filter=CustomerNo>=16 AND CustomerNo<=716

Ensuite, côté serveur:

using System.Linq.Dynamic;

var filteredCustomers = myCustomerTable.Where(filter);

Je pense que cela devrait être la solution la plus flexible.


AVERTISSEMENT ajouté le 19 janvier 2017 On vient de me signaler qu'il existe un exploit pour l'injection de code dans Dynamic Linq. L'exemple ci-dessus ouvre la possibilité que le client puisse démarrer l'exécution du code sur le serveur.

0
user3414239

Certains standards basés sur REST offrent des solutions adéquates à ce problème. Par exemple, https://www.hl7.org/fhir/stu3/search.html

Votre problème peut être résolu comme ça: GET /customers?id=ge500&id=lt1000

En outre, il existe OData , qui est bien plus interopérable que toute norme de niveau industriel. Et il propose ce style: GET /customers?$filter=id ge 500 and id lt 1000

0
astef