Quando uso parâmetros de caminho x parâmetros de consulta em uma API RESTful?

141

Quero tornar minha API RESTful muito previsível. Qual é a melhor prática para decidir quando fazer uma segmentação de dados usando o URI em vez de usar parâmetros de consulta.

Faz sentido para mim que os parâmetros do sistema que suportam paginação, classificação e agrupamento estejam após o '?' Mas e os campos como 'status' e 'region' ou outros atributos que segmentam sua coleção? Se esses também devem ser parâmetros de consulta, qual é a regra geral em saber quando usar parâmetros de caminho?

cosbor11
fonte
1
uma pergunta semelhante é respondida aqui ... stackoverflow.com/questions/3198492/…
Lalit Mehra

Respostas:

239

A melhor prática para o design da API RESTful é que os parâmetros de caminho são usados ​​para identificar um recurso ou recursos específicos, enquanto os parâmetros de consulta são usados ​​para classificar / filtrar esses recursos.

Aqui está um exemplo. Suponha que você esteja implementando pontos de extremidade da API RESTful para uma entidade chamada Car. Você estruturaria seus terminais assim:

GET /cars
GET /cars/:id
POST /cars
PUT/cars/:id
EXCLUIR/cars/:id

Dessa forma, você só está usando parâmetros de caminho ao especificar qual recurso buscar, mas isso não classifica / filtra os recursos de nenhuma maneira.

Agora, suponha que você queira adicionar a capacidade de filtrar os carros por cor em suas solicitações GET. Como a cor não é um recurso (é propriedade de um recurso), você pode adicionar um parâmetro de consulta que faça isso. Você adicionaria esse parâmetro de consulta à sua solicitação GET/cars assim:

OBTER /cars?color=blue

Esse terminal seria implementado para que apenas carros azuis fossem devolvidos.

No que diz respeito à sintaxe, seus nomes de URL devem estar em letras minúsculas. Se você tiver um nome de entidade que geralmente possui duas palavras em inglês, use um hífen para separar as palavras, não para maiúsculas e minúsculas.

Ex. /two-words

Mike
fonte
3
Obrigado pela sua resposta, Mike. Esta é uma metodologia clara e simples; vale um voto positivo de mim. Ainda assim, muitas vezes, os desenvolvedores optam pela abordagem 'cars / blue', estou me perguntando qual é o raciocínio deles ... talvez eles decidam fazer parâmetros de caminho para campos obrigatórios ou talvez o façam para indicar que o banco de dados é particionado por esse fragmento.
precisa saber é o seguinte
1
Não sei ao certo qual é o raciocínio deles. Honestamente, eu discordo disso. Acho que seguir as convenções e mantê-las simples faz mais sentido. Ao fazer isso, você permite que os consumidores de sua API entendam melhor exatamente o que precisam fazer para acessar sua funcionalidade.
12769 Mike
3
que tal / cars? id = 1 & color = blue em vez de cars / 1 /? color = blue. você está basicamente filtragem recurso carros em cada cenário
MKO
1
Errado, já que o carro com a identificação 1 existe apenas um, mas os carros com a cor azul talvez muitos. Não é a distinção entre identidade e filtro
paul
1
Minha hipótese a respeito de porque usando parâmetros de caminho é tão difundido é porque muitos desenvolvedores aprenderam com os quadros personalizados por pessoas que não têm uma boa compreensão dos princípios do REST (Ruby on Rails em particular.)
Chris Broski
58

A maneira fundamental de pensar sobre esse assunto é a seguinte:

Um URI é um identificador de recurso que identifica exclusivamente uma instância específica de um recurso TYPE. Como tudo na vida, todo objeto (que é algum tipo de instância), possui um conjunto de atributos que são invariantes ou temporais no tempo.

No exemplo acima, um carro é um objeto muito tangível que possui atributos como marca, modelo e VIN - que nunca mudam, e cor, suspensão etc. que podem mudar ao longo do tempo. Portanto, se codificarmos o URI com atributos que podem mudar ao longo do tempo (temporal), podemos acabar com vários URIs para o mesmo objeto:

GET /cars/honda/civic/coupe/{vin}/{color=red}

E anos depois, se a cor desse mesmo carro for alterada para preto:

GET /cars/honda/civic/coupe/{vin}/{color=black}

Observe que a instância do carro em si (o objeto) não mudou - é apenas a cor que mudou. Ter vários URIs apontando para a mesma instância de objeto forçará você a criar vários manipuladores de URI - esse não é um design eficiente e, é claro, não é intuitivo.

Portanto, o URI deve consistir apenas em partes que nunca serão alteradas e continuarão a identificar exclusivamente esse recurso durante toda a sua vida útil. Tudo o que pode mudar deve ser reservado para parâmetros de consulta, como:

GET /cars/honda/civic/coupe/{vin}?color={black}

Conclusão - pense no polimorfismo.

Kingz
fonte
2
Paradigma interessante. Esse é um padrão de design comumente usado? Você pode fornecer algumas APIs que usam isso em sua documentação ou algumas referências que descrevem essa estratégia?
cosbor11
1
Eu gosto de como você enfatizou "TYPE" quando escreveu "Um URI é um identificador de recurso que identifica exclusivamente uma instância específica de um recurso TYPE". Eu acho que é uma distinção importante.
jrahhali
15

