Práticas recomendadas da API REST: onde colocar parâmetros? [fechadas]

348

Uma API REST pode ter parâmetros de pelo menos duas maneiras:

  1. Como parte do caminho da URL (ou seja /api/resource/parametervalue )
  2. Como argumento de consulta (ou seja /api/resource?parameter=value )

Qual é a melhor prática aqui? Existem orientações gerais sobre quando usar 1 e quando usar 2?

Exemplo do mundo real: o Twitter usa parâmetros de consulta para especificar intervalos. ( http://api.twitter.com/1/statuses/home_timeline.json?since_id=12345&max_id=54321)

Seria melhor design colocar esses parâmetros no caminho da URL?

Kalle Gustafsson
fonte

Respostas:

254

Se existem práticas recomendadas documentadas, ainda não as encontrei. No entanto, aqui estão algumas diretrizes que eu uso ao determinar onde colocar parâmetros em um URL:

Parâmetros opcionais tendem a ser mais fáceis de inserir na string de consulta.

Se você deseja retornar um erro 404 quando o valor do parâmetro não corresponde a um recurso existente, eu tenderia para um parâmetro de segmento de caminho. por exemplo, /customer/232onde 232 não é um ID de cliente válido.

Se, no entanto, você quiser retornar uma lista vazia, quando o parâmetro não for encontrado, sugiro o uso de parâmetros de string de consulta. por exemplo/contacts?name=dave

Se um parâmetro afetar uma subárvore inteira do seu espaço de URI, use um segmento de caminho. por exemplo, um parâmetro de linguagem /en/document/foo.txt versus/document/foo.txt?language=en

Prefiro identificadores únicos a estar em um segmento de caminho em vez de um parâmetro de consulta.

As regras oficiais para URIs são encontradas nesta especificação RFC aqui . Também há outra especificação RFC muito útil aqui que define regras para parametrizar URIs.

Darrel Miller
fonte
5
A regra oficial dos URIs e o rascunho do sepc foram realmente úteis e interessantes! :-)
KajMagnus
11
O teste de erro 404 me ajuda muito a evitar colocar informações no caminho que pertence aos parâmetros de consulta, cabeçalhos ou corpo da solicitação. Obrigado por apontar isso!
Kevin Condon
152

Resposta tardia, mas adicionarei algumas informações adicionais ao que foi compartilhado, a saber, que existem vários tipos de "parâmetros" para uma solicitação, e você deve levar isso em consideração.

  1. Localizadores - por exemplo, identificadores de recursos, como IDs ou ação / exibição
  2. Filtros - por exemplo, parâmetros que fornecem uma pesquisa, classificação ou restrição do conjunto de resultados.
  3. Estado - por exemplo, identificação da sessão, chaves api, whatevs.
  4. Conteúdo - por exemplo, dados a serem armazenados.

Agora, vamos olhar para os diferentes lugares onde esses parâmetros podem ir.

  1. Solicitar cabeçalhos e cookies
  2. Cadeia de caracteres de consulta do URL (vars "GET")
  3. Caminhos de URL
  4. String de consulta do corpo / multipart (vars "POST")

Geralmente, você deseja que o Estado seja definido em cabeçalhos ou cookies, dependendo do tipo de informação do estado. Acho que todos podemos concordar com isso. Use cabeçalhos http personalizados (X-My-Header), se necessário.

Da mesma forma, o Conteúdo possui apenas um lugar para pertencer, que está no corpo da solicitação, como cadeias de consulta ou como conteúdo http multipart e / ou JSON. Isso é consistente com o que você recebe do servidor quando ele envia conteúdo. Portanto, você não deve ser rude e fazê-lo de maneira diferente.

Localizadores como "id = 5" ou "action = refresh" ou "page = 2" faria sentido ter um caminho de URL, como mysite.com/article/5/page=2onde você sabe em parte o que cada parte deve significar (o básico, como artigo e 5 obviamente significa obter os dados do tipo article com o id 5) e parâmetros adicionais são especificados como parte do URI. Eles podem estar na forma de page=2, ou page/2se você souber que após um certo ponto no URI, as "pastas" são valores-chave emparelhados.

Os filtros sempre entram na cadeia de consulta, porque, embora façam parte da localização dos dados corretos, eles estão lá apenas para retornar um subconjunto ou modificação do que os Localizadores retornam sozinhos. A pesquisa em mysite.com/article/?query=Obama(subconjunto) é um filtro e também/article/5?order=backwards (modificação). Pense no que faz, não apenas no que é chamado!

