RESTful重置密码和确认电子邮件

11

我在思考如何以最佳RESTful方式确认电子邮件并请求重置密码。 我只想找到正确的URI...

确认电子邮件

PUT /users/{userId}/confirmEmail?code=xyz - 由于confirmEmail,似乎不太符合RESTful标准

PUT /users/{userId}/email?confirmedBy=xyz - 或许更好?不确定

重置密码(类似的问题)

PUT /users/{userId}/resetPassword --DATA {email:xyz@xyz.xy} - 与之前的思路一样

PUT /users/{userId}/password --DATA {state:reseted,resent:xyz@xyz.xy} - 嗯...我不确定

你脑海中是否有更好的方法?:-)


2
为什么要使用 PUT 而不是 POST?因为你并没有真正为你正在处理的资源设置一个新的表现形式(而且它也不一定是幂等的)。 - Bruno
可以,好的。也许这可以使用POST,但我想要使用URI。 - dxxx
如果您想按照REST的方式进行操作,URI结构应该次于超媒体(告诉客户端如何处理此URI的方式)。 URI应该是不透明的,它们的结构是关于您如何在内部实现操作的。 您可能会对这些链接感兴趣:https://dev59.com/vG865IYBdhLWcg3wTclY#3889341 和 https://dev59.com/v2855IYBdhLWcg3w_5gQ#4044656 - Bruno
7个回答

10
如果您希望您的URI引用资源,那么将资源命名为confirmation并向用户帐户POST确认。
POST /users/{userid}/confirmation

5
真正的RESTful答案是URL并不重要,你无论如何都会在确认电子邮件中放置它,以便收件人进行跟踪。使用对于负载均衡器、反向代理、服务器等最方便的内容。
为了方便起见,即使确认请求以GET请求的形式发送,您也将最终接受它,因为这是肉体人类在点击电子邮件中的链接时浏览器所发送的(他们对Dr Roy T. Fielding等人毫不知情) :-)
既然已经完全学术化,我认为您考虑使用PUT是正确的,因为客户端幂等地放置了访问电子邮件的证据。重复请求没有进一步的影响。

从技术角度来看,URL结构对于成功处理请求并不重要。但是从易于理解、API发现和可维护性的角度来看,它们产生了巨大的影响。试图争论URL结构并不重要就像争论类名的名称并不重要一样。 - Paul Turner
3
是的,我认为我们说的是同一件事情,就像类名对于JVM来说并不重要,你可以根据程序员的喜好自由选择它们。在这种情况下,API的发现需要接收电子邮件,人工识别其中的确认链接,并单击它 :-) - Szocske

2
考虑到他提供的是为那些忘记密码的人提供重置服务,而不是已经登录的人提供更改密码服务...... 我建议使用两个服务。第一个服务用于请求重置密码邮件,第二个服务用于使用接收到的令牌设置新密码。
对于第一个服务: POST baseUrl/passwordReset 请求体
{
   "email" : "my@self.com"
}

这可以是POST或PUT,但是由于邮件投递不是受CRUD控制的资源,因此让我们不要过于追求细节,使用在html表单中经常使用的旧POST。

显然,我会控制同一客户端(ip?浏览器?...)不会在一分钟内向我发送20K封邮件。

将邮件发送给用户并不意味着旧密码无效。只有在第二个请求中更新新密码时才会发生这种情况。

响应204(即使您不知道该电子邮件,也应该执行此操作,因为如果返回错误,则意味着当您没有返回错误时,您正在向陌生人确认给定的电子邮件已注册)

对于第二个:
POST baseUrl/password
请求正文

{
    "token" : "3D21BA...4F",
    "newPassword" : "m%4pW1!O"
}

收到令牌的地方是在邮件中。因此,邮件可能会有一个链接到包含令牌的页面,当页面加载时,表单被填写并提交,令牌是一个隐藏字段,一些JavaScript从URL中读取并放在这里。

这实际上是一个需要更新的资源,所以使用POST方法。我认为没有必要为两个不同的资源/实体使用相同的URI和2个动词。

另外,我会将两者都设置为仅HTTPS,并且将所有敏感信息放在正文中,而不是URL参数中。


