我正在寻找一种方法来包装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客户机时使用会话是否有强烈的反对之处?:)(这里只是半开玩笑,基于会话的身份验证在我现有的基础设施中可以很好地发挥作用。)
几天后我才注意到这个问题,但我觉得我可以补充一些见解。我希望这能对你的平安创业有所帮助。
第一点:我理解得对吗?
你理解得对。这是RESTful体系结构的正确表示。你可能会发现维基百科上的以下矩阵对定义名词和动词很有帮助:
当处理类似于http://example.com/resources/的集合URI时
GET:列出集合的成员,包括它们的成员uri,以便进一步导航。例如,列出所有待售的汽车。
PUT:意思是“用另一个集合替换整个集合”。
POST:在集合中创建一个新条目,其中ID由集合自动分配。创建的ID通常包含在此操作返回的数据中。
DELETE:定义为“删除整个集合”。
当处理像http://example.com/resources/7HOU57Y这样的成员URI时
GET:检索以适当MIME类型表示的已寻址集合成员的表示形式。
PUT:更新集合的已寻址成员或使用指定的ID创建它。
POST:将已寻址的成员作为其本身的集合处理,并创建它的新从属。
DELETE:删除集合的寻址成员。
第二点:我需要更多的动词
一般来说,当您认为需要更多动词时,实际上可能意味着需要重新标识资源。记住,在REST中,您总是作用于一个资源或资源的集合。选择什么作为资源对API定义非常重要。
激活/取消激活登录:如果您正在创建一个新会话,那么您可能希望将“会话”视为资源。要创建一个新会话,使用POST到http://example.com/sessions/,并在主体中使用凭据。要使其过期,请使用PUT或DELETE(可能取决于您是否打算保留会话历史)到http://example.com/sessions/SESSION_ID。
Change Password:这次的资源是“the user”。您需要一个到http://example.com/users/USER_ID的PUT,并将旧密码和新密码放在主体中。您正在对“用户”资源进行操作,更改密码只是一个更新请求。它与关系数据库中的UPDATE语句非常相似。
我的直觉是执行GET调用
到一个URL
/ api /用户/ 1 / activate_login
这违背了REST的核心原则:正确使用HTTP动词。任何GET请求都不应该留下任何副作用。
例如,GET请求永远不应该在数据库上创建会话,返回带有新会话ID的cookie,或者在服务器上留下任何残留物。GET动词类似于数据库引擎中的SELECT语句。请记住,当使用相同的参数请求时,对带有GET谓词的任何请求的响应都应该是可缓存的,就像请求静态web页面一样。
要点3:如何返回错误消息和代码
将4xx或5xx HTTP状态代码视为错误类别。您可以在正文中详细说明错误。
连接数据库失败:/数据库登录不正确:一般情况下,您应该使用500错误来处理这些类型的错误。这是一个服务器端错误。客户没有做错什么。500个错误通常被认为是“可重试的”。也就是说,客户端可以重试相同的请求,并期望它在服务器的问题解决后成功。在正文中指定细节,以便客户端能够为我们人类提供一些上下文。
另一类错误是4xx族,它通常表示客户端做错了什么。特别是,这类错误通常向客户端表明,不需要按原样重试请求,因为它将继续永久地失败。也就是说,客户端在重新尝试这个请求之前需要修改一些东西。例如,“资源未找到”(HTTP 404)或“格式错误请求”(HTTP 400)错误就属于这一类。
要点4:如何进行身份验证
正如第1点所指出的,您可能希望考虑创建一个会话,而不是验证用户。您将返回一个新的“会话ID”,以及相应的HTTP状态代码(200:允许访问或403:拒绝访问)。
然后,您将询问RESTful服务器:“您可以为我获取这个会话ID的资源吗?”
没有身份验证模式——REST是无状态的:你创建一个会话,你要求服务器使用这个会话ID作为参数给你资源,在注销时你删除或过期会话。
回复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".
1. 恕我直言,你对如何设计你的资源有正确的想法。我什么都不会改变。
2. 不要试图用更多的动词来扩展HTTP,而是考虑您所提议的动词可以根据基本的HTTP方法和资源减少到什么程度。例如,不使用activate_login谓词,可以设置如下资源:/api/users/1/login/active,这是一个简单的布尔值。要激活登录,只需在那里放一个文档,说'true'或1或其他什么。要停用,请在那里放置一个为空或显示为0或false的文档。
类似地,要更改或设置密码,只需对/api/users/1/password执行put。
无论何时你需要添加一些东西(比如信用),都要考虑post。例如,你可以对类似/api/users/1/credits这样的资源执行POST操作,其主体包含要添加的积分数。同一资源上的PUT操作可以用于覆盖该值而不是添加。主体中带有负数的POST操作将进行减法,以此类推。
3. I'd strongly advise against extending the basic HTTP status codes. If you can't find one that matches your situation exactly, pick the closest one and put the error details in the response body. Also, remember that HTTP headers are extensible; your application can define all the custom headers that you like. One application that I worked on, for example, could return a 404 Not Found under multiple circumstances. Rather than making the client parse the response body for the reason, we just added a new header, X-Status-Extended, which contained our proprietary status code extensions. So you might see a response like:
HTTP/1.1 404 Not Found
X-Status-Extended: 404.3 More Specific Error Here
这样,像web浏览器这样的HTTP客户端仍然知道如何处理常规的404代码,而更复杂的HTTP客户端可以选择查看X-Status-Extended报头以获得更具体的信息。
4. 对于身份验证,如果可以的话,我建议使用HTTP身份验证。但恕我直言,如果对你来说更容易的话,使用基于cookie的身份验证并没有什么错。