Se "view" determina o formato de saída, é um filtro ( mysite.com/article/5?view=pdf) porque retorna uma modificação do recurso encontrado, em vez de localizar o recurso desejado. Se, em vez disso, decidir qual parte específica do artigo veremos (mysite.com/article/5/view=summary ), será um localizador.

Lembre-se, restringir um conjunto de recursos é filtrar. Localizar algo específico dentro de um recurso é localizar ... duh. A filtragem de subconjunto pode retornar qualquer número de resultados (até 0). A localização sempre encontrará essa instância específica de algo (se existir). A filtragem de modificação retornará os mesmos dados que o localizador, exceto modificados (se tal modificação for permitida).

Espero que isso ajude a dar às pessoas alguns momentos eureka, caso estejam perdidos sobre onde colocar as coisas!

Tor Valamo
fonte
2
Por que não é idum filtro então? Ele retorna um subconjunto do recurso
Jonathan.
13
@Jonathan. não, ele retorna um recurso específico, ou seja, o artigo número 5. Um filtro é sempre uma maneira de restringir uma pesquisa em uma coleção de recursos. Se você deseja apenas esse recurso específico, deve haver uma maneira designada de obtê-lo. Filtragem significa que você tem a possibilidade de retornar vários recursos. Um ID não é um filtro, é um recurso único definido. Se você tivesse uma GAMA de IDs, seria um filtro, mesmo que o intervalo incluísse apenas um ID. Se o filtro também incluísse tipos de recursos, ele retornaria todos os recursos com o ID 5, não apenas o artigo.
Tor Valamo
11
@ Jonathan .: como o DarrelMiller mencionado, você esperaria que uma solicitação no objeto / id retornasse 404 em caso de identificação desconhecida, enquanto você esperaria que o objeto? Id = id retornasse e esvaziasse a lista. Além disso, eu consideraria que qualquer tipo de filtragem / subconjunto deve retornar uma lista.
Njzk2
11
O Pages é difícil, porque, como você diz, pode ser um filtro de um recurso (coleção de páginas), mas, ao mesmo tempo, é um recurso específico nessa coleção. Eu sempre solicitaria uma página de artigo por localizador, não por filtro. No entanto, a página pode ser um filtro de uma lista de algo, digamos uma lista de usuários. Mas a página é inerentemente um delimitador, também conhecido como "iniciar no item (page-1)*perpagee mostrarperpage itens". Usá-lo como filtro está correto, mas por razões diferentes. Chamar isso de "página" está tecnicamente errado. Mais semanticamente correto seria chamá-lo de "from" ou "startAt"
Tor Valamo
11
(continuação) O significado semântico de "página" é que é um recurso específico que não muda. Vem da impressão física. Se nunca tivéssemos livros ou material impresso, "página" não seria realmente uma palavra. Se você possui uma lista dinâmica de itens, divididos em "páginas", deve fornecer um ponto de partida específico, numérico, alfabético ou até específico de item, além de um filtro "quantos por página". Se eu quiser fazer referência a algo na sua lista, quero detalhes. Também não quero ir para a página 5 apenas para perceber que agora você mudou o interno perpagepara 50 em vez de 20.
Tor Valamo
21

Depende de um design. Não há regras para URIs no REST sobre HTTP (o principal é que elas sejam únicas). Muitas vezes, trata-se de questão de gosto e intuição ...

Eu adotei a seguinte abordagem:

  • elemento do caminho da URL: o recurso e seu elemento do caminho formam um diretório e um sub-recurso (por exemplo, / items / {id}, / users / items). Quando não tiver certeza, pergunte a seus colegas, se eles acham que a travessia e eles pensam em "outro diretório", provavelmente o elemento do caminho é a escolha certa
  • parâmetro url: quando realmente não há passagem (recursos de pesquisa com vários parâmetros de consulta são um exemplo muito bom disso)
manuel aldana
fonte
11
Na verdade, existem regras bastante claras sobre a aparência de um URI e muito pouca ambiguidade sobre como aplicá-las aos URIs RESTful.
DanMan
18

Na IMO, os parâmetros devem ser melhores como argumentos de consulta. O URL é usado para identificar o recurso, enquanto os parâmetros de consulta adicionados para especificar qual parte do recurso você deseja, qualquer estado que o recurso deve ter etc.

