Qual é a maneira correta de executar um método de pesquisa RESTful complexo?

44

Seguindo os princípios REST, gostaria de criar um método GET para minha API que faça uma pesquisa usando alguns critérios e retorne os resultados ao cliente. O problema é que os critérios podem ter até 14 parâmetros, um deles é uma lista de objetos complexos, então ...

  • Eu nem sei se é possível codificar / decodificar esses objetos complexos para / de parâmetros de URL.

  • Não calculei quanto tempo o URL poderia ficar, mas tenho certeza de que será grande o suficiente e talvez atinja o limite de comprimento do URL.

Além disso, a pesquisa deve mostrar os resultados em "tempo real", ou seja, toda vez que o usuário altera algo do formulário de pesquisa, ele deve poder ver os novos resultados sem pressionar nenhum botão "pesquisar".

Você poderia me esclarecer esses pontos e qual seria o seu conselho para criar um método de pesquisa tranqüilo com muitos parâmetros?

anat0lius
fonte
3
Como um aparte (observo que nenhuma das duas respostas no momento da redação menciona a parte em tempo real), as pesquisas em tempo real normalmente devem estar apenas enviando a solicitação atualizada repetidamente. Por exemplo, enquanto você está digitando, você estaria enviando solicitações para coisas como search?q=t, search?q=te, search?q=teste assim por diante. Considere limitar a frequência com que a consulta é enviada para evitar danos ao servidor. Como alternativa, você também pode retornar uma grande quantidade de informações e, no lado do cliente, fazer a filtragem. Isso funciona bem se o usuário inserir categorias amplas que podem restringir bastante as coisas.
Kat
2
Não importa a solução, troque dados em um formato neutro em tecnologia. Portanto, não use uma representação interna ou será mais difícil gravar clientes para sua API.
Kwebble
Dependendo das suas necessidades, esta biblioteca pode funcionar: github.com/jirutka/rsql-parser . Ele possui uma extensão JPA se você usa o JPA ou pode escrever seu próprio visitante para mapear para a API do seu mecanismo de pesquisa. E você pode adicionar seu próprio operador.
Walfrat 30/08

Respostas:

54

Antes de ler minha resposta, gostaria de dizer que concordei com @Neil. Temos que escolher nossas batalhas. Geralmente, queremos dar o melhor de nós, mas às vezes há muito pouco espaço para discussão e precisamos tomar decisões contra a nossa vontade.

De qualquer forma, na resposta de Neil, sinto falta de mais uma coisa. Documentação . Apenas para garantir que os desenvolvedores saibam que os pedidos do POST /searchsão seguros.

Dito isto.

1. Dê uma chance para GET

Considere a GETopção primeiro. Confira o comprimento máximo do URL desta pergunta . Avalie se sua cadeia de caracteres de consulta mais longa tem mais de 2000 caracteres. Se não, e você não espera, continue GET. Pode parecer feio, mas pelo menos você pode marcar o URL como favorito e, é claro, tem todas as vantagens derivadas da semântica do método (idempotência, segurança e armazenamento em cache)

1.1 Tente codificar a string de consulta

Por exemplo, na base 64. Até o javascript suporta codificações da base 64 .

É assim que funciona:

  1. Crie o JSON com todos os filtros e normalize-o.
  2. Analise-o como string
  3. Codifique
  4. Envie o JSON codificado como solicitação param ( /search?q=SGVsbG8gV29ybGQh....).
  5. No lado do servidor, decodifique o parâmetro q .
  6. Desserializar a sequência JSON

Anteriormente, crie a cadeia JSON mais longa possível, codifique-a e obtenha o comprimento. Avalie se a sequência codificada se encaixa na URL. Eu implementei o seguinte snippet no Fiddle.js para você testar. (Espero que ainda funcione) 1

Os códigos da base 64 são determinísticos e reversíveis, portanto não há chance de colisões.

Com consultas codificadas, também poderíamos salvar pesquisas no banco de dados, marcar o URL como favorito, compartilhar links etc. E, é claro, não precisamos escapar / tirar o escape da string.

1.2 Tente com aliases

Lendo este blog sobre como criar APIs REST, lembrei-me de mais uma alternativa. Aliases para consultas comuns .

Acho isso interessante pelas próximas razões

  • Encurte o comprimento da string de consulta. Torna a API mais limpa e fácil de usar

    GET / tickets /? Status = fechado e fechadoAt = xxx vs GET / tickets / recentemente fechado /

  • Combinável com mais aliases ou mais parâmetros de solicitação.

    GET / tickets /? Status = fechado e fechadoAt = xxx e dentro de 30min vs GET / tickets / recentemente fechado /? Within = 30min

  • Podemos combinar aliases com cadeias de consulta codificadas

    GET / tickets /? Status = fechado e fechadoAt = xxx e dentro de 30min vs GET / tickets / recentemente fechado /? Q = SGVsbG8g ...