Em uma API REST, você não deve se preocupar muito com URIs previsíveis. A própria sugestão de previsibilidade de URI alude a um mal-entendido da arquitetura RESTful. Ele pressupõe que um cliente deva construir URIs, o que realmente não deveria.

No entanto, suponho que você não esteja criando uma API REST verdadeira, mas uma API 'inspirada em REST' (como a do Google Drive). Nesses casos, a regra geral é 'caminho params = identificação de recurso' e 'consulta params = classificação de recurso'. Então, a pergunta se torna: você pode identificar exclusivamente seu recurso SEM status / região? Se sim, então talvez seja um parâmetro de consulta. Se não, então é um caminho param.

HTH.

Oliver McPhee
fonte
11
Eu discordo, uma boa API deve ser previsível; RESTful ou não.
cosbor11
3
Acho que sim. Deve haver rima e razão para a formação do URI, em vez de nomear arbitrariamente pontos de extremidade. Quando alguém pode escrever intuitivamente um cliente de API sem fazer referência constante à documentação, você escreveu uma boa API na minha opinião.
cosbor11
2
"Quando alguém pode escrever intuitivamente um cliente de API sem consultar constantemente a documentação". É aí que eu acho que nossa compreensão do REST difere ... o cliente da API nunca precisa "criar" uma URL. Eles devem selecioná-lo na resposta da chamada anterior da API. Se você usar um site como uma analogia ... Você acessa facebook.com e seleciona um link para a página de eventos. Você não se importa se o URL de eventos do Facebook é 'previsível', pois não é possível digitá-lo. Você chega lá através de links de hipermídia. O mesmo vale para uma API REST. Então, faça URIs significativo para você (o servidor), mas não th cliente
Oliver McPhee
2
Nota adicionada. Isso não significa que os URIs não devem seguir um padrão fácil de entender, apenas significa que não é uma restrição de uma API RESTful. O maior problema dessa área são as pessoas que assumem que um cliente deve criar os URLs. Eles não deveriam, pois isso cria um acoplamento entre cliente e servidor que não deveria existir. (por exemplo - o servidor não pode alterar uma URL sem interromper todos os aplicativos clientes). Em uma API REST, o servidor pode alterá-los como quiser.
Oliver McPhee
3
+1 para usar as seguintes palavras: "'parâmetros de caminho = identificação de recurso' e 'parâmetros de consulta = classificação de recursos'". Isso realmente esclareceu tudo para mim.
Doug
3

Uma vez eu projetei uma API que recurso principal era people. Normalmente, os usuários solicitavam filtragem people, para impedir que os usuários ligassem para algo como /people?settlement=urbansempre, implementei o /people/urbanque mais tarde me permitiu adicionar facilmente /people/rural. Isso também permite acessar a /peoplelista completa, se for de alguma utilidade posteriormente. Em resumo, meu raciocínio era adicionar um caminho para subconjuntos comuns

A partir daqui :

Aliases para consultas comuns

Para tornar a experiência da API mais agradável para o consumidor médio, considere empacotar conjuntos de condições em caminhos RESTful facilmente acessíveis. Por exemplo, a consulta de tickets fechados recentemente acima pode ser empacotada comoGET /tickets/recently_closed

Mario Gil
fonte
1

De um modo geral, costumo usar parâmetros de caminho quando há uma 'hierarquia' óbvia no recurso, como:

/region/state/42

Se esse recurso único tiver um status, pode-se:

/region/state/42/status

No entanto, se 'região' não for realmente parte do recurso exposto, provavelmente pertencerá a um dos parâmetros de consulta - semelhante à paginação (como você mencionou).

morsor
fonte
0

A segmentação é mais hierárquica e "bonita", mas pode ser limitativa.

Por exemplo, se você tiver um URL com três segmentos, cada um passando parâmetros diferentes para procurar um carro por marca, modelo e cor:

www.example.com/search/honda/civic/blue

Este é um URL muito bonito e mais facilmente lembrado pelo usuário final, mas agora você fica preso nessa estrutura. Digamos que você queira fazê-lo para que, na pesquisa, o usuário possa procurar TODOS os carros azuis ou TODOS os Honda Civics? Um parâmetro de consulta resolve isso porque fornece um par de valores-chave. Então você pode passar:

www.example.com/search?color=blue
www.example.com/search?make=civic

Agora você tem uma maneira de referenciar o valor por meio de sua chave - "color" ou "make" no seu código de consulta.

Você pode contornar isso possivelmente usando mais segmentos para criar um tipo de estrutura de valor-chave como:

www.example.com/search/make/honda/model/civic/color/blue

Espero que faça sentido ..

webmaster_sean
fonte
-2

URL de exemplo: /rest/{keyword}

Este URL é um exemplo para parâmetros de caminho. Podemos obter esses dados de URL usando @PathParam.

URL de exemplo: /rest?keyword=java&limit=10

Este URL é um exemplo para parâmetros de consulta. Podemos obter esses dados de URL usando @Queryparam.

Sujithrao
fonte