DESIGN da API REST - Obtendo um recurso por meio de REST com parâmetros diferentes, mas o mesmo padrão de url

92

Tenho uma pergunta relacionada ao design de url REST. Encontrei alguns posts relevantes aqui: Diferentes representações RESTful do mesmo recurso e aqui: RESTful url para GET recurso por campos diferentes, mas as respostas não são muito claras sobre quais são as melhores práticas e por quê. Aqui está um exemplo.

Eu tenho urls REST para representar o recurso "usuários". Posso OBTER um usuário com um id ou com um endereço de e-mail, mas a representação do URL permanece a mesma para ambos. Ao ler muitos blogs e livros, vejo que as pessoas têm feito isso de muitas maneiras diferentes. Por exemplo

leia essa prática em um livro e em algum lugar no stackoverflow (não consigo encontrar o link novamente)

GET /users/id={id}
GET /users/email={email}

leia esta prática em muitos blogs

GET /users/{id}
GET /users/email/{email}

Os parâmetros de consulta são normalmente usados ​​para filtrar os resultados dos recursos representados pelo url, mas também vi essa prática sendo usada

GET /users?id={id}
GET /users?email={email}

Minha pergunta é, de todas essas práticas, qual faria mais sentido para os desenvolvedores que consomem as apis e por quê? Acredito que não existam regras imutáveis ​​quando se trata de designs de url REST e convenções de nomenclatura, mas eu só queria saber qual caminho devo seguir para ajudar os desenvolvedores a entender melhor as apis.

Todos ajudam apreciados!

shahshi15
fonte
1
Eu sei que isso é um pouco antigo, mas procurando recursos semelhantes, me deparo com essa pergunta e aquela que você estava procurando. Acredito que seja este stackoverflow.com/a/9743414/468327
Joel Berger

Respostas:

102

Na minha experiência, GET /users/{id} GET /users/email/{email}é a abordagem mais comum. Eu também esperaria que os métodos retornassem um 404 Not Found se um usuário não existir com o fornecido idou email. Eu também não ficaria surpreso em ver GET /users/id/{id}(embora, em minha opinião, seja redundante).

Comentários sobre as outras abordagens

  1. GET /users/id={id} GET /users/email={email}
    • Acho que não vi isso e, se vi, seria muito confuso. É quase como se estivesse tentando imitar parâmetros de consulta com parâmetros de caminho.
  2. GET /users?id={id} GET /users?email={email}
    • Eu acho que você acertou o prego na cabeça quando mencionou o uso de parâmetros de consulta para filtragem.
    • Será que ele vai fazer sentido para chamar este recurso com tanto um ide um email(por exemplo GET /users?id={id}&email={email})? Se não, eu não usaria um método de recurso único como este.
    • Eu esperaria que esse método recuperasse uma lista de usuários com parâmetros de consulta opcionais para filtragem, mas não esperava id, emailou qualquer identificador exclusivo, estar entre os parâmetros. Por exemplo: GET /users?status=BANNEDpode retornar uma lista de usuários banidos.

Confira esta resposta de uma pergunta relacionada.

DannyMo
fonte
Obrigado pela contribuição. Na verdade você está certo. Parece que li o nº 1 do livro "RESTful java with Jax-rs", mas um tanto fora do contexto. Mas eu me lembro muito claramente de ter lido isso como uma resposta a uma das perguntas sobre o próprio stackoverflow (acredite quando digo que estou tentando muito encontrar esse link para provar aos meus colegas também :)) e # 2 é exatamente o que eu era procurando por. Alguns comentários sobre o design. Obrigado!
shahshi15
PS: Não tenho certeza se postar isso é contra a regra aqui, mas você provavelmente pode lançar alguma luz sobre uma das minhas outras perguntas também. por favor? :) stackoverflow.com/questions/20508008/…
shahshi15
apenas para esclarecer sobre sua declaração de redundância para /users/id/{id}, isso permite funcionalidade estendida, simplesmente permite acessar um recurso através de vários identificadores (id, guid, nome). também respondeu aqui
Daniel Dubovski
4
Minha resposta seria que a maneira apropriada de fazer referência a um usuário específico, por exemplo, seria / users / {id}. Se você quisesse encontrar usuários por um endereço de e-mail específico que não seja o identificador exclusivo dos usuários, eu faria / users? Email = {email}, pois é uma forma de filtrar a coleção de usuários, não identificar um recurso específico por seu recurso identificador como / users / {id} é.
Kevin M
Ótima resposta! Perfeito para isso. A única coisa que eu recomendaria fazer também é mudar um pouco sua API, já que REST deve ser centrado no nome, então seus mapeamentos de recursos devem ser algo como: GET /user/1234e nãoGET /users/123
Sammy
36

Olhando para isso de forma pragmática, você tem uma coleção de usuários:

/users   # this returns many

Cada usuário tem um local de recurso dedicado:

