我正在构建一个网站,并且还希望构建一个REST Web服务来访问许多相同的功能(使用谷歌应用程序引擎和弹簧mvc3),我不确定如何集成/分离这两个部分的最佳实践应该.
例如,如果我想查看资源,我可以在表单中提供一个url:
{resourcetype}\{resourceid}
可以在视图中重定向对此URL的GET请求,该视图在客户端基于HTML /浏览器时生成网页.Spring(从我读到的 - 尚未尝试过)能够使用相同的资源URL来提供视图,该视图根据内容类型返回HTML/Xml/JSON.这一切看起来都很棒.
对URL的POST请求在REST中创建新资源应该返回201 CREATED(或者我读过)以及创建的资源的URL,这似乎对Api来说很好,但是看起来与预期的有点不同.一个网页(您可能会将其重定向到显示您创建的资源的页面,或者重定向到一个页面,表明它已成功创建或类似).我应该通过在包含创建资源的表单的不同URL上提供页面来处理此问题,然后通过ajax提交到Api URL并获取响应并重定向到响应中包含的资源URL.
这种模式似乎可以工作(也应该适用于DELETE),但这是一个好方法还是我最好保持REST Api URL和网站URL分开?这似乎可以引入一些重复和额外的工作.但是,拥有一个URL可能意味着您依赖于HTML 5支持浏览器的客户端上的javascript.
我建议你把它们分开.通过这样做,您可以获得多种好处.
首先,您将自己的网址与API网址分离,以便每个网址都可以独立更改.例如,您可能需要向API发布向后不兼容的更改,在这种情况下,您可以创建/ v2 /目录.同时,您可能需要网站上的/ about页面,但不需要一个API.
通过使用不同的URL,您可以简化实施.现在,每个方法都不必确定它是否面向JSON/XML或HTML.即使你有像Spring这样繁重的框架,这也是如此.对于当前用户,您仍然为网站做了额外的事情.
它还消除了一整类错误.例如,用户在浏览网站时不会获得JSON或XML输出 - 即使他们具有自定义浏览器匿名设置.
您可以轻松地分离逻辑以进行身份验证.有了网站,您需要一个登录页面和cookie.使用API,这些不是必需的,但是额外的身份验证标头(例如,HMAC + sha256签名).
最后,通过将站点与API分离,您可以满足不同的扩展需求.如果你的API受到了严重打击而网站没有受到重创,你可以在API上投入更多硬件,同时保持网站所需的最低限度.
更新: 为了澄清,我建议你不要两次编码.有两种不同的方法来查看此消除重复.
首先,在MVC的说法中,您对该模型有一个模型和两个不同的视图.这是MVC的重点,因此视图和模型不会捆绑在一起.获取特定资源的代码在两个客户端中都是相同的,因此您可以编写模型,以便只有一行代码从数据库或其来源获取该资源.简而言之,您的模型是一个易于使用的库,有两个客户端.
另一种看待它的方式是您的网站是您公共REST API的第一个客户端; Web服务器实际上调用您的RESTful API来获取它的信息.这是整个吃自己的狗粮原则.
我不同意Michael的回答并将其作为我自己的基础:
要"将您的网址与您的网址分离,以便每个网址都可以独立更改." 是面对REST吐.酷URI不会改变.不要担心自己更改网址.不要使用URI对API 进行版本控制.REST使用链接来支持OCP - 在以前版本的API上运行的客户端应该很乐意遵循其上线时存在的链接,不知道您为增强API而添加的新链接.
如果您坚持对API进行版本控制,我会要求您使用媒体类型而不是URI.
接下来,"您简化了实现.现在,每个方法都不必确定它是否面向JSON/XML或HTML."
如果你这样做,你做错了.来自泽西岛,无论我是否生成HTML,XML或JSON,我都会从每个方法返回相同的该死的对象.这是一个由编组人员负责的完全交叉的问题.我使用VelocityMessageBodyWriter发出围绕我的REST表示的HTML模板.
我系统中的每个资源都遵循相同的基本行为:
class FooResource extends Resource { @GET public FooRepresentation get() { return new FooRepresentation(); } @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public Response postForm(MulivaluedMapform) { return post(buildRepresentationFromForm(form)); } @POST @Consumes(MediaType.APPLICATION_XML) public Resopnse post(FooRepresentation representation) { // return ok, see other, whatever } }
GET方法可能需要构建到其他资源的链接.这些由REST API的使用者以及HTML模板使用.表单可能必须POST,我使用一些特定的链接(由Relation定义)作为"action"属性).
用户浏览器和用户机器之间的系统路径可能不同,但这不是实现的分离 - 它是 REST!状态转移发生在它需要的方式,如何定义它.用户(在浏览器中)如何继续.
"最后,通过将网站与API分离,您可以满足不同的扩展需求.如果您的API受到重创而不是网站,您可以在API上投入更多硬件,同时保持网站所需的最低限度." - 你的缩放不应该取决于谁在这里使用什么.很可能你在幕后做了一些比仅提供HTML更强烈的工作.通过使用相同的实现,缩放更容易.API本身(XML和域对象之间的编组和返回)不会超出业务逻辑和处理,数据库等.
最后,通过保持它们相同,可以更容易地考虑整个系统.实际上,从HTML开始.定义关系.如果您很难在HTML锚点和表单中表达特定的操作或用户故事,那么您可能会偏离REST.
请记住,您将事物表达为与其他事物的链接(特定关系).这些URI甚至可能会有所不同,具体取决于您是在生成XML还是HTML - HTML页面可能会POST到URI某些/ uri/a而API可能会POST到某些/ uri/b - 这是无关紧要的并且关注实际的内容URI内容是POX和RPC的黑暗路径
另一个漂亮的功能是,如果你这样做,你不依赖于JavaScript.您已经将系统定义为使用基本HTML,并且可以在JavaScript可用时"翻转".然后你真的正在使用你的"API"(我畏缩地将它们称为不同的东西,但我也试图将我的回答与你的措辞联系起来)
**我将添加一个最终评论,在生成HTML时,我使用303而不是201以便于POST-then-GET.如果JS已启用,那么您实际上是在谈论XML(或JSON),而您又回到了201.
为了支持迈克尔,与道格相反,你应该将它们分开.
事实证明,浏览器的基本形式并不是一个特别好的REST客户端.由于缺乏对HTTP的完全支持,糟糕的身份验证支持,以及对媒体类型的弱支持,浏览器实际上非常有限.如果你只是简单地使用内容,并且内容恰好是HTML,那么浏览器就可以了,但是超出这个范围并且API会受到浏览器不良支持的影响.
JavaScript可以提高浏览器的功能,并使其成为更好的REST公民,但是在浏览器中很少有东西比静态HTML页面更好.便携,高性能,可扩展到不同设备,具有一些CSS乐趣.每个人都喜欢静态页面的快照,特别是那些没有托管数量众多的图片以及其他慢速提供商的图片.单击,BANG,快速显示的快速滚动页面.
由于浏览器是一个悲伤的公民,因此您不应将API限制为其弱功能.通过分离它们,您可以在HTML + JS,Flash或Java,Obj-C for iOS或Android等中编写一个漂亮,丰富,动画,有弹性,令人兴奋的界面.
您还可以在PHP中编写一个漂亮的前端,托管在您的服务器上,并将结果推送到浏览器客户端.PHP应用程序根本不必是REST,它可以只是一个通用的Web应用程序,在域中工作和Web应用程序的约束(有状态会话,非语义标记等等).浏览器与PHP对话,PHP与您的REST服务对话.PHP应用程序允许您将浏览器的需求与REST服务的语义分开.
即使使用纯HTML,您也可以编写更多RESTful HTML应用程序.它们只是人们不喜欢使用的非常糟糕的应用程序.
显然,通用Web应用程序和REST服务之间存在很多可能的重叠,但重叠不是平等,而是它们是不同的.HTTP!= REST,使用HTTP并不意味着您正在使用REST.HTTP非常适合REST应用程序,但您当然可以以非RESTful方式使用HTTP.人们整天都这样做.
因此,如果您想将REST用作服务层,那么就这样做.为REST而利用REST,并构建您的服务.然后,开始处理利用该服务的客户端和接口.不要让初始客户端选择为REST服务本身添加颜色.专注于功能的使用案例,然后围绕它建立您的客户.
随着每个组件的需求发生变化,它们可以在必要时与彼此一致或彼此分开地增长.您无需为了浏览器所需的更改而惩罚移动应用程序,反之亦然.让每件作品都是他们自己的主人.
附加物:
山姆 -
提供混合方法没有错,其中一些请求由REST服务层直接提供,而其他请求则通过服务器端代理处理.只要语义相同,它就不重要了.您的REST服务当然不关心.但是,如果REST服务返回特定于"原始"REST服务而非混合服务的链接rels,则可能会出现问题.现在你有一个翻译表示法等问题.我的基本观点是不要让浏览器限制摇晃你的REST API,你可以使用一个单独的外观并让浏览器影响它.
这是一个逻辑上的分离,无论是否表现在URL模式中,我都没有意见.这更像是一个开发/维护/部署调用.我发现可以在物理上表现出来的逻辑分离在清晰度和理解方面有一些好处,但那就是我.
道格 -
原始HTML用户体验非常糟糕.如果不是这样,那么整个行业就不会让浏览器用户应用程序体验变得非常糟糕.当然它可以是功能性的,并且使用HTML是REST应用程序的一种出色的媒体类型,因为浏览器的工具和使得使用界面,查看工件,在可能的情况下与服务交互更容易.但是,您不是围绕调试器设计服务API,而原始浏览器是完全利用HTTP的不完整工具.作为JS通过XHR的主机,它变得更有能力,但现在我们正在谈论"富客户端",而不仅仅是浏览器中的直接HTML.
虽然你可以为delete等制作服务POST外观,但在你的例子中,你这样做的唯一原因是因为浏览器的限制,而不是为了API本身.如果它有任何问题,那么它就会混乱并使API复杂化."不止一种方法."
现在,显然你可以通过POST隧道传输一切,只是因为你想通过POST隧道传输一切.但是通过以这种方式隐藏事物,您可以绕过协议的其他方面.如果将POST foo/1发送到/ deleteFoos,您将无法利用缓存之类的东西.foo/1上的DELETE会使看到该操作的任何缓存无效,但POST会直接滑过,留下旧的,现在已删除的资源.
因此,即使本机浏览器选择不使用它们,也有理由在协议中存在除POST和GET之外的其他动词.