Como desenhar uma pesquisa/filtragem repousante?

Estou a criar e a implementar uma API repousante no PHP. No entanto, não consegui implementar o meu desenho inicial.

GET /users # list of users
GET /user/1 # get user with id 1
POST /user # create new user
PUT /user/1 # modify user with id 1
DELETE /user/1 # delete user with id 1
Até agora é bastante normal, não é?

O meu problema é com o primeiro. Estava a considerar enviar parâmetros no corpo do pedido para filtrar a lista. Isto porque eu quero ser capaz de especificar filtros complexos sem obter um url super longo, como:

GET /users?parameter1=value1&parameter2=value2&parameter3=value3&parameter4=value4
Em vez disso, queria ter alguma coisa. tipo:

GET /users
# Request body:
{
    "parameter1": "value1",
    "parameter2": "value2",
    "parameter3": "value3",
    "parameter4": "value4"
}

que é muito mais legível e lhe dá grandes possibilidades de definir filtros complexos.

De qualquer forma, não devolvi o corpo do pedido para pedidos. Eu também tentei http_get_request_body(), mas a hospedagem compartilhada que eu estou usando não tem pecl_http. Não sei se teria ajudado.

Encontrei esta pergunta e percebi que o GET provavelmente não devia ter um corpo de pedido. Foi um pouco inconclusivo, mas aconselharam-no a não o fazer.

Então agora não sei o que fazer. Como é que se desenha uma função RESTful search/filterng?

Acho que preciso, mas não me parece muito descansado.

Author: Community, 2011-02-16

7 answers

A melhor maneira de implementar uma busca repousante é considerar a busca em si como um recurso. Então você pode usar o verbo POST porque você está criando uma busca. Você não tem que literalmente criar algo em um banco de dados, a fim de usar um POST.

Por exemplo:

Accept: application/json
Content-Type: application/json
POST http://example.com/people/searches
{
  "terms": {
    "ssn": "123456789"
  },
  "order": { ... },
  ...
}
Está a criar uma pesquisa do ponto de vista do utilizador. Os pormenores de aplicação são irrelevantes. Algumas API descansadas podem nem mesmo precisar de persistência. Trata-se de um pormenor de execução.
 342
Author: Jason Harrelson, 2014-03-20 05:11:48

Se você usar o corpo do pedido em um pedido do GET, você está quebrando o princípio do resto, porque seu pedido do GET não será capaz de ser cache, porque o sistema de cache usa apenas a URL.

E o que é pior, a sua URL não pode ser marcada, porque a URL não contém todas as informações necessárias para redireccionar o utilizador para esta página

Utilize URL ou parâmetros da consulta em vez de parâmetros do corpo do pedido.

Por exemplo:

/myapp?var1=xxxx&var2=xxxx
/myapp;var1=xxxx/resource;var2=xxxx 
Na verdade, o HTTP RFC 7231 diz que: [3]

uma carga útil dentro de uma mensagem de pedido de GET não tem semântica definida; enviar um corpo de carga útil em um pedido de GET pode causar algumas implementações existentes para rejeitar o pedido.

Para mais informações, dê uma vista de olhos aqui.
 90
Author: jfcorugedo, 2016-09-16 13:56:56

Parece que a filtragem/pesquisa de recursos pode ser implementada de uma forma repousante. A idéia é introduzir um novo endpoint chamado /filters/ ou /api/filters/.

Usando este parâmetro O filtro pode ser considerado como um recurso e, portanto, criado através do método POST. Desta forma - é claro-o corpo pode ser usado para carregar todos os parâmetros, bem como estruturas complexas de pesquisa/filtro podem ser criadas.

Depois de criar esse filtro, existem duas possibilidades de obter o filtro/pesquisa resultado.

  1. Um novo recurso com ID único será devolvido juntamente com o código de Estado 201 Created. Em seguida, usando este ID a GET pedido pode ser feito para /api/users/ Como:

    GET /api/users/?filterId=1234-abcd
    
  2. Após o novo filtro ser criado através de POST, não irá responder com 201 Created mas de uma vez com 303 SeeOther, juntamente com o cabeçalho Location apontando para /api/users/?filterId=1234-abcd. Este redirecionamento será tratado automaticamente através da biblioteca subjacente.

Em ambos os cenários, é necessário fazer dois pedidos para filtrar o filtro. resultados-isso pode ser considerado como uma desvantagem, especialmente para aplicações móveis. Para aplicações móveis, utilizaria uma única chamada POST para /api/users/filter/.

Como manter os filtros criados?

Podem ser armazenados em DB e utilizados posteriormente. Eles também podem ser armazenados em algum armazenamento temporário, por exemplo redis e ter algum TTL após o qual eles vão expirar e serão removidos.

quais são as vantagens desta ideia?

Filtros, os resultados filtrados são cacheable e pode até ser marcado.

 36
Author: Opal, 2017-11-14 14:41:51

