Understanding REST: Verbs, error codes, and authentication
我正在寻找一种方法,在我的基于PHP的Web应用程序、数据库和CMS中围绕默认函数包装API。
我环顾四周,发现了几个"骨架"框架。除了我问题的答案之外,还有一个我喜欢的补品,一个休息框架,因为它非常轻。
我最喜欢REST,因为它很简单,并且希望基于它创建一个API架构。我正在努力使自己的头脑围绕着基本原则,但还没有完全理解它。因此,有一些问题。
1。我理解对吗?
假设我有一个资源"用户"。我可以这样设置一些URI:
1 2 3 4 5 | /api/users when called with GET, lists users /api/users when called with POST, creates user record /api/users/1 when called with GET, shows user record when called with PUT, updates user record when called with DELETE, deletes user record |
到目前为止,这是否是一个RESTful架构的正确表示?
2。我需要更多动词
理论上,创建、更新和删除可能就足够了,但实际上我需要更多的动词。我意识到这些东西可以嵌入到更新请求中,但是它们是特定的操作,可以有特定的返回代码,我不想将它们全部抛出到一个操作中。
在用户示例中,我们会想到:
1 2 3 4 | activate_login deactivate_login change_password add_credit |
我将如何表示那些在RESTful URL体系结构中的操作?
我的直觉是对一个类似
1 | /api/users/1/activate_login |
并期望返回状态代码。
不过,这与使用HTTP动词的想法有所不同。你怎么认为?
三。如何返回错误信息和代码
REST的优点很大程度上源于它对标准HTTP方法的使用。在一个错误中,我发出一个带有3xx、4xx或5xx错误状态代码的头。对于详细的错误描述,我可以使用正文(对吗?)到目前为止还不错。但是,传输专有错误代码的方法是什么,该错误代码在描述错误时更详细(例如,"未能连接到数据库"或"数据库登录错误")?如果我把它和消息一起放到正文中,我必须在后面解析出来。这种东西有标准的标题吗?
4。如何进行身份验证
- 遵循REST原则的基于API密钥的身份验证是什么样子的?
- 在认证一个REST客户机时,是否有反对使用会话的优点,而不是明显违反了REST原则?:)。
我晚了几天才注意到这个问题,但我觉得我可以增加一些见解。我希望这有助于你安心的冒险。好的。
第一点:我理解对吗?好的。
你理解得对。这是一个RESTful体系结构的正确表示。你可能会发现维基百科中的下列矩阵在定义名词和动词方面非常有用:好的。
处理集合URI时,如:
get:列出集合的成员,连同他们的成员uri一起完成进一步的导航。例如,列出所有待售的汽车。好的。
放置:定义为"用另一个集合替换整个集合"。好的。
发布:在集合中创建一个新条目,集合在其中自动分配ID。创建的ID通常包含在此操作返回的数据中。好的。
删除:定义为"删除整个集合"。好的。
处理成员URI时,如:
get:检索以适当的mime类型表示的集合的寻址成员的表示形式。好的。
放置:更新集合的寻址成员或使用指定的ID创建它。好的。
post:将寻址成员视为一个集合,并创建一个新的从属成员。好的。
删除:删除集合的寻址成员。好的。
第二点:我需要更多的动词好的。
一般来说,当你认为你需要更多的动词时,它实际上可能意味着你的资源需要被重新识别。请记住,在REST中,您总是在一个资源或一组资源上执行操作。选择什么作为资源对于您的API定义非常重要。好的。
激活/停用登录:如果您正在创建一个新会话,那么您可能希望将"会话"视为资源。要创建一个新会话,请使用Postto
更改密码:这次资源是"用户"。你需要一个新的和旧的密码放在
My instinct would be to do a GET call
to a URL like
/api/users/1/activate_login Ok.
这违背了一个非常核心的REST原则:正确使用HTTP动词。任何GET请求都不应留下任何副作用。好的。
例如,GET请求不应在数据库上创建会话,不应返回具有新会话ID的cookie,也不应在服务器上留下任何残留物。get动词类似于数据库引擎中的select语句。记住,当使用相同的参数请求时,对带有get动词的任何请求的响应都应该是可缓存的,就像请求静态网页时一样。好的。
第3点:如何返回错误消息和代码好的。
将4xx或5xx HTTP状态代码视为错误类别。您可以详细说明正文中的错误。好的。
未能连接到数据库:/数据库登录不正确:通常,这些类型的错误应使用500错误。这是服务器端错误。客户没有做错什么。500个错误通常被认为是"可重试的"。也就是说,客户机可以重试相同的确切请求,并期望在服务器故障解决后成功。在正文中指定细节,以便客户能够为我们人类提供一些上下文。好的。
另一类错误是4xx系列,通常表示客户做错了什么。特别是,这类错误通常会向客户机表明,不需要按原样重试请求,因为它将继续永久失败。即,客户端在重试此请求之前需要更改一些内容。例如,"找不到资源"(HTTP 404)或"格式错误的请求"(HTTP 400)错误将属于此类。好的。
第4点:如何进行身份验证好的。
如第1点所指出的,您可能希望考虑创建一个会话,而不是对用户进行身份验证。将返回一个新的"会话ID",以及相应的HTTP状态代码(200:已授予访问权限或403:已拒绝访问)。好的。
然后,您将询问您的RESTful服务器:"您能为我获取此会话ID的资源吗?".好的。
没有经过身份验证的模式-REST是无状态的:您创建了一个会话,您要求服务器提供使用此会话ID作为参数的资源,然后在注销时删除或终止该会话。好的。好啊。
简单地说,这是完全逆向的。好的。
您不应该从应该使用的URL接近这个问题。一旦您确定了系统所需的资源、如何表示这些资源以及资源和应用程序状态之间的交互,URL将"免费"提供。好的。
引用罗伊·菲尔丁的话好的。
A REST API should spend almost all of
its descriptive effort in defining the
media type(s) used for representing
resources and driving application
state, or in defining extended
relation names and/or
hypertext-enabled mark-up for existing
standard media types. Any effort spent
describing what methods to use on what
URIs of interest should be entirely
defined within the scope of the
processing rules for a media type
(and, in most cases, already defined
by existing media types). [Failure
here implies that out-of-band
information is driving interaction
instead of hypertext.]Ok.
人们总是从URI开始,认为这是解决方案,然后他们倾向于忽略REST体系结构中的一个关键概念,特别是如上所述,"这里的失败意味着带外信息驱动交互而不是超文本。"好的。
老实说,很多人看到了一堆URI,一些人看到了,发布了,认为休息很容易。休息不容易。RPCoverHTTP很容易,通过HTTP有效负载来回移动代理的数据块很容易。然而,休息是远远不够的。休息是协议不可知论。HTTP非常流行,并且适合于REST系统。好的。
REST存在于媒体类型、定义以及应用程序如何通过超文本(有效链接)驱动这些资源可用的操作中。好的。
对于REST系统中的媒体类型有不同的视图。一些支持特定于应用程序的有效负载,而另一些则喜欢将现有媒体类型提升到适合应用程序的角色中。例如,一方面,您有特定的XML模式,这些模式是为您的应用程序设计的,而不是使用XHTML之类的东西作为您的表示,也许是通过微格式和其他机制。好的。
我认为,这两种方法都有它们的位置,XHTML在覆盖人驱动和机器驱动Web的场景中工作得很好,而前者,更具体的数据类型,我觉得更容易促进机器到机器的交互。我发现,商品格式的提升可能会使内容谈判变得困难。"application/xml+yourresource作为一种媒体类型比application/xhtml+xml更为具体,因为后者可以应用于许多有效负载,这些负载可能是或可能不是机器客户真正感兴趣的,也不能在不进行自省的情况下确定。好的。
然而,XHTML在人类网络中工作得很好(很明显),在人类网络中,浏览器和渲染非常重要。好的。
您的应用程序将指导您做出这些决定。好的。
设计一个REST系统的过程的一部分是发现系统中的第一类资源以及派生资源,以支持对主要资源的操作所必需的资源。一旦资源被发现,那么这些资源的表示,以及状态图在表示中通过超文本显示资源流,因为下一个挑战。好的。
回想一下,在超文本系统中,资源的每一种表示都结合了实际的资源表示以及资源可用的状态转换。将每个资源视为图中的一个节点,其中链接是将该节点保留到其他状态的线。这些链接不仅通知客户可以做什么,还通知客户需要做什么(因为一个好的链接结合了URI和所需的媒体类型)。好的。
例如,您可能有:好的。
1 2 | <link href="http://example.com/users" rel="users" type="application/xml+usercollection"/> <link href="http://example.com/users?search" rel="search" type="application/xml+usersearchcriteria"/> |
您的文档将讨论名为"users"的rel字段,以及"application/xml+youruser"的媒体类型。好的。
这些链接可能看起来是多余的,它们都在和同一个URI通信,差不多。但他们不是。好的。
这是因为对于"用户"关系,该链接指的是用户集合,您可以使用统一的界面来处理集合(获取并检索所有用户,删除并删除所有用户等)。好的。
如果您发布到这个URL,您将需要传递一个"application/xml+usercollection"文档,该文档可能只包含文档中的一个用户实例,因此您可以添加用户,或者不添加用户,一次添加几个。也许您的文档会建议您只需传递一个用户类型,而不是集合。好的。
您可以看到应用程序执行搜索所需的内容,如"搜索"链接和它的mediatype所定义的。搜索媒体类型的文档将告诉您这是如何操作的,以及作为结果的期望值。好的。
不过,这里的结论是,URI本身基本上并不重要。应用程序控制的是URI,而不是客户机。除了一些"入口点",您的客户机应该依赖应用程序提供的URI来完成其工作。好的。
客户机需要知道如何操作和解释媒体类型,但不太需要关心它的去向。好的。
在客户机眼中,这两个链接在语义上是相同的:好的。
1 2 | <link href="http://example.com/users?search" rel="search" type="application/xml+usersearchcriteria"/> <link href="http://example.com/AW163FH87SGV" rel="search" type="application/xml+usersearchcriteria"/> |
所以,关注你的资源。关注它们在应用程序中的状态转换以及如何最好地实现。好的。好啊。
回复1:到目前为止还不错。记住在"location:"头中返回新创建用户的URI,作为对post的响应的一部分,并返回"201 created"状态代码。
Re2:通过get激活是一个坏主意,在URI中包含动词是一种设计味道。您可能需要考虑在GET上返回表单。在Web应用程序中,这将是一个带有提交按钮的HTML表单;在API用例中,您可能希望返回一个包含要放入以激活帐户的URI的表示。当然,您也可以将这个URI包含在post-to/users的响应中。使用Put将确保您的请求是等幂的,也就是说,如果客户不确定是否成功,则可以安全地再次发送请求。一般来说,想想你能把动词转化成什么样的资源(比如"动词的名词化")。问问自己,你的具体行动与什么方法最接近。例如,更改密码->放置;停用->可能删除;添加信用->可能张贴或放置。通过在您的表示中包含它们,将客户机指向适当的URI。
Re 3。不要发明新的状态码,除非你相信它们是如此通用,它们值得全球标准化。尽量使用最合适的可用状态代码(请阅读RFC2616中的所有代码)。在响应正文中包含其他信息。如果您真的,真的确定要发明一个新的状态代码,请再想一想;如果您仍然这样认为,请确保至少选择正确的类别(1xx->OK,2xx->Informational,3xx->Redirection;4xx->Client Error,5xx->Server Error)。我有没有提到发明新的状态码是个坏主意?
Re 4。如果可能,请使用HTTP中内置的身份验证框架。看看Google在gdata中进行身份验证的方式。一般来说,不要将API密钥放在URI中。尝试避免会话以增强可伸缩性和支持缓存——如果对请求的响应因以前发生的事情而不同,则通常将自己绑定到特定的服务器进程实例。最好将会话状态转换为客户机状态(例如,使其成为后续请求的一部分),或者通过将其转换为(服务器)资源状态(即,为其提供自己的URI)使其明确化。
1。你对如何设计你的资源有正确的想法,imho。我什么都不会改变。
2。不要试图用更多的动词来扩展HTTP,而是考虑根据基本的HTTP方法和资源,可以将建议的动词简化为什么。例如,您可以设置诸如:
同样,要更改或设置密码,只需将
每当你需要添加一些东西(如信用证)时,就用
三。我强烈建议不要扩展基本的HTTP状态代码。如果找不到与您的情况完全匹配的,请选择最接近的一个,并将错误详细信息放在响应正文中。另外,记住HTTP头是可扩展的;您的应用程序可以定义您喜欢的所有自定义头。例如,我处理的一个应用程序可以在多种情况下返回
1 2 | HTTP/1.1 404 Not Found X-Status-Extended: 404.3 More Specific Error Here |
这样,像Web浏览器这样的HTTP客户机仍然知道如何处理常规404代码,更复杂的HTTP客户机可以选择查看
4。对于身份验证,如果可以,我建议使用HTTP身份验证。但是,如果使用基于cookie的认证对你来说更容易的话,那就没有什么错了。
休息基础
REST有一个统一的接口约束,它声明REST客户机必须依赖标准,而不是实际REST服务的特定于应用程序的细节,因此REST客户机不会被细微的更改破坏,而且它可能是可重用的。
所以REST客户机和REST服务之间有一个契约。如果您使用HTTP作为基础协议,那么以下标准是合同的一部分:
- HTTP 1.1
- 方法定义
- 状态代码定义
- 缓存控制头
- 接受和内容类型标题
- Auth-Head
- IRI(UTF8 URI)
- 身体(挑一)
- 注册的特定于应用程序的mime类型,例如maze+xml
- 供应商特定的mime类型,例如vnd.github+json
- 带的通用mime类型
- 特定于应用程序的RDF词汇,例如ld+json&hydra,schema.org
- 特定于应用程序的配置文件,例如hal+json&profile link param(我猜)
- 超链接
- 应该包含它们的内容(选择一个)
- 在链接头中发送
- 发送超媒体响应,例如HTML、Atom+XML、HAL+JSON、LD+JSON&Hydra等…
- 语义
- 使用IANA链接关系,可能是自定义链接关系
- 使用特定于应用程序的RDF词汇
- 应该包含它们的内容(选择一个)
REST有一个无状态约束,它声明REST服务和客户机之间的通信必须是无状态的。这意味着REST服务无法维护客户机状态,因此您不能拥有服务器端会话存储。您必须验证每个请求。例如,HTTP基本认证(HTTP标准的一部分)是可以的,因为它在每个请求中都发送用户名和密码。
回答你的问题是的,可以。
更重要的是,客户机不关心IRI结构,他们关心语义,因为他们遵循具有链接关系或链接数据(RDF)属性的链接。
关于IRIS唯一重要的是,一个IRI必须只识别一个资源。它允许单个资源(如用户)具有许多不同的虹膜。
这很简单,为什么我们使用像
你有更多的动词,比如Put,Patch,Options,甚至更多,但是你不需要更多的动词…你必须学会如何添加新的资源,而不是添加新的动词。
deactivate_login -> PUT /login/active false
change_password -> PUT /user/xy/password"newpass"
add_credit -> POST /credit/raise {details: {}}
(由于无状态约束,从REST的角度来看,登录没有意义。)
您的用户不关心问题存在的原因。他们只想知道是否有成功或错误,可能还有一条他们能理解的错误信息,例如:"抱歉,我们无法保存您的帖子。"等等……
HTTP状态头是您的标准头。我想其他的都应该在身体里。单个头不足以描述详细的多语言错误消息。
无状态约束(连同缓存和分层系统约束)确保服务能够很好地扩展。你当然不想在服务器上维护数百万个会话,当你可以在客户机上做同样的事情时…
如果用户使用主客户机授予对第三方客户机的访问权限,则第三方客户机将获得访问令牌。之后,第三方客户端将随每个请求一起发送访问令牌。还有更复杂的解决方案,例如,您可以签署每个请求等。有关详细信息,请查看OAuth手册。
相关文献
- 体系结构风格与基于网络的软件体系结构设计罗伊·托马斯·菲尔丁(REST作者)的论文2000年,加州大学欧文分校
- 第三代Web API-弥合REST和链接数据之间的鸿沟Markus Lanthaler的论文(JSON-LD的合著者和Hydra的作者)2014年,奥地利格拉茨理工大学
对于您提到的示例,我将使用以下内容:
激活登录
停用登录
转换密码
附加信用证
对于错误,您将以收到请求的格式返回正文中的错误,因此如果您收到:
您将以XML形式发送响应,JSON等的情况也是如此……
对于身份验证,您应该使用HTTP身份验证。
冗长的,但copied from the method(HTTP 1.1规范www.w3.org protocols http://///rfc2616-sec9.html rfc2616>
9.3 get
get the method均值信息(无论在形态retrieve of the by the is an entity)identified请求的URI。if the request to a URI日期"是指生产过程,它的生产日期,which is the returned as the entity将好茶源响应和not the text of the process,除非我能to be the text of the过程输出。>
the method of the change to get语义的"get the request message的条件包括"if if修饰-安-如果- unmodified -因为,因为,如果匹配,如果不匹配,或者如果一场范围标头。在get that the requests实体法条件下描述的程序办理好转移by the only the header field特殊条件(S)。intended method is the conditional get usage模式减少不必要的民族网络高速缓存allowing entities refreshed requests to be without requiring举行日期前多已转移模式的客户。>
the method of the change to get语义的"get the request message if部分"包括在头场范围。get requests,只在部分单位转移part of the section中描述的程序办理好,as 14.35%。intended method is to get the部分减少不必要的民族网络allowing usage by电离retrieved entities to be completed already控制模式转换没有日期的客户。>
在request is to get the response and only if it if可缓存的HTTP缓存满足要求for the 13段中描述的程序办理。>
see section for 15.1.3 forms when used for安全的考虑。>
9.5后
the method is used to后的that the request origin服务器accept the entity封闭在the request of the New隶属作为资源identified by the the request请求URI在网上。is to allow后均匀设计法,盖在下面的功能:>
1 2 3 4 5 6 | - Annotation of existing resources; - Posting a message to a bulletin board, newsgroup, mailing list, or similar group of articles; - Providing a block of data, such as the result of submitting a form, to a data-handling process; - Extending a database through an append operation. |
the actual Performed by the function by the method is后矛盾和is on the usually依赖服务器的请求的URI。posted entity to that is the URI在次级路,the same to a目录文件是隶属新闻article is含EN,EN组隶属to a to which is is posted隶属或记录到数据库。>
the action Performed by the method not result中后会在identified by that can be资源的URI。在这房子,或者200 204((好)或不适当的响应内容)is the whether or not depending在线状态,响应实体,包括安茶Describes the result。>
has been created a资源if on the origin服务器201(should be created the response and which contain an entity)Describes the of the request to"是指地位和新的资源,和标题的位置(第二区)。>
对此方法的响应不可缓存,除非响应包含适当的缓存控制或Expires头字段。但是,303(请参阅其他)响应可用于指示用户代理检索可缓存资源。好的。
邮政请求必须遵守第8.2节中规定的信息传输要求。好的。
安全考虑见第15.1.3节。好的。9.6放
Put方法请求将封闭实体存储在提供的请求URI下。如果请求URI引用了一个已经存在的资源,那么所附的实体应该被视为源服务器上的一个修改版本。如果请求URI未指向现有资源,并且请求用户代理能够将该URI定义为新资源,则源服务器可以使用该URI创建资源。如果创建了新资源,源服务器必须通过201(已创建)响应通知用户代理。如果修改了现有资源,则应发送200(确定)或204(无内容)响应代码以指示成功完成请求。如果无法使用请求URI创建或修改资源,则应给出反映问题性质的适当错误响应。实体的接收者不能忽略它不理解或实现的任何内容**(例如,内容范围)头,并且在这种情况下必须返回501(未实现)响应。好的。
如果请求通过缓存,而请求URI标识一个或多个当前缓存的实体,则这些条目应视为过时条目。对此方法的响应不可缓存。好的。
POST和PUT请求之间的根本区别体现在请求URI的不同含义中。POST请求中的URI标识将处理封闭实体的资源。该资源可能是一个接受数据的进程,一个到其他协议的网关,或者一个接受注释的独立实体。相反,put请求中的uri标识了与请求一起包含的实体——用户代理知道要使用什么uri,服务器不能尝试将请求应用到其他资源。如果服务器希望将请求应用到其他URI,好的。
它必须发送301(永久移动)响应;然后用户代理可以自己决定是否重定向请求。好的。
单个资源可以由许多不同的URI标识。例如,一篇文章可能有一个用于标识"当前版本"的URI,它与标识每个特定版本的URI是分开的。在这种情况下,对常规URI的Put请求可能会导致源服务器定义其他几个URI。好的。
HTTP/1.1没有定义Put方法如何影响源服务器的状态。好的。
Put请求必须遵守第8.2节中规定的消息传输要求。好的。
除非对特定实体头另有指定,否则Put请求中的实体头应应用于Put创建或修改的资源。好的。9.7删除
delete方法请求源服务器删除由请求URI标识的资源。此方法可能会被源服务器上的人工干预(或其他方法)覆盖。即使从源服务器返回的状态代码指示操作已成功完成,也无法保证客户端已执行该操作。但是,除非在给出响应时,服务器打算删除资源或将其移动到不可访问的位置,否则服务器不应指示成功。好的。
如果响应包括描述状态的实体,则成功响应应为200(好),如果操作尚未制定,则为202(已接受),如果操作已制定,但响应不包括实体,则为204(无内容)。好的。
如果请求通过缓存,而请求URI标识一个或多个当前缓存的实体,则这些条目应视为过时条目。对此方法的响应不可缓存。好的。好啊。
我建议(作为第一步),
1 | /api/users when called with PUT, creates user record |
我觉得不舒服。第一部分的其余部分。不过,动词用法)看起来很合乎逻辑。
关于REST返回代码:混合HTTP协议代码和REST结果是错误的。
然而,我看到许多实现混合了它们,许多开发人员可能不同意我的观点。
HTTP返回代码与
例如,当您呼叫/users/时返回"404 Not Found",因为这可能意味着:
- URI错误(HTTP)
- 找不到用户(其余)
"403禁止/拒绝访问"可能意味着:
- 需要特别许可。浏览器可以通过询问用户/密码来处理。(HTTP)
- 服务器上配置的访问权限错误。(HTTP)
- 您需要经过身份验证(REST)
列表可能会继续出现"500服务器错误"(apache/nginx HTTP抛出错误或rest中的业务约束错误)或其他HTTP错误等。
从代码中,很难理解失败的原因是什么,HTTP(传输)失败还是REST(逻辑)失败。
如果实际执行HTTP请求成功,则无论是否找到记录,它都应始终返回200个代码。因为找到了URI资源,并且由HTTP服务器处理。是的,它可能返回空的集合。是否可以接收一个带有200的空网页作为HTTP结果,对吗?
相反,您可以返回200个HTTP代码和一个带有空数组/对象的JSON,或者使用bool result/success标志来通知已执行的操作状态。
此外,一些互联网提供商可能会截获您的请求并返回404 HTTP代码。这并不意味着找不到您的数据,但它在传输级别是错误的。
来自维基:
In July 2004, the UK telecom provider BT Group deployed the Cleanfeed
content blocking system, which returns a 404 error to any request for
content identified as potentially illegal by the Internet Watch
Foundation. Other ISPs return a HTTP 403"forbidden" error in the same
circumstances. The practice of employing fake 404 errors as a means to
conceal censorship has also been reported in Thailand and Tunisia. In
Tunisia, where censorship was severe before the 2011 revolution,
people became aware of the nature of the fake 404 errors and created
an imaginary character named"Ammar 404" who represents"the invisible
censor".