1: usei o JSON, mas poderíamos usar outros formatos assim que desserializá-lo no lado do servidor.

Laiv
fonte
2
Isso é prático e correto. Também é importante notar que a maioria das linguagens de programação torna trivial a transformação de um hash em uma string de consulta, portanto é muito fácil iniciar uma ação GET.
Aluan Haddad
1
Eu amo o Spring stackoverflow.com/questions/16942193/… Não acredito que funcionou na primeira tentativa: D. Sobre o comprimento da URL, é menor que 1k, embora ainda precisemos iterar as especificações.
Anat0lius
Então, vá com GET. Pela simplicidade. Com o Spring MVC, você pode obter o mesmo mapeamento com GET. Procure WebArgumentResolver do Spring ;-)
Laiv
Base64 aumenta o tamanho da carga útil em cerca de 4/3. Enquanto a urlencoding pode torná-lo 3/1 para caracteres especiais, as consultas com caracteres seguros são mantidas no mesmo tamanho. Existe algum outro motivo para usar o base64?
villasv
Na verdade não. Apenas não gosto (des) de escape de URLs. O excesso de carga útil é a troca aqui. Ele ainda precisa se ajustar ao tamanho máximo de GET por solicitação. Foi por isso que construí o trecho. Para o usuário tentar. Quando escrevi a resposta, priorizei a semântica da Web sobre os detalhes da implementação. O ponto principal da resposta é "continue tentando com GET". Encontre o seu caminho ou use qualquer um desses que eu compartilho com você.
Laiv 18/02
13

Se tudo o que você tem é um martelo, tudo parece um prego. Parece que o problema aqui é que você está tentando transformar uma página de pesquisa em uma RESTful, e esse dificilmente parece ser um padrão comum para o design RESTful resolver.

Basta ir com uma solicitação POST com os parâmetros fornecidos pelo usuário para obter as informações necessárias no back-end. Suponho que você não precise fazer nada além de realizar uma pesquisa; portanto, não há chance de inserir nesta página. Basta adicionar uma pesquisa / ao final do seu URL para não correr o risco de entrar em conflito com a página / users, que seria RESTful.

Neil
fonte
2
@LiLou_: Para esse requisito, existem apenas duas possibilidades realistas: 1. Leia todos os dados no seu front-end e faça a filtragem lá. Isso pode ser proibitivo na quantidade necessária de memória. 2. Faça uma nova solicitação ao servidor para cada alteração nos critérios de pesquisa. Não importa se essa é uma solicitação POST ou GET, mas a latência de rede envolvida pode ser perturbadora para o senso de atualizações de "tempo real" do usuário.
Bart van Ingen Schenau
2
Eu concordo em discordar, POST significa outra coisa semântica. Eu sugeriria ir com o GET e passar todos os dados do filtro no parâmetro de consulta agora, se houver muitos parâmetros, é uma falha no nível do aplicativo.
CodeYogi
2
@CodeYogi às vezes o cliente não oferece espaço para discussão. Eu implementei páginas de exibição semelhantes ao Excel com 40 a 50 colunas, cada uma delas com seu próprio filtro. E classificável, é claro. De qualquer forma, ainda é possível com GET, mas ele pode não parecer muito fashion
LAIV
1
Nesse caso, o @Laiv precisa ser discutido seriamente porque o POST se destina a alterações no estado do servidor. Os casos de uso como este não são excepcionais, portanto, devem ser tratados sem hacks.
CodeYogi
2
Nesses casos, a documentação é uma obrigação. Eu tive uma discussão séria com os clientes sobre a usabilidade de seus aplicativos. Mais tarde, provei que estava certo porque o usuário final concordou comigo. No entanto, às vezes você tem que escolher suas batalhas.
19417 Laiv
0

Depende totalmente de qual é o seu modelo de API: como nenhum ou como verbo.

Se a API for nenhuma, convém obter a lista de objetos da seguinte maneira:

GET: /api/v1/objects

Nesse caso, você deve enviar dados como parâmetros de solicitação. Portanto, você deve descrever seus parâmetros como uma lista simples de valores-chave:

GET: /api/v1/objects

key1 : val1
key2.key1 : val 21
key2.key2 : val 22
....

Algumas plataformas suportam o resolvedor de parâmetros personalizado (por exemplo, Spring MVC), e você pode converter parâmetros em um objeto.

Mostafa
fonte