将REST JSON API服务器和客户端分离?

372

我即将从零开始创建一堆Web应用程序。 (请参阅http://50pop.com/code进行概述。)我希望它们可以从许多不同的客户端访问:前端网站、智能手机应用程序、后端Web服务等。因此,我确实想要每个应用程序都有JSON REST API。

此外,我更喜欢在后端工作,所以我幻想着自己专注于API,然后聘请其他人制作前端UI,无论是网站、iPhone、Android还是其他应用程序。

请帮助我决定应采取哪种方法:

在Rails中一起构建

创建一个非常标准的Rails Web应用程序。在控制器中,使用respond_with开关,提供JSON或HTML服务。然后,JSON响应成为我的API。

优点:有很多先例。伟大的标准和许多这样做的示例。

缺点:不一定希望API与Web应用程序相同。不喜欢if/then respond_with开关方法。混合了两个非常不同的东西(UI + API)。

REST服务器+JavaScript密集客户端

创建一个仅JSON的REST API服务器。使用Backbone或Ember.js进行客户端JavaScript,直接访问API,在浏览器中显示模板。

优点:我喜欢API和客户端的分离。聪明人说这是正确的方式。理论上很棒。看起来很前沿和令人兴奋。

缺点:先例不多。没有做好这个的很多示例。公共示例(twitter.com)感觉迟缓,甚至正在切换到其他方法。

REST服务器+服务器端HTML客户端

创建一个仅JSON的REST API服务器。制作一个基本的HTML网站客户端,只访问REST API。客户端JavaScript较少。

优点: 我喜欢API和客户端的分离。但提供普通的HTML5相当简单,不需要客户端的大量计算。

缺点: 缺乏先例。没有很多成功的实例可供借鉴。框架对此支持不够好。不确定如何着手处理。

尤其是希望从经验上获得建议,而不仅仅是理论方面的。


52
通常情况下,我们更喜欢把那些探讨概念和想法的白板问题放到http://programmers.stackexchange.com上,而在Stack Overflow上发问时,99%的情况下应该包含实际源代码。但这是一个很好的问题,我很喜欢你的工作,所以现在可以算是灰色地带。 - Jeff Atwood
12
很多公司(例如Twitter)最初采用JavaScript客户端的原因是他们认为它会更快。但现在,他们意识到这样做其实更慢了。请参见以下链接:http://engineering.twitter.com/2012/05/improving-performance-on-twittercom.html 和 http://openmymind.net/2012/5/30/Client-Side-vs-Server-Side-Rendering/ - Moshe Katz
18个回答

138
Boundless,我们深入研究了第二种选择,并将其推广到数千名学生中。我们的服务器是JSON REST API(Scala + MongoDB),所有客户端代码都直接从CloudFront提供服务(即:www.boundless.com只是CloudFront的别名)。
优点: - 前沿/令人兴奋 - 为您自己的Web客户端、移动客户端、第三方访问等提供API基础,物超所值 - 极快的网站加载/页面转换
缺点: - 没有进行更多工作就无法实现SEO友好/准备 - 需要顶尖的Web前端人才,他们准备应对70% JavaScript的网站体验和这意味着什么。
我认为这是所有Web应用程序的未来。
一些针对Web前端人员的想法(鉴于这种架构,所有新颖/挑战都在这里):
  • CoffeeScript。生产高质量代码更加容易。
  • Backbone。优秀的逻辑组织方式,活跃的社区。
  • HAMLC。Haml + CoffeeScript 模板 => JS。
  • SASS

我们已经为前端开发构建了一个工具集 'Spar'(Single Page App Rocketship),它实际上是Rails的资产管道,针对单页面应用程序开发进行了调整。我们将在我们的 github 页面上公开发布,以及一篇博客文章详细说明如何使用它和总体架构。

更新:

关于人们对Backbone的担忧,我认为它们被夸大了。Backbone远不是一个深层框架,而是一种组织原则。 Twitter本身是一个巨型Javascript怪物,涵盖了数百万用户和旧版浏览器中的每一个角落情况,在实时加载推文,垃圾回收等方面显示了大量多媒体等内容。在所有“纯”JS网站中,Twitter是个例外。通过JS交付的许多令人印象深刻的复杂应用程序表现非常出色。

你选择的架构完全取决于你的目标。如果你正在寻找支持多个客户端并且拥有良好前端人才的最快方式,那么投资独立API是一个绝佳的选择。


49
非常好的问题,+1。毫无疑问,这对我来说是未来有用的参考资料。此外,@Aaron和其他人为讨论增加了价值。 像Ruby一样,这个问题同样适用于其他编程环境。
我已经使用了前两个选项。第一个选项用于众多应用程序,第二个选项用于我的开源项目Cowoop
选项1
毫无疑问,这是最受欢迎的选项。但我发现实现非常http-ish。每个API的初始代码都涉及处理请求对象。因此,API代码不仅仅是纯的ruby / python /其他语言代码。

选项2

我一直很喜欢这个。

这个选项还意味着HTML不是在服务器上运行时生成的。这就是选项2与选项3不同的地方。但是,它们使用构建脚本作为静态HTML构建。当在客户端加载时,这些HTML将作为JS API客户端调用API服务器。

  • 关注点分离是一个很大的优势。并且非常符合您和我的口味,后端专家实现后端API,像平常的语言代码一样轻松测试,而不必担心框架/ HTTP请求代码。

  • 这在前端方面并不像听起来那么困难。进行API调用,然后生成的数据(大多数为json)可用于客户端模板或MVC。

  • 较少的服务器端处理。这意味着您可以选择廉价硬件/较便宜的服务器。

  • 更容易独立测试各个层,更容易生成API文档。

它确实有一些缺点。

  • 许多开发人员认为这过于工程化,难以理解。因此,架构可能会受到批评。

  • i18n / l10n很难。由于HTML本质上是在构建时生成的静态文件,因此每种支持的语言需要多次构建(这并不一定是坏事)。但即使如此,您可能会遇到l10n / i18n周围的边角情况,并需要小心处理。

选项 3

在这种情况下,后端编码必须与第二个选项相同。大多数第二个选项的要点也适用于此。

使用服务器端模板动态呈现网页。这使得 i18n/l10n 更加容易,采用更成熟/被接受的技术。对于某些需要页面呈现的基本上下文(如用户、语言、货币等),可能会减少一个 http 调用。因此,服务器端处理增加了渲染,但可能通过减少对 API 服务器的 http 调用来补偿。

现在页面在服务器上呈现,前端与编程环境更紧密地联系在一起。对于许多应用程序来说,这甚至可能不是一个考虑因素。

Twitter 案例

据我所知,Twitter 可能会在服务器上进行初始页面呈现,但对于页面更新仍然有一些 API 调用和客户端模板来操作 DOM。因此,在这种情况下,您需要维护双重模板,这会增加一些开销和复杂性。不像 Twitter,不是每个人都能负担得起这个选项。

我们的项目堆栈

我碰巧使用 Python。我使用 JsonRPC 2.0 而不是 REST。尽管我喜欢 JsonRPC 的想法,但我建议使用 REST,我使用以下库。考虑选项 2/3 的人可能会发现它有用。

我的结论和建议

选项3!

所有说的,我已经成功地使用了选项2,但现在更倾向于选项3,因为它更简单。使用构建脚本生成静态HTML页面,并使用专门用于提供静态页面的超快速服务器之一进行服务非常诱人(选项2)。

27
我们在建立gaug.es时选择了方案#2。我负责API(Ruby、Sinatra等)的开发,我的合伙人Steve Smith则负责前端(JavaScript客户端)的开发。
优点:
1. 并行快速开发。如果我领先于Steve进行工作,我可以为新功能创建API。如果他领先于我,则可以很容易地虚假API并构建UI。 2. 免费获得API。对应用程序中的数据具有开放访问权迅速成为标准功能。如果从头开始建立API,则可以免费获得此功能。 3. 清晰的分离。更好地将您的应用程序视为具有客户端的API。当然,第一个也是最重要的客户端可能是Web客户端,但这使您可以轻松地创建其他客户端(iPhone、Android)。
缺点:
1. 向后兼容性。这与API比您的直接问题更相关,但一旦API发布出去,您不能只是打破它或者它会影响所有客户端。这并不意味着您必须慢下来,但这确实意味着您经常必须同时使两个事情起作用。添加API或新字段很好,但更改/删除不应该没有版本控制而进行。
目前我无法想到其他缺点。
结论:如果计划发布API,则API + JS客户端是最佳选择。
附言:我还建议在发布API之前完全记录您的API。记录Gaug.es API的过程真正帮助我们改进了API文档,详情请参见http://get.gaug.es/documentation/api/

10
我更倾向于方案2和3。主要是因为方案1违反了关注点分离并混合了各种内容。最终你会发现需要一个没有匹配HTML页面/等的API端点,而你会在同一代码库中使用混合的HTML和JSON端点。即使是MVP,它也变成了一团乱麻,你最终必须重写它,因为它太杂乱无章了,这样甚至不值得挽救。
采用方案2或3可以让您完全拥有一个几乎相同的API。这提供了巨大的灵活性。我还不完全信服Backbone/ember/whatever/etc.js。我认为它很棒,但正如我们在Twitter上看到的那样,这并不理想。但是... Twitter也是一个庞大的企业,拥有数亿用户。因此,任何改进都可能对各个业务单元的各个领域的底线产生巨大影响。我认为决策背后的原因不仅仅是速度,他们没有让我们知道更多。但这只是我的看法。然而,我不排除Backbone及其竞争对手的优势。这些应用程序很棒,并且非常干净,非常响应(在很大程度上)。
第三个选项也有一些有效的吸引力。这是我将遵循Pareto原则(80/20规则)的地方,在服务器上呈现主要标记的20%(或反之亦然),然后让一个漂亮的JS客户端(Backbone /等)运行其余部分。如果必要,您可能不会通过JS客户端与REST API进行100%的通信,但您将做一些工作以使用户体验更佳。
我认为这是那种“因情况而异”的问题,答案是取决于你正在做什么,为谁服务,以及你希望他们获得什么样的体验。鉴于此,我认为您可以在2或3之间或两者的混合中做出决定。

7

通常我会选择第二个选项,使用Rails来构建API,使用backbone来处理JS的事情。你甚至可以免费使用ActiveAdmin来获取管理面板。我已经使用这种后端方式发布了数十个移动应用程序。

然而,这严重取决于你的应用程序是否具有交互性。

我在上一个RubyDay.it上做了一个关于这种方法的演示:http://www.slideshare.net/matteocollina/enter-the-app-era-with-ruby-on-rails-rubyday

对于第三个选项,为了获得第二个选项的响应速度,您可能需要尝试pajax,就像Github一样。


7
我们采用以下变体的方法#3: 制作仅限JSON的REST API服务器。制作HTML网站服务器。 HTML Web服务器不是像您的变体那样,作为REST API服务器的客户端。相反,两者是同级。在表面之下不远处,存在一个内部API,提供了两个服务器所需的功能。
我们不知道有任何先例,因此它有点试验性质。到目前为止(即将进入Beta测试阶段),它表现得非常好。

7
我目前正在将一个庞大的CMS从选项1转换到选项3,进展顺利。我们选择在服务器端呈现标记,因为SEO对我们来说非常重要,我们希望网站在手机上表现良好。
我使用Node.js作为客户端的后端,并使用一些模块来帮助我。我在这个过程中还比较早,但基础已经奠定,只需要检查数据是否都正确呈现即可。这是我正在使用的内容:
- Express作为应用程序的基础。 (https://github.com/visionmedia/express) - Request用于获取数据。 (https://github.com/mikeal/request) - Underscore模板在服务器端呈现。我在客户端上重复使用它们。(https://github.com/documentcloud/underscore) - UTML将Underscore的模板包装起来,使它们可以与Express一起使用。(https://github.com/mikefrey/utml) - Upfront收集模板,并让您选择哪些模板发送到客户端。(https://github.com/mrDarcyMurphy/upfront) - Express Expose将获取的数据、一些模块和模板传递给前端。(https://github.com/visionmedia/express-expose) - Backbone在前端创建模型和视图,吞噬了传递的数据。(https://github.com/documentcloud/backbone)
这是堆栈的核心。我发现其他一些模块也很有用:
- fleck (https//github.com/trek/fleck) - moment (https//github.com/timrwood/moment) - stylus (https//github.com/LearnBoost/stylus) - smoosh (https//github.com/fat/smoosh) …虽然我正在研究grunt(https//github.com/cowboy/grunt) - console trace (//github.com/LearnBoost/console-trace).
不,我没有使用coffeescript。
对我来说,这个选项非常有效。后端的模型不存在,因为我们从API获取的数据结构良好,我将其原样传递到前端。唯一的例外是我们的布局模型,在那里我添加了一个单一属性,使呈现更加智能和轻便。我没有使用任何花哨的模型库,只是一个在初始化时添加我需要的内容并返回自身的函数。
(抱歉链接有点奇怪,我太菜了,不能在stackoverflow上发布这么多链接)

6

我已经进行了2个月的3个月项目,采用了你在这里概述的第二种方法。我们在服务器端使用RESTful API,前端使用backbone.js。Handlebars.js管理模板,jQuery处理AJAX和DOM操作。对于旧浏览器和搜索蜘蛛,我们回退到服务器端渲染,但是我们使用与Handlebars前端相同的HTML模板,使用Mozilla Rhino。

我们选择这种方法有很多不同的原因,但非常清楚它有一点风险,因为它尚未在广泛范围内得到证明。不过,到目前为止,一切都进展顺利。

到目前为止,我们只使用了一个API,但在项目的下一阶段,我们将使用第二个API。第一个API用于大量数据,第二个通过API更像CMS。

让这两个项目完全独立于彼此是选择这种基础架构的关键考虑因素。如果您正在寻找一种架构来混合不同的独立资源而没有任何依赖关系,则这种方法值得一看。

很抱歉我不是Ruby专家,所以无法评论其他方法。有时冒险是可以的,但有时最好保守一些。根据项目类型,您自己会知道该怎么做。

祝您在这里做出正确的选择。也很想看看其他人分享的内容。


4

当我的网站不是100% CRUD实现数据时,我喜欢使用#3。这还没有发生。

我更喜欢sinatra,并将应用程序拆分为几个具有不同目的的rack应用程序。我将创建一个特定于API的rack应用程序,它将涵盖我需要的API内容。然后可能会创建一个用户rack应用程序,用于呈现我的网页。有时该版本将查询API(如果需要),但通常只涉及html网站。

如果需要,我会从用户端进行持久性层查询,而不必担心它。我不过度关注创建完全分离,因为它们通常用于不同的目的。

这里是使用多个rack应用程序的非常简单的示例。我在其中添加了一个快速的jquery示例,让您可以看到它如何命中API应用程序。您可以看到使用sinatra和挂载具有不同目的的多个rack应用程序可以多么简单。

https://github.com/dusty/multi-rack-app-app


1
非常好的问题,我很惊讶,因为我认为这是一个非常普遍的任务,所以我会有很多资源来解决这个问题,但事实并非如此。
我的想法如下: - 创建一些模块,在API控制器和HTML控制器之间具有共同的逻辑,返回JSON或呈现HTML,并将此模块包含在HTML控制器和API控制器中,然后可以进行任何操作,例如:
module WebAndAPICommon
    module Products

        def index
            @products = # do some logic here that will set @products variable
        end

    end
end


class ProductsController < ApplicationController
    # default products controlelr, for rendering HMTL pages 
    include WebAndAPICommon

    def index
        super
    end

end



module API
    class ProductsController
        include WebAndAPICommon

        def index
            super
            render json: @products
        end

    end
end

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