/users/{id}    # this returns one

Você também tem várias maneiras de pesquisar usuários:

/users?email={email}
/users?name=*bob*

Como todos esses são parâmetros de consulta para / usuários, todos eles devem retornar listas ... mesmo que seja uma lista de 1.

Escrevi uma postagem no blog sobre design pragmático de API RESTful aqui que fala sobre isso, entre outras coisas, aqui: http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api

Vinay Sahni
fonte
Obrigado pela resposta vinay. Mas se pensarmos nisso de outra perspectiva, {email} assim como {id} SEMPRE retornará um resultado de volta. Se estivermos realmente pesquisando, por que não usar? Id = {id} assim como para e-mail? Eu queria saber se há alguma maneira de ser consistente. PS: novo no stackoverflow, não sabia que posso responder apenas uma vez. Mas obrigado pela sua resposta! Agradeço. Talvez você possa me ajudar com este também: stackoverflow.com/questions/20508008/…
shahshi15
7
É aqui que o aspecto "design" do design da API entra em jogo. Seu recurso deve ter um local dedicado. Onde fica a localização? A localização é por id? Se sim, então você não "pesquisa" por id, mas "obtém" por id. Mas você busca por tudo o mais. Claro, não há nenhuma regra definida aqui. É apenas uma perspectiva :)
Vinay Sahni
6
Eu também diria que dessa forma sua API fica mais estável. Você realmente sabe que sua identidade é única, mas tem certeza do endereço de e-mail? Mesmo que seja exclusivo, provavelmente não é permanente, pois o usuário pode alterá-lo. Portanto, / users? Email = {email} deixa claro que esta é realmente uma consulta e não o acesso a um recurso com um identificador permanente.
Lex82 01 de
Eu acredito que esta é realmente a resposta mais correta. A filtragem de uma coleção por endereço de e-mail pode ser baseada em caracteres curinga, portanto, nem sempre retorna apenas um resultado.
Kevin M
@VinaySahni O que você diria sobre um padrão como: /user?by=email&email="[email protected] "/ user? By = id & id =" xashkhx "/ users? FilterA =" a "& filterB =" b "(plural para vários usuários)
Shasak
2

Sobre os recursos do usuário

no caminho, /usersvocê sempre obterá uma coleção de recursos do usuário retornados.

no caminho, /users/[user_id]você pode esperar que várias coisas aconteçam:

  1. você deve obter um recurso singleton representando o recurso do usuário com seu identificador [user_id] ou
  2. uma resposta 404 não encontrada se nenhum usuário com id [user_id] existir ou
  3. um 401 proibido se você não tiver permissão para acessar o recurso de usuário solicitado.

Cada singleton é identificado exclusivamente por seu caminho e identificador e você os usa para encontrar o recurso. Não é possível usar vários caminhos para o singleton.

Você pode consultar o caminho /userscom parâmetros de consulta ( GETParâmetros). Isso retornará uma coleção com usuários que atendem aos critérios solicitados. A coleção que é retornada deve conter os recursos do usuário, todos com seu caminho de recurso de identificação na resposta.

Os parâmetros podem ser qualquer campo presente nos recursos da coleção; firstName, lastName,id

Sobre o email

O email pode ser um recurso ou uma propriedade / campo do recurso do usuário.

- E-mail como propriedade do usuário:

Se o campo for uma propriedade do usuário, a resposta do usuário seria algo assim:

{ 
  id: 1,
  firstName: 'John'
  lastName: 'Doe'
  email: '[email protected]'
  ...
}

Isto significa que há nenhum ponto de extremidade especial para e-mails, mas agora você pode encontrar um usuário pelo seu e-mail, enviando seguinte pedido: /[email protected]. Que (assumindo que os emails são exclusivos para usuários) retornaria uma coleção com um item de usuário que corresponde ao email.

- Email como recurso:

Mas se e-mails de usuários também são recursos. Então você pode fazer uma API onde /users/[user_id]/emailsretorna uma coleção de endereços de e-mail para o usuário com id user_id. /users/[user_id]/emails/[email_id]retorna o e-mail do usuário com user_id e ['email_id']. O que você usa como um identificador é com você, mas eu ficaria com um número inteiro. Você pode excluir um e-mail do usuário enviando uma DELETEsolicitação para o caminho que identifica o e-mail que você deseja excluir. Assim, por exemplo, DELETEem /users/[user_id]/emails/[email_id]apagará o e-mail com email_id que pertence ao usuário com user_id. Provavelmente, apenas esse usuário tem permissão para executar esta operação de exclusão. Outros usuários obterão uma resposta 401.

Se um usuário pode ter apenas um endereço de e-mail, você pode ficar com /users/[user_id]/email Isso retorna um recurso singleton. O usuário pode atualizar seu endereço de e-mail PUTdigitando o endereço de e-mail naquele url.

Wilt
fonte