我正在寻找一种方法来包装api的默认功能在我的基于php的web应用程序,数据库和cms。

我环顾四周,发现了几个“骨架”框架。除了我的问题的答案之外,还有一个我喜欢的REST框架Tonic,因为它非常轻量级。

我最喜欢REST,因为它简单,并希望基于它创建一个API体系结构。我正在努力理解基本原理,但还没有完全理解。因此,有一些问题。

1. 我理解得对吗?

假设我有一个资源“用户”。我可以像这样设置一些uri:

/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. 我需要更多的动词

创建、更新和删除在理论上可能已经足够了,但实际上我需要更多的动词。我知道这些东西可以嵌入到更新请求中,但它们是特定的操作,可以有特定的返回代码,我不想把它们都扔到一个操作中。

在用户示例中想到的一些是:

activate_login
deactivate_login
change_password
add_credit

我该如何表达像RESTful URL体系结构那样的动作呢?

我的直觉是对URL进行GET调用

/api/users/1/activate_login 

并等待状态码返回。

但是,这偏离了使用HTTP谓词的想法。你怎么看?

3.如何返回错误消息和代码

A great part of REST's beauty stems from its use of standard HTTP methods. On an error, I emit a header with a 3xx,4xx or 5xx error status code. For a detailed error description, I can use the body (right?). So far so good. But what would be the way to transmit a proprietary error code that is more detailed in describing what went wrong (e.g. "failed to connect to database", or "database login wrong")? If I put it into the body along with the message, I have to parse it out afterwards. Is there a standard header for this kind of thing?

4. 如何进行身份验证

遵循REST原则的基于API密钥的身份验证是什么样子的? 除了公然违反REST原则之外,在验证REST客户机时使用会话是否有强烈的反对之处?:)(这里只是半开玩笑,基于会话的身份验证在我现有的基础设施中可以很好地发挥作用。)


当前回答

回复1:到目前为止看起来还不错。记住在“Location:”报头中返回新创建用户的URI,作为对POST的响应的一部分,同时返回“201 created”状态码。

re 2: Activation via GET is a bad idea, and including the verb in the URI is a design smell. You might want to consider returning a form on a GET. In a Web app, this would be an HTML form with a submit button; in the API use case, you might want to return a representation that contains a URI to PUT to to activate the account. Of course you can include this URI in the response on POST to /users, too. Using PUT will ensure your request is idempotent, i.e. it can safely be sent again if the client isn't sure about success. In general, think about what resources you can turn your verbs into (sort of "nounification of verbs"). Ask yourself what method your specific action is most closely aligned with. E.g. change_password -> PUT; deactivate -> probably DELETE; add_credit -> possibly POST or PUT. Point the client to the appropriate URIs by including them in your representations.

re 3. Don't invent new status codes, unless you believe they're so generic they merit being standardized globally. Try hard to use the most appropriate status code available (read about all of them in RFC 2616). Include additional information in the response body. If you really, really are sure you want to invent a new status code, think again; if you still believe so, make sure to at least pick the right category (1xx -> OK, 2xx -> informational, 3xx -> redirection; 4xx-> client error, 5xx -> server error). Did I mention that inventing new status codes is a bad idea?

re 4. If in any way possible, use the authentication framework built into HTTP. Check out the way Google does authentication in GData. In general, don't put API keys in your URIs. Try to avoid sessions to enhance scalability and support caching - if the response to a request differs because of something that has happened before, you've usually tied yourself to a specific server process instance. It's much better to turn session state into either client state (e.g. make it part of subsequent requests) or make it explicit by turning it into (server) resource state, i.e. give it its own URI.

其他回答

关于REST返回码:将HTTP协议代码和REST结果混淆是错误的。

然而,我看到许多实现混合了它们,许多开发人员可能不同意我的观点。

HTTP返回码与HTTP请求本身相关。REST调用是使用超文本传输协议(Hypertext Transfer Protocol)请求完成的,它的工作级别比调用的REST方法本身要低。REST是一种概念/方法,其输出是业务/逻辑结果,而HTTP结果代码是传输结果。

例如,当你调用/users/时返回“404 Not found”是令人困惑的,因为它可能意味着:

URI错误(HTTP) 没有找到用户(REST)

“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".

简单地说,你完全是在逆向操作。

你不应该从你应该使用的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.]

人们总是从uri开始,并认为这是解决方案,然后他们往往会忽略REST体系结构中的一个关键概念,特别是上面引用的,“这里的失败意味着驱动交互的是带外信息,而不是超文本。”

老实说,很多人看到一堆uri和一些get、put和post,认为REST很简单。REST并不容易。基于HTTP的RPC很容易,通过HTTP有效负载来回移动数据块也很容易。然而,REST不止于此。REST是协议不可知的。HTTP非常流行,适用于REST系统。

REST存在于媒体类型、它们的定义以及应用程序如何通过超文本(实际上是链接)驱动对这些资源可用的操作。

对于REST系统中的媒体类型有不同的看法。一些人喜欢应用程序特定的有效负载,而另一些人喜欢将现有的媒体类型提升到适合应用程序的角色。例如,一方面,您拥有适合应用程序的特定XML模式,而不是使用XHTML之类的东西作为表示(可能是通过微格式或其他机制)。