PeterWong
fonte
7
Na verdade, o caminho e a consulta são usados ​​em combinação para identificar o recurso. Isso foi esclarecido na RFC 3986 http://labs.apache.org/webarch/uri/rfc/rfc3986.html#query
Darrel Miller
@DarrelMiller Eu sei que esta é uma postagem antiga, mas estou interessado em saber mais sobre os parâmetros de consulta de fato também são usados ​​para identificar o recurso. O link que você forneceu agora está morto. Eu olhei para o RFC3986, mas não vejo como você deduziu esse fato. Além disso, por definição, os parâmetros de um identificador não devem ser opcionais, portanto, não parece apropriado usar parâmetros de consulta para identificação.
Mickael Marrache
@MickaelMarrache Veja a primeira linha na seção 3.4 tools.ietf.org/html/rfc3986#section-3.4
Darrel Miller
2
@DarrelMiller Thanks! Minha pergunta vem do fato de que, geralmente, os componentes HTTP intermediários não armazenam em cache as respostas de solicitações que contêm uma string de consulta. Portanto, parece que os parâmetros de consulta são mais apropriados para pesquisar recursos de acordo com alguns critérios e não para identificar exclusivamente um recurso.
Mickael Marrache
17

De acordo com a implementação REST,

1) As variáveis ​​de caminho são usadas para a ação direta nos recursos, como um contato ou uma música ex.
GET etc / api / resource / {songid} ou
GET etc / api / resource / {contactid} retornará os respectivos dados.

2) Permissões / argumentos de consulta são usados ​​para recursos diretos, como metadados de uma música ex .., GET / api / resource / {songid}? Metadata = genres, ele retornará os dados dos gêneros para essa música em particular.

Satish Bellapu
fonte
5
Na verdade, não existe um padrão REST . Por Wikipedia : Diferentemente dos serviços Web baseados em SOAP, não existe um padrão "oficial" para APIs da Web RESTful. [14] Isso ocorre porque o REST é um estilo de arquitetura, diferente do SOAP, que é um protocolo. Apesar de resto não é um padrão, uma aplicação RESTful como a Web pode usar padrões como HTTP, URI, XML, etc.
DavidRR
Eu não gosto da abordagem 2. Eu preferiria preffer / api / gêneros MotoID = 123 ou / api / canções / {MotoID} / gêneros?
Bart Calixto
11
@ Bart, Satish estava se referindo a Variáveis ​​no caminho, que é essencialmente o que você mencionou como sua preferência .. no entanto, se os gêneros são realmente metadados, e não um campo da entidade / recurso da música, então eu pude ver mais sensibilidade usando uma string de consulta nele ..
Brett Caswell
@BrettCaswell entendeu! Obrigado por apontar isso. realmente aprecio isso!
Bart Calixto
16

"Empacote" e POSTE seus dados no "contexto" fornecido pelo localizador de recursos do universo, o que significa o número 1 em prol do localizador.

Observe as limitações com o item 2. Prefiro POSTs a # 1.

nota: limitações são discutidas para

Postar em Existe um tamanho máximo para o conteúdo do parâmetro POST?

Entrar Existe um limite para a duração de uma solicitação GET? e tamanho máximo dos parâmetros de URL em _GET

ps esses limites são baseados nos recursos do cliente (navegador) e servidor (configuração).

dgm
fonte
add-on: as rotas espirituosas podem ter versões (distintas por meio de cabeçalhos), portanto, fornecem funcionalidade evoluída, sem necessidade de alterar o código que consome o código restante (api) que você escreve como em restify -> procurar rotas versionadas
dgm
5

De acordo com o padrão URI o caminho é para parâmetros hierárquicos e a consulta é para parâmetros não hierárquicos. Claro. pode ser muito subjetivo o que é hierárquico para você.

Nas situações em que vários URIs são atribuídos ao mesmo recurso, gosto de colocar os parâmetros - necessários para a identificação - no caminho e os parâmetros - necessários para construir a representação - na consulta. (Para mim, dessa maneira, é mais fácil rotear.)

Por exemplo:

  • /users/123 e /users/123?fields="name, age"
  • /users e /users?name="John"&age=30

Para reduzir o mapa, eu gosto de usar as seguintes abordagens:

  • /users?name="John"&age=30
  • /users/name:John/age:30

Portanto, depende de você (e do seu roteador do lado do servidor) como você constrói seus URIs.

