Projete a API de consulta RESTful com uma longa lista de parâmetros de consulta [fechado]

153

Eu preciso criar uma API de consulta RESTful, que retorna um conjunto de objetos com base em alguns filtros. O método HTTP usual para isso é GET. O único problema é que ele pode ter pelo menos uma dúzia de filtros e, se passarmos todos eles como parâmetros de consulta, a URL poderá ficar bastante longa (tempo suficiente para ser bloqueado por algum firewall).

Reduzir o número de parâmetros não é uma opção.

Uma alternativa em que eu poderia pensar é usar o método POST no URI e enviar os filtros como parte do corpo do POST. Isso é contra ser RESTfull (fazer uma chamada POST para consultar dados).

Alguém tem alguma sugestão de design melhor?

missionE46
fonte
2
Usar nomes curtos (1 caractere, etc.)?
precisa saber é o seguinte
2
Pode não ser verdadeiramente RESTful, mas acho que você precisa ser prático quando se trata de GETs e POSTs. Se você tiver tantas variáveis ​​para enviar e não puder reduzi-las, eu as POSTAREI. Não gosto de exagerar na URL, mas sou apenas eu.
Doug Dawson
Obrigado. Embora essa pergunta esteja encerrada, é EXATAMENTE a pergunta para a qual eu precisava de uma resposta. Estou feliz que você perguntou.
Casey Crookston

Respostas:

142

Lembre-se de que, com uma API REST, é tudo uma questão de seu ponto de vista.

Os dois conceitos principais em uma API REST são os terminais e os recursos (entidades). Em termos gerais, um ponto de extremidade retorna recursos via GET ou aceita recursos via POST e PUT e assim por diante (ou uma combinação dos itens acima).

É aceito que, com o POST, os dados que você envia podem ou não resultar na criação de um novo recurso e nos pontos de extremidade associados, que provavelmente não "viverão" sob o URL do POST. Em outras palavras, quando você POST envia os dados para algum lugar para manipulação. O terminal POST não é onde o recurso normalmente pode ser encontrado.

Citação da RFC 2616 (com partes irrelevantes omitidas e partes relevantes destacadas):

9.5 POST

O método POST é usado para solicitar que o servidor de origem aceite a entidade incluída na solicitação como um novo subordinado do recurso identificado pelo Request-URI na linha de solicitação. O POST foi projetado para permitir um método uniforme para cobrir as seguintes funções:

  • ...
  • Fornecer um bloco de dados, como o resultado do envio de um formulário, para um processo de manipulação de dados;
  • ...

...

A ação executada pelo método POST pode não resultar em um recurso que pode ser identificado por um URI . Nesse caso, 200 (OK) ou 204 (Sem conteúdo) é o status de resposta apropriado, dependendo de a resposta incluir ou não uma entidade que descreve o resultado .

Se um recurso foi criado no servidor de origem, a resposta DEVE ser 201 (Criado) ...

Nós nos acostumamos a pontos finais e recursos que representam 'coisas' ou 'dados', seja um usuário, uma mensagem, um livro - qualquer que seja o domínio do problema. No entanto, um terminal também pode expor um recurso diferente - por exemplo, resultados da pesquisa.

Considere o seguinte exemplo:

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

Este é um REST CRUD típico. No entanto, o que se adicionamos:

POST /books/search

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

Não há nada de RESTful nesse terminal. Ele aceita dados (entidade) na forma do corpo da solicitação. Esses dados são os Critérios de pesquisa - um DTO como qualquer outro. Este terminal produz um recurso (entidade) em resposta à solicitação: Resultados da Pesquisa . O recurso de resultados da pesquisa é temporário, servido imediatamente ao cliente, sem redirecionamento e sem ser exposto a outros URLs canônicos.

Ainda é REST, exceto que as entidades não são livros - a entidade de solicitação é um critério de pesquisa de livros e a entidade de resposta é um resultado de pesquisa de livros.

