Desenha / efetua login ou / registra recursos RESTfully?


122

Eu estava projetando um aplicativo da web e parei para pensar em como minha API deveria ser projetada como um serviço da web RESTful. Por enquanto, a maioria dos meus URIs são genéricos e podem se aplicar a vários aplicativos da web:

GET  /logout   // destroys session and redirects to /
GET  /login    // gets the webpage that has the login form
POST /login    // authenticates credentials against database and either redirects home with a new session or redirects back to /login
GET  /register // gets the webpage that has the registration form
POST /register // records the entered information into database as a new /user/xxx
GET  /user/xxx // gets and renders current user data in a profile view
POST /user/xxx // updates new information about user

Tenho a sensação de que estou fazendo muitas coisas erradas aqui, depois de fuçar no SO e no Google.

Começando com /logout, talvez já que eu realmente não faça GETnada - pode ser mais apropriado POSTuma solicitação para /logoutdestruir a sessão e, em seguida, GETredirecionar. E o /logoutprazo deve permanecer?

Que tal /logine /register. Eu poderia mudar /registerpara, /registrationmas isso não altera a forma como meu serviço funciona fundamentalmente - se ele tiver problemas mais profundos.

Percebo agora que nunca exponho um /userrecurso. Talvez isso pudesse ser utilizado de alguma forma. Por exemplo, considere o usuário myUser:

foo.com/user/myUser

ou

foo.com/user

O usuário final não requer esse detalhamento extra no URI. No entanto, qual é mais atraente visualmente?

Percebi algumas outras perguntas aqui no SO sobre esse negócio REST, mas eu realmente gostaria de receber algumas orientações sobre o que expus aqui, se possível.

Obrigado!

ATUALIZAR:

Eu também gostaria de algumas opiniões sobre:

/user/1

vs

/user/myUserName

Respostas:


63

Uma coisa se destaca em particular por não ser REST-ful: o uso de uma solicitação GET para efetuar logout.

(de http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods )

Alguns métodos (por exemplo, HEAD, GET, OPTIONS e TRACE) são definidos como seguros, o que significa que se destinam apenas à recuperação de informações e não devem alterar o estado do servidor. Em outras palavras, eles não devem ter efeitos colaterais, além de efeitos relativamente inofensivos, como registro, armazenamento em cache, veiculação de anúncios em banner ou incremento de um contador na web. [...]

O [...] tratamento [de solicitações GET] pelo servidor não é tecnicamente limitado de forma alguma. Portanto, a programação descuidada ou deliberada pode causar alterações não triviais no servidor. Isso é desencorajado, porque pode causar problemas para cache da Web, motores de busca e outros agentes automatizados [...]

Quanto ao logout e redirecionamento, você pode ter uma postagem em seu URI de logout com uma resposta 303 redirecionando para a página de pós-logout.

http://en.wikipedia.org/wiki/Post/Redirect/Get

http://en.wikipedia.org/wiki/HTTP_303

Edite para abordar questões de design de URL:

"Como faço para projetar meus recursos?" é uma questão importante para mim; "como faço para projetar meus URLs?" é uma consideração em duas áreas:

URLs que os usuários verão não devem ser muito feios e significativos, se possível; se quiser que os cookies sejam enviados em solicitações para algum recurso, mas não para outros, você vai querer estruturar seus caminhos e caminhos de cookie.

