Atualmente, estou projetando e implementando uma API RESTful em PHP. No entanto, não consegui implementar meu design 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 padrão, certo?
Meu problema é com o primeiro GET /users
. Eu estava pensando em enviar parâmetros no corpo da solicitação para filtrar a lista. Isso ocorre porque eu quero poder especificar filtros complexos sem obter um URL super longo, como:
GET /users?parameter1=value1¶meter2=value2¶meter3=value3¶meter4=value4
Em vez disso, eu queria ter algo como:
GET /users
# Request body:
{
"parameter1": "value1",
"parameter2": "value2",
"parameter3": "value3",
"parameter4": "value4"
}
o que é muito mais legível e oferece grandes possibilidades para definir filtros complexos.
De qualquer forma, file_get_contents('php://input')
não retornou o corpo da GET
solicitação. Eu também tentei http_get_request_body()
, mas a hospedagem compartilhada que estou usando não tem pecl_http
. Não tenho certeza de que teria ajudado de qualquer maneira.
Encontrei essa pergunta e percebi que o GET provavelmente não deveria ter um corpo de solicitação. Foi um pouco inconclusivo, mas eles desaconselharam.
Então agora não tenho certeza do que fazer. Como você cria uma função de pesquisa / filtragem RESTful?
Suponho que eu poderia usar POST
, mas isso não parece muito RESTful.
Respostas:
A melhor maneira de implementar uma pesquisa RESTful é considerar a própria pesquisa como um recurso. Em seguida, você pode usar o verbo POST porque está criando uma pesquisa. Você não precisa literalmente criar algo em um banco de dados para usar um POST.
Por exemplo:
Você está criando uma pesquisa do ponto de vista do usuário. Os detalhes de implementação são irrelevantes. Algumas APIs RESTful podem nem precisar de persistência. Esse é um detalhe de implementação.
fonte
Se você usar o corpo da solicitação em uma solicitação GET, estará violando o princípio REST, porque sua solicitação GET não poderá ser armazenada em cache, porque o sistema de cache usa apenas a URL.
E o pior é que seu URL não pode ser marcado como favorito, porque o URL não contém todas as informações necessárias para redirecionar o usuário para esta página.
Use parâmetros de URL ou consulta em vez de parâmetros do corpo da solicitação.
por exemplo:
De fato, o HTTP RFC 7231 diz que:
Uma carga útil dentro de uma mensagem de solicitação GET não possui semântica definida; o envio de um corpo de carga útil em uma solicitação GET pode fazer com que algumas implementações existentes rejeitem a solicitação.
Para mais informações, dê uma olhada aqui
fonte
Parece que a filtragem / pesquisa de recursos pode ser implementada de maneira RESTful. A idéia é introduzir um novo terminal chamado
/filters/
or/api/filters/
.O uso desse filtro de terminal pode ser considerado como um recurso e, portanto, criado via
POST
método. Dessa maneira, é claro, o corpo pode ser usado para transportar todos os parâmetros, bem como estruturas complexas de pesquisa / filtro.Depois de criar esse filtro, há duas possibilidades para obter o resultado da pesquisa / filtro.
Um novo recurso com ID exclusivo será retornado junto com o
201 Created
código de status. Em seguida, usando esse ID, umaGET
solicitação pode ser feita/api/users/
como:Após a criação do novo filtro,
POST
ele não responderá com,201 Created
mas ao mesmo tempo303 SeeOther
junto com oLocation
cabeçalho apontando para/api/users/?filterId=1234-abcd
. Esse redirecionamento será tratado automaticamente através da biblioteca subjacente.Nos dois cenários, é necessário fazer duas solicitações para obter os resultados filtrados - isso pode ser considerado uma desvantagem, especialmente para aplicativos móveis. Para aplicativos móveis, eu usaria uma
POST
chamada para/api/users/filter/
.Como manter os filtros criados?
Eles podem ser armazenados no DB e usados posteriormente. Eles também podem ser armazenados em algum armazenamento temporário, por exemplo, redis e possuem algum TTL após o qual expiram e serão removidos.
Quais são as vantagens dessa idéia?
Filtros, resultados filtrados são armazenados em cache e podem até ser marcados.
fonte
Eu acho que você deve seguir os parâmetros de solicitação, mas apenas enquanto não houver um cabeçalho HTTP apropriado para realizar o que deseja fazer. A especificação HTTP não diz explicitamente que GET não pode ter um corpo. No entanto, este documento afirma:
fonte
Como eu estou usando um back-end do laravel / php, eu costumo usar algo assim:
O PHP transforma automaticamente os
[]
parâmetros em uma matriz, portanto, neste exemplo, terminarei com uma$filter
variável que contém uma matriz / objeto de filtros, juntamente com uma página e quaisquer recursos relacionados que eu queira carregar com urgência.Se você usar outro idioma, isso ainda poderá ser uma boa convenção e você poderá criar um analisador para converter
[]
em uma matriz.fonte
[
e]
. Usar representações codificadas desses caracteres para agrupar parâmetros de consulta é uma prática bem conhecida. É usado até na especificação JSON: API .Não se preocupe muito se sua API inicial for totalmente RESTful ou não (especialmente quando você está apenas nos estágios alfa). Faça com que o encanamento de back-end funcione primeiro. Você sempre pode fazer algum tipo de transformação / reescrita de URL para mapear as coisas, refinando iterativamente até obter algo estável o suficiente para testes generalizados ("beta").
Você pode definir URIs cujos parâmetros são codificados por posição e convenção nos próprios URIs, prefixados por um caminho que você sabe que sempre mapeará para alguma coisa. Eu não sei PHP, mas suponho que esse recurso exista (como existe em outras linguagens com estruturas da web):
.ie. Faça um tipo de pesquisa "usuário" com o parâmetro [i] = valor [i] para i = 1..4 na loja 1 (com valor1, valor2, valor3, ... como uma abreviação para os parâmetros de consulta do URI):
ou
ou da seguinte forma (embora eu não o recomende, mais sobre isso mais tarde)
Com a opção 1, você mapeia todos os URIs prefixados
/store1/search/user
para o manipulador de pesquisa (ou qualquer que seja a designação do PHP) por padrão para fazer pesquisas por recursos em store1 (equivalente a/search?location=store1&type=user
.Por convenção documentada e aplicada pela API, os valores de parâmetros 1 a 4 são separados por vírgulas e apresentados nessa ordem.
A opção 2 adiciona o tipo de pesquisa (neste caso
user
) como parâmetro posicional # 1. Qualquer uma das opções é apenas uma escolha cosmética.A opção 3 também é possível, mas acho que não gostaria. Eu acho que a capacidade de pesquisa dentro de certos recursos deve ser apresentada no próprio URI que precede a pesquisa em si (como se estivesse indicando claramente no URI que a pesquisa é específica dentro do recurso).
A vantagem disso ao passar parâmetros no URI é que a pesquisa faz parte do URI (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 .
Depois de fazer algo assim, você pode usar GET, e ele seria um recurso somente leitura (já que você não pode POST ou PUT) - ele é atualizado quando é obtido. Também seria um recurso que só existe quando é invocado.
Também se pode adicionar mais semântica armazenando em cache os resultados por um período de tempo ou com um DELETE fazendo com que o cache seja excluído. Isso, no entanto, pode ser contrário ao que as pessoas normalmente usam DELETE (e porque as pessoas normalmente controlam o cache com cabeçalhos de cache).
Como você agiria seria uma decisão de design, mas seria assim que eu agiria. Não é perfeito, e tenho certeza de que haverá casos em que fazer isso não é a melhor coisa a se fazer (especialmente para critérios de pesquisa muito complexos).
fonte
FYI: Eu sei que é um pouco tarde, mas para quem estiver interessado. Depende do quão RESTful você deseja ser, você terá que implementar suas próprias estratégias de filtragem, pois a especificação HTTP não é muito clara. Gostaria de sugerir a codificação de URL de todos os parâmetros de filtro, por exemplo
Eu sei que é feio, mas acho que é a maneira mais RESTful de fazer isso e deve ser fácil de analisar no lado do servidor :)
fonte