Nota: Apenas para mencionar esses parâmetros são parâmetros de consulta. Então, o que você está realmente fazendo é definir uma linguagem de consulta simples. Por consultas complexas (que contêm operadores como e, ou, maior que etc.), sugiro que você use uma linguagem de consulta já existente. Os recursos dos modelos de URI são muito limitados ...

inf3rno
fonte
4

Como programador frequentemente no cliente, prefiro o argumento de consulta. Além disso, para mim, ele separa o caminho da URL dos parâmetros, aumenta a clareza e oferece mais extensibilidade. Também me permite ter uma lógica separada entre a criação de URL / URI e o construtor de parâmetros.

Gosto do que Manuel Aldana disse sobre a outra opção, se houver algum tipo de árvore envolvida. Eu posso ver peças específicas do usuário sendo cortadas dessa maneira.

Joe Plante
fonte
4

Não há regras rígidas e rápidas, mas a regra prática, do ponto de vista puramente conceitual que eu gosto de usar, pode ser resumida da seguinte forma: um caminho URI (por definição) representa um recurso e os parâmetros de consulta são essencialmente modificadores nesse recurso . Até o momento que provavelmente não ajuda ... Com uma API REST você tem os principais métodos de agir sobre um único recurso usando GET, PUTe DELETE. Portanto, se algo deve ser representado no caminho ou como um parâmetro, pode ser reduzido se esses métodos fazem sentido para a representação em questão. Você faria PUTalgo razoavelmente nesse caminho e seria semanticamente correto fazê-lo? Você poderia, é claro, PUTalgo em qualquer lugar e dobrar o back-end para lidar com isso, mas você devePUTo que equivale a uma representação do recurso real e não a uma versão dele desnecessariamente contextualizada. Para coleções, o mesmo pode ser feito POST. Se você deseja adicionar a uma coleção específica, qual seria um URL que faz sentido POST?

Isso ainda deixa algumas áreas cinzentas, pois alguns caminhos podem apontar para a quantidade de filhos dos recursos dos pais, o que é um tanto discricionário e depende do uso deles. A única linha difícil que isso desenha é que qualquer tipo de representação transitiva deve ser feita usando um parâmetro de consulta, pois não teria um recurso subjacente.

Em resposta ao exemplo do mundo real fornecido na pergunta original (API do Twitter), os parâmetros representam uma consulta transitiva que filtra o estado dos recursos (em vez de uma hierarquia). Nesse exemplo em particular, seria totalmente irracional adicionar à coleção representada por essas restrições e, além disso, essa consulta não seria capaz de ser representada como um caminho que faria algum sentido nos termos de um gráfico de objetos.

A adoção desse tipo de perspectiva orientada a recursos pode facilmente mapear diretamente para o gráfico de objeto do seu modelo de domínio e direcionar a lógica da sua API ao ponto em que tudo funciona de maneira muito limpa e de uma maneira bastante auto-documentável, uma vez que se encaixa na clareza. O conceito também pode ser esclarecido afastando-se dos sistemas que usam o roteamento de URL tradicional mapeado para um modelo de dados normalmente inadequado (ou seja, um RDBMS). O Apache Sling certamente seria um bom lugar para começar. O conceito de envio de objetos em um sistema como o Zope também fornece um analógico mais claro.

Matt Whipple
fonte
4

Aqui está a minha opinião.

Os parâmetros de consulta são usados ​​como metadados para uma solicitação. Eles atuam como filtro ou modificador para uma chamada de recurso existente.

Exemplo:

/calendar/2014-08-08/events

deve fornecer eventos do calendário para esse dia.

Se você deseja eventos para uma categoria específica

/calendar/2014-08-08/events?category=appointments

ou se você precisar de eventos com mais de 30 minutos

/calendar/2014-08-08/events?duration=30

Um teste decisivo seria verificar se a solicitação ainda pode ser atendida sem parâmetros de consulta.

Jay
fonte
2

Eu geralmente tendem a # 2, como um argumento de consulta (ou seja, / api / resource? Parameter = value).

Uma terceira opção é realmente postar o parâmetro = valor no corpo.

Isso ocorre porque funciona melhor para recursos com vários parâmetros e é mais extensível para uso futuro.

Não importa qual você escolher, certifique-se de escolher apenas um, não misture e combine. Isso leva a uma API confusa.

NorthIsUp
fonte
2

Uma "dimensão" deste tópico foi deixada de fora, mas é muito importante: há momentos em que as "melhores práticas" precisam entrar em acordo com a plataforma que estamos implementando ou aprimorando com os recursos REST.