Amir Abiri
fonte
Você poderia sugerir algumas classes de convenções de nomenclatura para o DTO?
Kwadz
Pessoalmente eu iria com BooksSearchCriteriaDTOe BooksSearchResultsDTO.
Amir Abiri
qual seria o melhor código de resposta HTTP para este caso de POST / books / search? 201 ainda se aplica?
L. Holanda
9
201 é o oposto - implica que um recurso foi criado. Um recurso que se espera tenha seu próprio URI em algum lugar. 201 é adequado quando POSTé usado para a parte C do CRUD. Eu iria com o simples velho 200, opcionalmente talvez com o 204 para resultados de pesquisa vazios.
Amir Abiri
@AmirAbiri muito obrigado.
mohamed-mhiri
84

Muitas pessoas aceitaram a prática de que um GET com uma string de consulta muito longa ou muito complexa (por exemplo, as strings de consulta não manipulam dados aninhados facilmente) pode ser enviado como um POST, com os dados complexos / longos representados no corpo do pedido.

Procure a especificação para POST na especificação HTTP. É incrivelmente amplo. (Se você quiser navegar em um navio de guerra por uma brecha no REST ... use POST.)

Você perde alguns dos benefícios da semântica GET ... como tentativas automáticas porque GET é idempotente, mas se você pode conviver com isso, pode ser mais fácil aceitar o processamento de consultas realmente longas ou complicadas com o POST.

(lol digressão longa ... Descobri recentemente que, pelas especificações HTTP, o GET pode conter um corpo do documento. Há uma seção que diz, parafraseando: "Qualquer solicitação pode ter um corpo do documento, exceto os listados nesta seção" ... e a seção a que se refere não lista nenhuma. Pesquisei e encontrei um segmento em que os autores do HTTP estavam falando sobre isso, e era intencional, para que roteadores e outros não precisassem diferenciar entre mensagens diferentes. praticar muitas peças de infraestrutura soltam o corpo de um GET. Assim, você pode obter com filtros representados no corpo, como POST, mas você estaria lançando os dados.)

Roubar
fonte
11
Consulte também esta pergunta para obter mais discussões sobre HTTP GET com o corpo.
RickyA
6

Em poucas palavras: faça um POST, mas substitua o método HTTP usando o cabeçalho X-HTTP-Method-Override .

Pedido real

POST / livros

Entidade

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

Cabeçalhos

Substituição do método X-HTTP: GET

No lado do servidor, verifique se o cabeçalho X-HTTP-Method-Override existe e use seu valor como método para criar a rota para o terminal final no back-end. Além disso, considere o corpo da entidade como a string de consulta. Do ponto de vista de back-end, a solicitação se tornou apenas um GET simples.

Dessa forma, você mantém o design em harmonia com os princípios REST.

Edit: Eu sei que esta solução foi originalmente planejada para resolver o problema do verbo PATCH em alguns navegadores e servidores, mas também funciona para mim com o verbo GET no caso de uma URL muito longa, que é o problema descrito na pergunta.

Delmo
fonte
2
O IETF descontinuou os cabeçalhos de HTTP com prefixo X: tools.ietf.org/html/rfc6648
jannis
@jannis O RFC que você vincula permanece 1.4. não faz nenhuma recomendação sobre X-remoção existente e 1.5. ele não substitui as especificações existentes. ... A X-IMO permanecerá aqui.
Jan Molnar
-3

Se você estiver desenvolvendo em Java e JAX-RS, recomendo usar o @QueryParam com o @GET

Eu tive a mesma pergunta quando precisei fazer uma lista.

Consultar exemplo:

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();
    }
}

Padrão de URI: “poc / test? Code = 1 & code = 2 & code = 3

O @QueryParam converterá o parâmetro de consulta "orderBy = age & orderBy = name" em java.util.List automaticamente.

acacio.martins
fonte
Seria melhor se você explicasse o seu exemplo. Em que linguagem de programação está escrita?
Aleks Andreev
Olá @AleksAndreev. Obrigado por sua opinião. Ficou melhor? tks
acacio.martins 19/03/2019
Esta pergunta é sobre o design do serviço RESTful, não sobre a implementação. Esta resposta não responde à pergunta.
Heretic Monkey
@ user1331413 IMHO sim, agora é melhor. Obrigado por seu esforço .. No entanto, como disse Mike McCaughan, pergunta é sobre o conceito de REST, ao invés de implementação
Aleks Andreev