RESTful设计/ login或/ register资源?


122

我当时在设计一个Web应用程序,然后停下来思考如何将我的api设计为RESTful Web服务。目前,我的大多数URI都是通用的,可能适用于各种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

在SO和google上闲逛之后,我觉得自己在这里做错了很多。

从开始/logout,也许因为我实际上GET什么都不是-它可能更适合于POST请求/logout,销毁会话,然后GET重定向。这个/logout词应该保留吗?

/login/register。我可以更改/register为,/registration但这不会改变我的服务的基本工作原理-如果它存在更深的问题。

我现在注意到,我从不公开/user资源。也许可以以某种方式加以利用。例如,以用户myUser

foo.com/user/myUser

要么

foo.com/user

最终用户不需要URI中的额外详细信息。但是,哪一个在视觉上更有吸引力?

我在这里注意到关于REST业务的其他问题,但是如果可能的话,我真的很感谢我在这里提出的一些指导。

谢谢!

更新:

我还希望就以下问题发表意见:

/user/1

/user/myUserName

Answers:


63

特别令人讨厌的一件事是:没有使用REST:使用GET请求注销。

(摘自http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods

某些方法(例如HEAD,GET,OPTIONS和TRACE)被定义为安全的,这意味着它们仅用于信息检索,不应更改服务器的状态。换句话说,除了记录,缓存,横幅广告投放或增加Web计数器等相对无害的影响外,它们不应具有副作用。[...]

服务器对[GET请求]的[...]处理并没有任何技术限制。因此,粗心或故意的编程可能会导致服务器上的重大变化。不建议这样做,因为它可能会导致Web缓存,搜索引擎和其他自动化代理出现问题[...]

至于注销和重定向,您可以在注销URI上发布一个帖子,以提供303响应,重定向到注销后页面。

http://zh.wikipedia.org/wiki/邮寄/重定向/获取

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

编辑以解决URL设计问题:

“我如何设计资源?” 对我来说是一个重要的问题;“如何设计我的网址?” 是两个方面的考虑因素:

如果可能,用户将看到的URL不应过于丑陋和有意义;如果您希望在请求中发送Cookie而不是发送给其他资源,则需要构造路径和cookie路径。

如果JRandomUser要查看自己的个人资料,并且希望该网址比foo.com/user/JRandomUser或美观foo.com/user/(JRandom's numeric user id here),则可以为用户查看自己的信息制作一个单独的网址:

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.
                     */

在这个问题上,我比无知要容易得多,但是这里有一些资源设计方面的考虑:

  1. 消费者:哪些资源应该在浏览器中直接查看,通过XHR加载或由其他类型的客户端访问?
  2. 访问/身份:响应是否取决于Cookie或引荐来源网址?

1
好答案,谢谢!如果我要实施您单独的URL建议(GET foo.com/profile/),那么就像momo所建议的那样,这将成为表示层的一部分吗?换句话说,该GET请求应返回什么?网页还是JSON?
Qcom 2011年

2
嗯,我想我现在知道了。Momo的回答确实清除了一切。所以,一个RESTful API构建以允许多个平台GETPOSTPUT,和DELETE资源。网站只是访问API的另一个平台。换句话说,网站URL设计与RESTful API设计完全不同。请告诉我我是否仍然错误哈哈。
Qcom 2011年

是的,将您的REST API设置为一组URL,将您的网站设置为另一组。然后,您的网站URL应该返回适​​当的HTML + Javascript,以便该页面将对您的API URL进行适当的XmlHttpRequests充当客户端。
ellisbben

129

RESTful可用作构建URL的准则,您可以建立会话用户资源:

  • GET /session/new 获取具有登录表单的网页
  • POST /session 针对数据库验证凭据
  • DELETE /session 破坏会话并重定向到/
  • GET /users/new 获取具有注册表格的网页
  • POST /users 将输入的信息作为新的/ user / xxx记录到数据库中
  • GET /users/xxx //在配置文件视图中获取并呈现当前用户数据
  • POST /users/xxx //更新有关用户的新信息

这些可以是复数或单数(我不确定哪一个是正确的)。我通常用于/users用户索引页面(按预期方式),并/sessions查看谁登录(按预期方式)。

在URL中使用名称而不是数字(/users/43vs. /users/joe)通常是出于对用户或搜索引擎更友好的愿望,而不是任何技术要求。两种都不错,但我建议您保持一致。

我认为,如果您使用register / login / logout或sign(in|up|out),那么使用宁静的术语将无法正常工作。


6
太棒了!我喜欢你如何使用这些资源;很干净 虽然,据我所知,不是要附加/newGET /session/非RESTful吗?我听说动词通常留给HTTP动词(GETPOST,等)。
Qcom 2011年

2
@Zach new不是动词。在这种情况下,它是会话的子资源。
Kugel 2013年

如何确定在DELETE / session中删除哪个会话?Curl在DELETE请求中既不发送cookie也不发送任何参数。我假设-只是使用DELETE / session / sessionId?另一个问题是如何以POST / session和哪种格式返回会话ID。
Tvaroh 2013年

9
确实,让自己感到不愉快并浪费时间在根本不重要的事情上是一种方法。
陈剑

6
我个人不喜欢具有返回表格(/ new)的路线的想法。这打破了视图和业务逻辑之间的分离。他说,没有/ new路线,建议的路线看起来很完美。
Scadge '17

60

会话不是RESTful

  • 是的我知道。通常是通过OAuth完成的,但实际上会话不是RESTful的。您不应该拥有/ login / logout资源,主要是因为您不应该拥有会话。

  • 如果您要这样做,请使其为RESTful。资源是名词,/ login和/ logout不是名词。我会使用/ session。这使创建和删除操作更加自然。

  • 会话的POST与GET比较容易。如果您将用户名/密码作为变量发送,那么我将使用POST,因为我不想将密码作为URI的一部分发送。它会显示在日志中,并且可能会暴露在电线上。您还冒着使软件因GET args限制而失败的风险。

  • 我通常对REST服务使用基本身份验证或不使用身份验证。

建立使用者

  • 这是一种资源,因此您不需要/ register。

    • POST / user-如果请求者无法指定ID,则创建一个用户
    • PUT / user / xxx-假设您事先知道ID,则创建或更新用户
    • GET / user-列出x个用户ID
    • GET / user / xxx-获取ID为xxx的用户的详细信息
    • DELETE / user / xxx-删除ID为xxx的用户
  • 使用哪种ID是一个难题。您必须考虑强制执行唯一性,重新使用已删除的旧ID。例如,如果要回收ID(如果可能的话),则不想将这些ID用作后端的外键。不过,您可以查找外部/内部ID转换,以减轻后端需求。


6
这是最好的答案。/ login和/ logout不是资源,不会破坏REST的想法。
wle8300 '16

5
身份验证!=会话
Dietbuddha

1
是的,Fielding的论文在第5.1.3节中指出,“会话状态完全保留在客户端上”。此外,我认为,理想情况下,身份验证在服务器端也应该是无状态的,即,与其将活动的“身份验证票证”存储在数据库中,服务器还应该能够仅基于身份凭证本身来验证身份凭证,例如,通过结合使用自包含的加密令牌和私钥。因此,代替一个/ session资源,可以引入一个/ authentication资源,但它也不能真正解决问题……
raner

实际上,/ login和/ logout是名词。我假设您正在考虑/ log_in和/ log_out。
TiggerToo

21

我将简单地从我为客户集成各种REST Web服务的经验中谈一谈,无论是用于移动应用程序还是用于服务器到服务器的通信,以及为其他人构建REST API。以下是我从其他人的REST API以及我们自己构建的观察中收集到的一些观察结果:

  • 当我们说API时,它通常指的是编程接口的集合,而不是表示层。REST也是以数据为中心的,而不是表示驱动的。也就是说,大多数REST都以JSON或XML的形式返回数据,而很少返回特定的表示层。这种特性(返回数据而不是直接访问网页)赋予了REST进行多渠道交付的能力。这意味着同一Web服务可以以HTML,iOS,Android呈现,甚至可以用作服务器到服务器的组合。
  • 很少将HTML和REST结合为URL。默认情况下,REST被认为是服务,没有表示层。那些使用Web服务的人的工作就是根据他们想要的东西从他们调用的服务中呈现数据。到目前为止,您的下方网址与我到目前为止所遇到的大多数基于REST的设计都不符(也不符合来自Facebook或Twitter的标准)
GET / register //获取具有注册表格的网页
  • 从上一点继续,基于REST的服务进行重定向(例如以下建议的重定向)也很罕见(我还没有遇到过):
GET / logout //破坏会话并重定向到/
POST / login //验证数据库的凭据,并使用新的会话重定向到首页或重定向回/ login
 

由于REST被设计为服务,因此诸如登录和注销之类的功能通常会返回成功/失败结果(通常为JSON或XML数据格式),然后由消费者解释。这样的解释可能包括您提到的重定向到适当的网页

  • 在REST中,URL表示所采取的操作。因此,我们应消除尽可能多的歧义。在您的情况下,使GET和POST具有相同的路径(例如/ register)执行不同的操作是合法的,但是这种设计会在所提供的服务中引入歧义,并可能使您的服务使用者困惑。例如,下面介绍的URL之类的URL对于基于REST的服务而言并不理想。
GET / register //获取具有注册表单的网页
POST / register //将输入的信息作为新的/ user / xxx记录到数据库中

这些是我所处理的几点。我希望它可以为您提供一些见识。

现在,对于REST的实现,以下是我遇到的典型实现:

  • GET /登出  
    

    在后端执行注销并返回JSON,以表示操作成功/失败

  • POST /登录
    

    将凭据提交到后端。返回成功/失败。如果成功,通常它还将返回会话令牌以及配置文件信息。

  • POST /注册
    

    将注册提交到后端。返回成功/失败。如果成功,通常将其视为成功登录,或者您可以选择将其注册为一项独特的服务

  • GET /用户/ xxx
    

    获取用户个人资料并返回用户个人资料的JSON数据格式

  • POST /用户/ xxx 
    //重命名为 
    开机自检/ updateUser / xxx
    

    将更新的配置文件信息以JSON格式发布,并在后端更新信息。将成功/失败返回给呼叫者


3
是的,如果您将REST API与基于HTML的应用程序集成在一起(通过Javascript和AJAX),您将受益匪浅,因为JSON是由Javascript本地解析的。与XML相比,在Android / Java中,JSON更容易解析,也更直接。
momo

15
GET / logout很危险。GET应该是幂等的。浏览器也喜欢预取<a> hrefs,它将使您注销!
Kugel 2013年

4

我相信这是一种RESTful身份验证方法。对于LogIn,请使用HttpPut。提供密钥后,此HTTP方法可用于创建,并且重复调用是幂等的。对于LogOff,您可以在HttpDelete方法下指定相同的路径。没有动词。正确的收藏多元化。HTTP方法支持此目的。

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

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

如果需要,可以用电流代替有源。


1

我建议您使用类似于twitter的用户帐户URL,该用户的帐户URL foo.com/myUserName就像您可以使用URL进入我的twitter帐户一样 https://twitter.com/joelbyler

我不同意需要POST的注销。作为API的一部分,如果您要维护会话,则可以使用UUID形式的会话ID来跟踪用户并确认所采取的操作已得到授权。然后,即使是GET也可以将会话ID传递给资源。

简而言之,我建议您保持简单,URL应该简短易记。


问题是关于API资源。您的答案与表示层有关。
Henno
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.