Exemplo prático:

Atualmente, muitos aplicativos da web implementam a arquitetura MVC (Model, View, Controller). Eles assumem que um determinado caminho padrão é fornecido, ainda mais quando esses aplicativos da Web vêm com a opção "Ativar URLs de SEO".

Apenas para mencionar um aplicativo da Web bastante famoso: uma loja de comércio eletrônico OpenCart. Quando o administrador ativa os "URLs de SEO", espera que esses URLs cheguem em um formato MVC bastante padrão, como:

http://www.domain.tld/special-offers/list-all?limit=25

Onde

  • special-offers é o controlador MVC que deve processar o URL (mostrando a página de ofertas especiais)

  • list-allé o nome da ação ou função do controlador a ser chamado. (*)

  • limite = 25 é uma opção, informando que 25 itens serão mostrados por página.

(*) list-allé um nome de função fictício que usei para maior clareza. Na realidade, o OpenCart e a maioria das estruturas MVC têm uma função implícita (e geralmente omitida na URL) indexpadrão que é chamada quando o usuário deseja que uma ação padrão seja executada. Portanto, o URL do mundo real seria:

http://www.domain.tld/special-offers?limit=25

Com um aplicativo agora bastante padrão ou uma estrutura de estrutura semelhante à acima, você geralmente obtém um servidor da Web otimizado para isso, que reescreve os URLs para ele (o verdadeiro "URL sem SEO" seria:) http://www.domain.tld/index.php?route=special-offers/list-all&limit=25.

Portanto, como desenvolvedor, você deve lidar com a infraestrutura existente e adaptar suas "melhores práticas", a menos que seja o administrador do sistema, saiba exatamente como ajustar uma configuração de reescrita do Apache / NGinx (a última pode ser desagradável!) E assim em.

Portanto, sua API REST geralmente seria muito melhor seguindo os padrões do aplicativo da Web de referência, tanto para consistência com ele quanto para facilidade / velocidade (e, portanto, economia de orçamento).

Para voltar ao exemplo prático acima, uma API REST consistente seria algo com URLs como:

http://www.domain.tld/api/special-offers-list?from=15&limit=25

ou (URLs não SEO)

http://www.domain.tld/index.php?route=api/special-offers-list?from=15&limit=25

com uma mistura de argumentos "caminhos formados" e argumentos "formados por consulta".

Dario Fumagalli
fonte
1

Vejo muitas APIs REST que não lidam bem com parâmetros. Um exemplo que surge com frequência é quando o URI inclui informações de identificação pessoal.

http://software.danielwatrous.com/design-principles-for-rest-apis/

Penso que uma pergunta corolária é quando um parâmetro não deve ser um parâmetro, mas deve ser movido para o HEADER ou BODY da solicitação.

Daniel Watrous
fonte
0

É uma pergunta muito interessante.

Você pode usar os dois, não há nenhuma regra estrita sobre esse assunto, mas o uso de variáveis ​​de caminho URI tem algumas vantagens:

  • Cache : a maioria dos serviços de cache da Web na Internet não armazena em cache a solicitação GET quando eles contêm parâmetros de consulta. Eles fazem isso porque existem muitos sistemas RPC usando solicitações GET para alterar dados no servidor (falha !! Get deve ser um método seguro)

Mas se você usar variáveis ​​de caminho, todos esses serviços poderão armazenar em cache suas solicitações GET.

  • Hierarquia : As variáveis ​​de caminho podem representar a hierarquia: / Cidade / Rua / Local

Dá ao usuário mais informações sobre a estrutura dos dados.

Mas se seus dados não tiverem nenhuma relação hierárquica, você ainda poderá usar as variáveis ​​Path, usando vírgula ou ponto e vírgula:

/ Cidade / longitude, latitude

Como regra, use vírgula quando a ordem dos parâmetros for importante, use ponto-e-vírgula quando a ordem não for importante:

/ IconGenerator / vermelho; azul; verde

Além desses motivos, há alguns casos em que é muito comum usar variáveis ​​de string de consulta:

  • Quando você precisa que o navegador coloque automaticamente variáveis ​​de formulário HTML no URI
  • Quando você está lidando com algoritmo. Por exemplo, o mecanismo do Google usa cadeias de consulta:

http: // www.google.com/search?q=rest

Para resumir, não há nenhum motivo forte para usar um desses métodos, mas sempre que possível, use variáveis ​​de URI.

jfcorugedo
fonte