Se JRandomUserquiser ver seu próprio perfil e quiser que o URL seja mais bonito do que foo.com/user/JRandomUserou foo.com/user/(JRandom's numeric user id here), você pode criar um URL separado apenas para que o usuário veja suas próprias informações:

GET foo.com/profile /*examines cookies to figure out who 
                     * is logged in (SomeUser) and then 
                     * displays the same response as a
                     * GET to foo.com/users/SomeUser.
                     */

Eu alegaria ignorância muito mais prontamente do que sabedoria sobre este assunto, mas aqui estão algumas considerações de design de recursos:

  1. Consumidor: quais recursos devem ser visualizados diretamente em um navegador, carregados via XHR ou acessados ​​por algum outro tipo de cliente?
  2. Acesso / identidade: a resposta depende de cookies ou referenciadores?

1
Ótima resposta, obrigado! Se eu fosse implementar sua sugestão de URL separada ( GET foo.com/profile/), isso faria parte, como sugeriu momo, da camada de apresentação? Em outras palavras, o que exatamente essa GETsolicitação deve retornar? Uma página da web ou algum JSON?
Qcom

2
Ah, acho que entendo agora. A resposta de Momo realmente esclareceu as coisas. Assim, uma API RESTful é construído para permitir que múltiplas plataformas para GET, POST, PUTe DELETErecursos. Um site é apenas mais uma plataforma de acesso à API. Em outras palavras, o design da URL do site é completamente diferente do design da API RESTful. Por favor me diga se ainda estou errado haha.
Qcom

Sim, torne sua API REST um conjunto de URLs e seu site um conjunto diferente. Em seguida, o URL do seu site deve fornecer HTML + Javascript apropriado para que a página faça XmlHttpRequests apropriados aos URLs da API para atuar como um cliente.
ellisbben,

129

RESTful pode ser usado como uma diretriz para construir URLs, e você pode criar sessões e recursos de usuários :

  • GET /session/new obtém a página da web que contém o formulário de login
  • POST /session autentica credenciais no banco de dados
  • DELETE /session destrói a sessão e redireciona para /
  • GET /users/new obtém a página da web que contém o formulário de registro
  • POST /users registra as informações inseridas no banco de dados como um novo / usuário / xxx
  • GET /users/xxx // obtém e renderiza os dados atuais do usuário em uma visualização de perfil
  • POST /users/xxx // atualiza novas informações sobre o usuário

Eles podem ser no plural ou no singular (não tenho certeza de qual é o correto). Eu geralmente uso /userspara uma página de índice do usuário (como esperado) e /sessionspara ver quem está logado (como esperado).

Usar o nome no URL em vez de um número ( /users/43vs. /users/joe) geralmente é motivado pelo desejo de ser mais amigável com os usuários ou mecanismos de pesquisa, não por quaisquer requisitos técnicos. Qualquer um está bom, mas eu recomendo que você seja consistente.

Acho que se você for com o cadastro / login / logout ou sign(in|up|out), não funciona tão bem com a terminologia repousante.


6
Impressionante! Gosto de como você pronunciou esses recursos; isso é muito limpo. Embora, pelo que eu ouvi, não está anexando /newa GET /session/não RESTful? Ouvi dizer que os verbos são normalmente deixadas para os verbos HTTP ( GET, POST, etc.).
Qcom

2
@Zach new não é um verbo. Nesse caso, é um sub-recurso da sessão.
Kugel

Como determinar qual sessão excluir em DELETE / sessão? O Curl não envia cookies nem parâmetros na solicitação DELETE. Eu presumo - apenas para usar DELETE / session / sessionId? Outra questão é como retornar o id da sessão no POST / sessão e em qual formato.
Tvaroh

9
Repousante, na verdade, é uma maneira de se tornar infeliz e perder tempo com coisas que não importam.
Jian Chen

6
Pessoalmente não gosto da ideia de ter as rotas que devolvem o formulário (/ new). Isso quebra a separação entre a visão e a lógica de negócios. Tha disse, sem as / novas rotas a sugerida parece perfeita.
Scadge

60

As sessões não são RESTful

  • Sim eu conheço. Está sendo feito, geralmente com OAuth, mas na verdade as sessões não são RESTful. Você não deve ter um recurso / login / logout principalmente porque você não deve ter sessões.

  • Se você vai fazer isso, torne-o RESTful. Recursos são substantivos e / login e / logout não são substantivos. Eu iria com / sessão. Isso torna a criação e exclusão uma ação mais natural.

  • POST vs. GET para sessões é fácil. Se você estiver enviando usuário / senha como variáveis, eu usaria POST porque não quero que a senha seja enviada como parte do URI. Ele aparecerá em registros e possivelmente ficará exposto ao longo do fio. Você também corre o risco de o software falhar nas limitações de argumentos GET.

  • Eu geralmente uso o Basic Auth ou nenhum Auth com serviços REST.

Criação de usuários

  • É um recurso, então você não deve precisar / registrar-se.

    • POST / user - Cria um usuário se o solicitante não pode especificar o id
    • PUT / user / xxx - Cria ou atualiza um usuário presumindo que você sabe o id de antemão
    • GET / usuário - lista x IDs de usuário
    • GET / user / xxx - obtém os detalhes do usuário com id xxx
    • DELETE / user / xxx - Exclui o usuário com id xxx
  • Qual tipo de ID usar é uma questão difícil. Você tem que pensar em impor exclusividade, sobre a reutilização de ids antigos que foram EXCLUÍDOS. Por exemplo, você não deseja usar esses ids como chaves estrangeiras em um back-end se os ids forem reciclados (se possível). Você pode ter uma pesquisa embora para conversão de id externo / interno para mitigar os requisitos de back-end.


6
Esta é a melhor resposta. / login e / logout não são recursos e quebram a ideia de REST.
wle8300

5
Autenticação! = Sessão
dietbuddha

1
Sim, a tese de Fielding afirma na seção 5.1.3 que "o estado da [...] essência é mantido inteiramente no cliente." Além disso, eu argumentaria que, idealmente, a autenticação também deve ser sem estado no lado do servidor, ou seja, em vez de armazenar "tíquetes de autenticação" ativos em um banco de dados, o servidor deve ser capaz de verificar uma credencial de autenticação apenas com base na própria credencial, por exemplo, usando um token criptográfico independente em conjunto com uma chave privada. Portanto, em vez de um recurso de / sessão, pode-se introduzir um recurso de autenticação, mas também não resolve o problema ...
raner

Na verdade, / login e / logout são substantivos. Presumo que você esteja pensando em / log_in e / log_out.
TiggerToo

21

Vou simplesmente falar sobre minha experiência na integração de vários serviços da Web REST para meus clientes, sejam eles usados ​​para aplicativos móveis ou para comunicação de servidor a servidor, bem como construção de API REST para outros. Aqui estão algumas observações que obtive da API REST de outras pessoas, bem como daquelas que construímos:

  • Quando falamos API, normalmente se refere ao conjunto de interface de programação e não necessariamente a camada de apresentação. REST também é centrado em dados e não direcionado à apresentação. Dito isso, a maioria dos REST retorna dados na forma de JSON ou XML e raramente retorna uma camada de apresentação específica. Essa característica (de retornar dados e não a página da Web direta) confere ao REST a capacidade de fazer entrega em vários canais. O que significa que o mesmo serviço da web pode ser processado em HTML, iOS, Android ou mesmo usado como uma combinação de servidor para servidor.
  • É muito raro combinar HTML e REST como um URL. Por padrão, REST são pensamentos como serviços e não possuem camada de apresentação. É função de quem consome os webservices renderizar os dados dos serviços que chama de acordo com o que deseja. Até esse ponto, seu URL abaixo não está em conformidade com a maioria dos designs baseados em REST que encontrei até agora (nem com os padrões, como aqueles que vêm do Facebook ou Twitter)
GET / register // obtém a página da web que contém o formulário de registro
  • Continuando do ponto anterior, também é incomum (e eu não encontrei) que o serviço baseado em REST faça o redirecionamento, como os sugeridos abaixo:
GET / logout // destrói a sessão e redireciona para /
POST / login // autentica credenciais no banco de dados e redireciona para casa com uma nova sessão ou redireciona de volta para / login
 

Como os REST são projetados como serviços, funções como login e logout normalmente retornam resultados de sucesso / falha (normalmente em formato de dados JSON ou XML) que o consumidor interpretaria. Essa interpretação pode incluir o redirecionamento para a página da web apropriada, conforme você mencionou

  • Em REST, o URL significa as ações executadas. Por esse motivo, devemos remover o máximo de ambigüidade possível. Embora seja legítimo no seu caso ter GET e POST com o mesmo caminho (como / register) que executam ações diferentes, esse design introduz ambigüidade nos serviços fornecidos e pode confundir o consumidor de seus serviços. Por exemplo, os URLs como o que você apresenta abaixo não são ideais para serviços baseados em REST
GET / register // obtém a página da web que contém o formulário de registro
POST / register // registra as informações inseridas no banco de dados como um novo / usuário / xxx

Esses são alguns pontos do que eu lidei. Espero que possa fornecer alguns insights para você.

Agora, no que diz respeito à implementação de seu REST, estas são as implementações típicas que encontrei:

  • GET / logout  
    

    Execute o logout no back-end e retorne JSON para denotar o sucesso / falha da operação

  • POST / login
    

    Envie credenciais para o back-end. Sucesso / falha do retorno. Se for bem-sucedido, normalmente ele também retornará o token de sessão, bem como as informações de perfil.

  • POST / registrar
    

    Envie o registro para o back-end. Sucesso / falha do retorno. Se for bem-sucedido, normalmente é tratado como login bem-sucedido ou você pode optar por fazer o registro como um serviço distinto

  • GET / user / xxx
    

    Obtenha o perfil do usuário e retorne o formato de dados JSON para o perfil do usuário

  • POST / usuário / xxx 
    // renomeado para 
    POST / updateUser / xxx
    

    Publique informações de perfil atualizadas no formato JSON e atualize as informações no back-end. Sucesso / falha de retorno ao chamador


3
Sim, se você estiver integrando sua API REST com aplicativo baseado em HTML (via Javascript e AJAX), você terá enormes benefícios, pois JSON é analisado nativamente por Javascript. No Android / Java, JSON é mais fácil e direto de analisar também em comparação com XML.
momo

15
GET / logout é perigoso. GET deve ser idempotente. Também os navegadores gostam de pré-buscar <a> hrefs, o que o desconectará!
Kugel

4

Eu acredito que esta é uma abordagem RESTful para autenticação. Para LogIn você usa HttpPut. Este método HTTP pode ser usado para criação quando a chave é fornecida e chamadas repetidas são idempotentes. Para LogOff, você especifica o mesmo caminho no HttpDeletemétodo. Sem verbos utilizados. Pluralização adequada da coleção. Os métodos HTTP suportam o propósito.

[HttpPut]
[Route("sessions/current")]
public IActionResult LogIn(LogInModel model) { ... }

[HttpDelete]
[Route("sessions/current")]
public IActionResult LogOff() { ... }

Se desejar, você pode substituir a corrente por ativa.


1

Eu recomendaria usar um URL de conta de usuário semelhante ao twitter, onde o URL da conta do usuário seria algo foo.com/myUserNamecomo você pode acessar minha conta do Twitter com o URL https://twitter.com/joelbyler

Eu discordo sobre o logout que exige um POST. Como parte de sua API, se você pretende manter uma sessão, um id de sessão na forma de um UUID pode ser algo que pode ser usado para rastrear um usuário e confirmar se a ação que está sendo executada está autorizada. Então, até mesmo um GET pode passar a id da sessão para o recurso.

Resumindo, eu recomendo que você mantenha as coisas simples, as URLs devem ser curtas e memoráveis.


A questão é sobre os recursos da API. Sua resposta é sobre a camada de apresentação.
Henno de
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.