这也是我的逻辑所在。我正在使用完全相同的原则。 - bblue

1

这是一种RESTful的方式。

请求

PUT /{userid}/email HTTP/1.1
Content-Type: text/json+confirmation-code

{"activateCode": "23sfgsg3twt3rgsdhgs"}

响应

HTTP/1.1 200 OK
Content-Type: text/json+email-status
{"email": "my-email@address.com", "active": "true"}

URI中不需要动词 :)


2
现在,如果您能使电子邮件中的内容可点击就好了。 - Kugel

1
首先,我认为PUT不是正确的方法。 PUT的广义含义是“把这个放在这里”,其中URL标识内容应该位于何处。您实际上是要求现有资源执行某些操作,这使得POST更加正确。
回答您的直接问题,RESTful URL应该标识您想要处理请求的资源。在这种情况下,资源可以是用户或用户内的某个密码重置资源。
我的首选是密码重置资源: POST /users/{userid}/password-reset 从HTTP的角度来看,这是有意义的,因为您可以对资源发出GET请求,并收到指示如何执行密码重置的内容(例如提示关联帐户的电子邮件地址的HTML表单)。
编辑:
为了进行电子邮件验证,有两个明显的选择。您可以将电子邮件地址和确认数据POST到“确认电子邮件”资源,以请求服务器处理确认,或者您可以执行PUT将确认信息放在服务器上:

POST /users/{userid}/confirm-email

或者

PUT /users/{userid}/email-confirmation


我在之前的帖子中评论了POST,我同意。算了,让我们将其标识为密码重置资源。那么我猜你会添加类似的电子邮件确认资源?并添加查询参数或数据以获取代码? - dxxx
当你说“确认电子邮件”时,你确切指的是什么?流程的哪个部分需要用户确认他们的电子邮件?这个问题可能需要编辑以包括您的用例。 - Paul Turner
这是注册后的经典流程。你先进行注册,然后会收到一封带有确认码的电子邮件,将该确认码发送给服务器后,你的电子邮件地址就被视为已确认。 - dxxx
这里的限制将是您的电子邮件客户端:它可能只执行HTTP GET操作,这对于此操作来说并不符合RESTful标准;GET应该仅检索资源的表示形式,并且不应具有副作用(例如更新用户帐户)。我认为您必须打破RESTful模型才能实现您想要的。 - Paul Turner
是的,我正在考虑这个。而且我不必做得很差劲。RESTful 接口用于联系应用程序逻辑。该接口由 Web 前端调用。因此,我将为前端创建一些 GET URL,其中将有一些填写了代码(来自电子邮件)的表单,通过提交此表单,Web 应用程序将调用 REST API,无论它调用什么方法(它可以调用任何方法)。 - dxxx
我更新了我的答案,加入了更适合你提出的流程的相关内容。 - Paul Turner

0

我认为像第一个例子中那样拥有confirmEmail并没有什么问题。在URL中,您有用户的密钥,confirmEmail是操作,并且该操作的数据在查询字符串中。


据我所知,编写RESTful接口总是需要做出妥协的,但你应该把URI视为图中的路径。在URI中添加一个操作并不符合RESTful的设计原则。这个URI告诉我,在用户之间查找指定用户及其确认电子邮件,有些奇怪... - dxxx
@dxxx,将动作名称放在URI中没有问题,只要获取该URI上的表示形式实际上不执行该动作(但为客户端提供一个表单,告诉客户端如何执行此操作,例如)。 (类似于这个答案。) - Bruno
我学过不这样做,所以我正在尝试找到另一种方法。我知道资源(resource)!= 模型(model)。资源只是与具有属性的对象不同的东西。但是,在URI中仍然有一个动作似乎...我不知道...感觉很糟糕 :-) - dxxx
如果 "confirm" 这个词的动词方向让您感到不适,请将其称为 "confirmationEmail"。 - laz

0

最近我在编程中处理了以下内容:

POST /{base_url}/password

因为我实际上正在创建一个新的随机密码并将其发送给用户。

另外:

PUT /{base_url}/confirmation?token=...

因为我正在更新用户注册时已经发送的确认信息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接