Eu acho que você deve ir com os parâmetros do pedido, mas apenas enquanto não houver um cabeçalho HTTP apropriado para realizar o que você quer fazer. A especificação HTTP não diz explicitamente, que o GET não pode ter um corpo. No entanto este documento diz:

Por convenção, quando o método de obtenção é utilizado, todas as informações necessárias identificar o recurso em que está codificado o URI. Não existe convenção em HTTP / 1.1 para uma interação segura (e.g., recuperacao) onde o cliente fornece dados para o servidor em uma entidade HTTP corpo e não na parte da consulta de a URI. Isto significa que, por segurança operações, URIs pode ser longo.

 13
Author: Daff, 2011-02-16 19:02:24

Não se preocupe muito se a sua API inicial estiver totalmente descansada ou não (especialmente quando estiver apenas nos estágios Alfa). Pôr a canalização a funcionar primeiro. Você pode sempre fazer algum tipo de transformação de URL/re-escrita para mapear as coisas, refinando iterativamente até que você obtenha algo estável o suficiente para testes generalizados ("beta").

Você pode definir URIs cujos parâmetros são codificados pela posição e Convenção nos próprios URIs, prefixados por um caminho que você sabe que irá sempre mapear a alguma coisa. Eu não sei PHP, mas eu assumiria que tal facilidade existe (como existe em outras línguas com frameworks web):

. ie.do a "user" type of search with param[I]=value[i] for i=1..4 on store #1 (with value1,value2,value3,... como abreviatura para parâmetros de pesquisa URI):

1) GET /store1/search/user/value1,value2,value3,value4

Ou

2) GET /store1/search/user,value1,value2,value3,value4

Ou como se segue (embora eu não o recomendasse, mais sobre isso mais tarde)

3) GET /search/store1,user,value1,value2,value3,value4

Com a opção 1, você mapeia todos os URIs prefixados com /store1/search/user para o controlador de pesquisa (ou seja qual for a designação do PHP) faltando para fazer buscas por recursos sob store1 (equivalente a /search?location=store1&type=user.

Por convenção documentada e aplicada pela API, os valores 1 a 4 são separados por vírgulas e apresentados por essa ordem.

A Opção 2 adiciona o tipo de pesquisa (neste caso user) como parâmetro posicional #1. Qualquer das opções é apenas uma escolha cosmética.

A opção 3 também é possível, mas acho que não gostaria. Acho que a capacidade de busca interior certos recursos devem ser apresentados no próprio URI que precede a própria pesquisa (como se indicando claramente no URI que a pesquisa é específica dentro do recurso.)

A vantagem disto sobre os parâmetros de passagem no URI é que a pesquisa é parte do URI (assim tratando uma pesquisa como um recurso, um recurso cujo conteúdo pode - e irá - mudar ao longo do tempo.) A desvantagem é que a ordem dos parâmetros é obrigatória.

Uma vez que faças algo assim, podes usar o GET, e ... seria um recurso apenas de leitura (uma vez que você não pode postar ou colocá - lo-ele é atualizado quando ele é GET'ed). Seria também um recurso que só existe quando é invocado.

Também se pode adicionar mais semântica a ele, Cache os resultados por um período de tempo ou com uma DELETE fazendo com que o cache seja excluído. Isto, no entanto, pode ser contrário ao que as pessoas normalmente usam para excluir (e porque as pessoas tipicamente controlam cache com cabeçalhos de cache.)

Como vais fazer? seria uma decisão de design, mas seria assim que eu faria. Não é perfeito, e estou certo de que haverá casos em que fazer isso não é a melhor coisa a fazer (especialmente para critérios de pesquisa muito complexos).
 9
Author: luis.espinal, 2017-01-26 14:38:18

Como estou a usar uma infra-estruturalaravel/php tenho tendência a usar algo assim:

/resource?filters[status_id]=1&filters[city]=Sydney&page=2&include=relatedResource

O PHP transforma automaticamente [] Os parâmetros num array, por isso neste exemplo vou acabar com uma variável $filter que contém um array/objecto de filtros, juntamente com uma página e quaisquer recursos relacionados que eu queira carregar eager.

Se você usar outra língua, esta ainda pode ser uma boa Convenção e você pode criar um analisador para converter [] para uma matriz.

 7
Author: the-a-train, 2018-06-22 17:37:50
Para tua informação, sei que é um pouco tarde, mas para quem estiver interessado. Depende de quão descansado você quer ser, você terá que implementar suas próprias estratégias de filtragem como o HTTP spec não é muito claro sobre isso. Gostaria de sugerir a codificação url de todos os parâmetros do filtro, por exemplo
GET api/users?filter=param1%3Dvalue1%26param2%3Dvalue2
Eu sei que é feio, mas acho que é a maneira mais descansada de o fazer e deve ser fácil de processar do lado do servidor.
 2
Author: shanks, 2017-04-21 11:02:46