Both approaches have their place, I think, the XHTML working very well in scenarios that overlap both the human driven and machine driven web, whereas the former, more specific data types I feel better facilitate machine to machine interactions. I find the uplifting of commodity formats can make content negotiation potentially difficult. "application/xml+yourresource" is much more specific as a media type than "application/xhtml+xml", as the latter can apply to many payloads which may or may not be something a machine client is actually interested in, nor can it determine without introspection.

然而,XHTML在web浏览器和呈现非常重要的人类网络中工作得很好(显然)。

您的应用程序将指导您做出此类决定。

REST系统设计过程的一部分是发现系统中的第一类资源,以及派生的支持资源,支持对主要资源的操作所必需的支持资源。一旦发现了资源,那么这些资源的表示以及通过表示中的超文本显示资源流的状态图将成为下一个挑战。

回想一下,在超文本系统中,资源的每种表示都将实际资源表示与资源可用的状态转换结合在一起。将每个资源视为图中的一个节点,链接是将该节点留给其他状态的线。这些链接不仅告诉客户可以做什么,还告诉他们需要做什么(因为一个好的链接结合了URI和所需的媒体类型)。

例如,你可能有:

<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"/>

您的文档将讨论rel字段名为“users”,媒体类型为“application/xml+youruser”。

这些链接看起来可能是多余的,它们几乎都在与相同的URI通信。但事实并非如此。

这是因为对于“用户”关系,该链接谈论的是用户的集合,您可以使用统一接口来处理集合(GET检索所有用户,DELETE删除所有用户,等等)。

如果您POST到这个URL,您将需要传递一个“application/xml+usercollection”文档,该文档可能只包含一个用户实例,因此您可以添加用户,或者一次添加几个用户。也许您的文档会建议您可以简单地传递单个用户类型,而不是集合。

您可以看到应用程序执行搜索所需的内容(由“search”链接定义)和它的mediatype。搜索媒体类型的文档将告诉您它的行为,以及预期的结果。

不过,这里的要点是uri本身基本上不重要。应用程序控制uri,而不是客户机。除了几个“入口点”,你的客户端应该依赖应用程序提供的uri来工作。

客户端需要知道如何操作和解释媒体类型,但不太需要关心它的去向。

在客户的眼中,这两个链接在语义上是相同的:

<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"/>

所以,专注于你的资源。关注它们在应用程序中的状态转换,以及如何最好地实现状态转换。

对于你说的例子,我将使用以下:

activate_login

POST /用户/ 1 /激活

deactivate_login

删除/用户/ 1 /激活

change_password

PUT /口令(假设用户已经过身份验证)

add_credit

POST /credits(假设用户已经过身份验证)

对于错误,你会以你得到请求的格式在正文中返回错误,所以如果你收到:

删除/用户/ 1. xml

你可以用XML发送回响应,JSON等也是如此……

对于身份验证,应该使用http身份验证。

其他基本知识

REST有一个统一的接口约束,它规定REST客户端必须依赖于标准,而不是实际REST服务的特定于应用程序的细节,因此REST客户端不会因微小的更改而中断,而且可能是可重用的。

因此,REST客户机和REST服务之间存在一个契约。如果您使用HTTP作为底层协议,那么以下标准是合同的一部分:

HTTP 1.1 method definitions status code definitions cache control headers accept and content-type headers auth headers IRI (utf8 URI) body (pick one) registered application specific MIME type, e.g. maze+xml vendor specific MIME type, e.g. vnd.github+json generic MIME type with application specific RDF vocab, e.g. ld+json & hydra, schema.org application specific profile, e.g. hal+json & profile link param (I guess) hyperlinks what should contain them (pick one) sending in link headers sending in a hypermedia response, e.g. html, atom+xml, hal+json, ld+json&hydra, etc... semantics use IANA link relations and probably custom link relations use an application specific RDF vocab

REST有一个无状态约束,它声明REST服务和客户机之间的通信必须是无状态的。这意味着REST服务不能维护客户端状态,因此您不能拥有服务器端会话存储。你必须验证每一个请求。例如,HTTP基本认证(HTTP标准的一部分)是可以的,因为它会在每个请求中发送用户名和密码。

来回答你的问题

是的,它可以是。

值得一提的是,客户端并不关心IRI结构,他们关心的是语义,因为他们遵循具有链接关系或链接数据(RDF)属性的链接。

关于IRI唯一重要的一点是,单个IRI必须只能标识单个资源。允许单个资源(如用户)拥有许多不同的IRIs。

我们使用/users/123/password这样漂亮的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状态标头是标准标头。其他的都应该在身体里。例如,单个报头不足以描述详细的多语言错误消息。

The stateless constraint (along with the cache and layered system constraints) ensures that the service scales well. You surely don't wan't to maintain millions of sessions on the server, when you can do the same on the clients... The 3rd party client gets an access token if the user grants access to it using the main client. After that the 3rd party client sends the access token with every request. There are more complicated solutions, for example you can sign every single request, etc. For further details check the OAuth manual.

相关文献

架构风格与基于网络的软件架构设计 Roy Thomas Fielding博士论文(REST作者) 2000年,加州大学欧文分校 第三代Web api——弥合REST和关联数据之间的差距 Markus Lanthaler博士论文(JSON-LD合著者,Hydra作者) 2014年,奥地利格拉茨理工大学

我建议(作为第一步)PUT应该只用于更新现有实体。POST应该用于创建新的。即。

/api/users     when called with PUT, creates user record

我觉得不对劲。然而,第一部分的其余部分(重新使用动词)看起来是合乎逻辑的。