<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>run99</title>
    <link>https://ruby-china.org/run99</link>
    <description/>
    <language>en-us</language>
    <item>
      <title>分享一下假期完善的 Grape 脚手架</title>
      <description>&lt;p&gt;项目地址：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://gitee.com/run27017/grape_app_scaffold" rel="nofollow" target="_blank"&gt;https://gitee.com/run27017/grape_app_scaffold&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="Grape App Scaffold"&gt;Grape App Scaffold&lt;/h2&gt;
&lt;p&gt;可以说是最完善的 Grape 框架的脚手架&lt;/p&gt;
&lt;h2 id="概览"&gt;概览&lt;/h2&gt;
&lt;p&gt;不同于 Rails，使用纯 Ruby 构建的脚手架项目，仅使用 Bundler 和 Rack 运行服务器，非常地轻量。&lt;/p&gt;

&lt;p&gt;脚手架包含的组件：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;纯 &lt;a href="https://github.com/ruby-grape/grape" rel="nofollow" target="_blank" title=""&gt;Grape&lt;/a&gt; 实现，完全脱离 Rails&lt;/li&gt;
&lt;li&gt;支持自动生成 Swagger 文档&lt;/li&gt;
&lt;li&gt;单元测试&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rake db:setup&lt;/code&gt;、&lt;code&gt;rake db:migrate&lt;/code&gt; 等&lt;/li&gt;
&lt;li&gt;ActiveRecord&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/varvet/pundit" rel="nofollow" target="_blank" title=""&gt;pundit&lt;/a&gt;：权限验证&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/sds/overcommit" rel="nofollow" target="_blank" title=""&gt;overcommit&lt;/a&gt; 和 &lt;a href="https://github.com/rubocop-hq/rubocop" rel="nofollow" target="_blank" title=""&gt;RuboCop&lt;/a&gt;：代码质量保证&lt;/li&gt;
&lt;li&gt;一个类似于 &lt;code&gt;rails console&lt;/code&gt; 的交互式环境&lt;/li&gt;
&lt;li&gt;……&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>run99</author>
      <pubDate>Tue, 11 Feb 2020 17:52:19 +0800</pubDate>
      <link>https://ruby-china.org/topics/39497</link>
      <guid>https://ruby-china.org/topics/39497</guid>
    </item>
    <item>
      <title>已撤回</title>
      <description>&lt;p&gt;Done&lt;/p&gt;</description>
      <author>run99</author>
      <pubDate>Wed, 11 Dec 2019 10:30:49 +0800</pubDate>
      <link>https://ruby-china.org/topics/39316</link>
      <guid>https://ruby-china.org/topics/39316</guid>
    </item>
    <item>
      <title>Rails API 的整体实践之面向测试的开发</title>
      <description>&lt;p&gt;这个是我的一个&lt;a href="https://ruby-china.org/topics/39100" title=""&gt;系列文章&lt;/a&gt;的正文的第二篇了。&lt;a href="https://ruby-china.org/topics/39115" title=""&gt;上一篇&lt;/a&gt;是讲接口设计和接口行为的，似乎没有取得太大的反响。这一次，说一说关于 Rails 的东西。&lt;/p&gt;

&lt;p&gt;老实讲，Rails 我用了三年有余了，基本上 Web 建站就全靠它。因为实在也没有换用别的框架（这里有个特例，是关于 Grape 的，我会在下一篇讲）或者语言的必要。&lt;/p&gt;

&lt;p&gt;我在这里，会讲到有关 Rails 的基本用法的一切，包括如何去构建测试，如何去构建一个完整的 API（就像上一篇文章所讲的那样），以及一些配套的 gem 等。&lt;/p&gt;

&lt;p&gt;框架所起的作用，是它提供了一套既有的模式，简化了我们大量的骨架性的工作。我发现，很多初学者，即使不怎么精通这门语言，也能很快上手框架的开发，做一些简单的需求。Rails 就是这样，很多入门选手，可以称之为是一个 Rails 选手，却不是一个合格的 Ruby 选手，只是因为他们是先接触的 Rails，然后再去学 Ruby 的。我不知道这是 Rails 的幸运，还是 Rails 的不幸。&lt;/p&gt;
&lt;h2 id="构建一套完整的 API"&gt;构建一套完整的 API&lt;/h2&gt;
&lt;p&gt;内容估计有点多，我想放在下次再讲。&lt;/p&gt;
&lt;h2 id="面向测试的开发"&gt;面向测试的开发&lt;/h2&gt;
&lt;p&gt;Rails 指南里关于&lt;a href="https://ruby-china.github.io/rails-guides/testing.html" rel="nofollow" target="_blank" title=""&gt;测试&lt;/a&gt;的一节，只讲到了框架为支持测试提供了哪些方法，并没有讲到在实践中如何去构建测试。例如，它对于 Controller 层的一个测试范例这样写道：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# articles_controller_test.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ArticlesControllerTest&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionDispatch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;IntegrationTest&lt;/span&gt;
  &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;"should get index"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;articles_url&lt;/span&gt;
    &lt;span class="n"&gt;assert_response&lt;/span&gt; &lt;span class="ss"&gt;:success&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;什么意思呢？它是说，先是调用一个接口，然后它应该成功。就没了。我不知道这样的测试能说明什么问题。这样的测试，多多少少会让人有点不放心的感觉。&lt;/p&gt;

&lt;p&gt;那么，问题来了。Controller 的测试应该怎么去写呢？或者更进一步讲，分层次的测试应该怎么去写呢？的确，测试应该要分层，我们不可能把所有测试都放在 Controller 里面，它会变得臃肿不堪。而事实上，很多测试，如果放在 Controller 内，这样的测试代码会很难写。分层的测试是必要的。&lt;/p&gt;

&lt;p&gt;在介绍每个层次的测试怎么写之前，先列出一下测试的层次有哪些：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Controler&lt;/li&gt;
&lt;li&gt;Policy&lt;/li&gt;
&lt;li&gt;ActiveRecord&lt;/li&gt;
&lt;li&gt;ActiveJob&lt;/li&gt;
&lt;li&gt;Utils or core&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;注意到，这里涉及到 MVC 框架里的 M 和 C 的部分，没有 V. 永远不要用代码去测有关视图的部分，它应该用肉眼去验证，或者交给前端开发者报告问题，而不是交给机器。试想一下，下面的测试代码是不是有些愚蠢：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;"should get index"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;articles_url&lt;/span&gt;
  &lt;span class="n"&gt;assert_response&lt;/span&gt; &lt;span class="ss"&gt;:success&lt;/span&gt;

  &lt;span class="nb"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;articles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'articles'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;assert_equal&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;
  &lt;span class="n"&gt;article1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;article2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt;
  &lt;span class="n"&gt;assert_equal&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;article2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;
  &lt;span class="n"&gt;assert_equal&lt;/span&gt; &lt;span class="s1"&gt;'title 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;article1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;
  &lt;span class="n"&gt;assert_equal&lt;/span&gt; &lt;span class="s1"&gt;'content 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;article1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;content&lt;/span&gt;
  &lt;span class="n"&gt;assert_equal&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;article2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;
  &lt;span class="n"&gt;assert_equal&lt;/span&gt; &lt;span class="s1"&gt;'title 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;article2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;
  &lt;span class="n"&gt;assert_equal&lt;/span&gt; &lt;span class="s1"&gt;'content 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;article2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;content&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="Controller 的测试"&gt;Controller 的测试&lt;/h3&gt;&lt;h3 id="基本模式"&gt;基本模式&lt;/h3&gt;
&lt;p&gt;Controller 的测试，一般只需要测试正确的接口行为就可以了。我们应该把一般通用的异常情况交给其他的层去测试，如权限问题、参数问题等。所以，一个 Controller 的测试，一般只用写成如下简单的形式：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;"should get index"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;articles_url&lt;/span&gt;
  &lt;span class="n"&gt;assert_response&lt;/span&gt; &lt;span class="ss"&gt;:success&lt;/span&gt;
  &lt;span class="n"&gt;assert_equals&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:one&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:two&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:three&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;assigns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:articles&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里用到两个组件，fixtures 和 rails-controller-testing. 前者是 Rails 标配的，后者需要你自行在 Gemfile 中添加：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'rails-controller-testing'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这要求，我们需要在 Controller 的处理方法里暴露出&lt;code&gt;@articles&lt;/code&gt;实例变量，像这样：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
  &lt;span class="vi"&gt;@articles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;是的，我们去测方法的返回值，而不是 JSON 或者 HTML 视图。将测试粒度控制在我们方便处理的范围，是我们专业测试人员的义务。由于 index 方法不可能返回任何的值，我们就只能测试它的实例变量了。事实上，这些实例变量会交给视图层去渲染，所以也不算对方法造成污染。&lt;/p&gt;

&lt;p&gt;当然，这只是一个简单的情况。对于复杂的接口行为，依然可以采用这种方式。你可以暴露出更多的实例变量，一个、两个或更多个，只要是视图层需要的都会暴露出来。然后我们去测试这些实例变量，就好了。&lt;/p&gt;

&lt;p&gt;TODO： factory_bot&lt;/p&gt;
&lt;h3 id="接口调用"&gt;接口调用&lt;/h3&gt;
&lt;p&gt;在上面的例子里，接口调用采用的是如下的方式：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;articles_url&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;有时候需要传递参数，还有 Headers. 这个时候，我推荐在大多数情况下借助一下 factory_bot 的用法：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;"should put update"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;put&lt;/span&gt; &lt;span class="n"&gt;article_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@article&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="ss"&gt;params: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;article: &lt;/span&gt;&lt;span class="n"&gt;attributes_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:article&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;title: &lt;/span&gt;&lt;span class="s1"&gt;'updated title'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;assert_response&lt;/span&gt; &lt;span class="ss"&gt;:success&lt;/span&gt;
  &lt;span class="n"&gt;assert_equals&lt;/span&gt; &lt;span class="s1"&gt;'updated title'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;assigns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:article&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意这一节代码&lt;code&gt;attributes_for(:article, title: 'updated title')&lt;/code&gt;，它是 factory_bot 的一个方法。该方法会返回&lt;code&gt;:article&lt;/code&gt;工厂的属性值，当然&lt;code&gt;title&lt;/code&gt;属性会被覆盖为&lt;code&gt;updated title&lt;/code&gt;。在测试 update 的接口的时候，我们是不需要测试所有的字段更新的，只用测试自己最关心的几个就好了。当然，不放心的人可以测试完整的。使用&lt;code&gt;attributes_for&lt;/code&gt;包装的一个考虑是，它会自动控制 validation 的问题。有可能你只传递&lt;code&gt;title&lt;/code&gt;属性接口会报错，用&lt;code&gt;attributes_for&lt;/code&gt;包装，就不用关心 validation 的问题了。&lt;/p&gt;
&lt;h2 id="Policy 的测试"&gt;Policy 的测试&lt;/h2&gt;
&lt;p&gt;刚才说过，Controller 层只需要测试正确的行为，一般的错误行为交给其他层去测试。Controller 层最容易出现的错误是：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;required 参数没有传&lt;/li&gt;
&lt;li&gt;ActiveRecord 的验证没有通过&lt;/li&gt;
&lt;li&gt;调用者身份没有权限&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;错误 1 由捕获异常通用地处理了，可以不必测；错误 2 交给 ActiveRecord 层测试；而错误 3 就是这里讲的 Policy 的测试了。&lt;/p&gt;

&lt;p&gt;Policy 的测试是很直白的，就是直接构建 Policy 的类实例，测试它的方法即可：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:one&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;assert&lt;/span&gt; &lt;span class="no"&gt;Pundit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update?&lt;/span&gt;
&lt;span class="n"&gt;assert_not&lt;/span&gt; &lt;span class="no"&gt;Pundit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:two&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里测试 update 接口的用户权限问题。我把它们都写在一起了。事实上它告诉我们，唯有&lt;code&gt;article.user&lt;/code&gt;才可以更新这篇文章，而其他人则不可以。&lt;/p&gt;
&lt;h2 id="Model 的测试"&gt;Model 的测试&lt;/h2&gt;
&lt;p&gt;在 Rails 中，Model 层采用的是 ActiveRecord 模式。这里不讨论 ActiveRecord 模式的优劣，但 ActiveRecord 确实是一种包含太多的东西设计模式。在 ActiveRecord 中，会定义到：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;数据验证&lt;/li&gt;
&lt;li&gt;回调&lt;/li&gt;
&lt;li&gt;业务逻辑&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这三种情形的测试没有统一的方式一言以蔽之。不过，数据验证倒是可以讲讲。&lt;/p&gt;

&lt;p&gt;在应用数据验证测试之前，我推荐使用 fixture 和 factory. Rails 自带有 fixture，而 factory 可以使用 factory_bot 这个 gem.&lt;/p&gt;

&lt;p&gt;应用 factory_bot 的时候，注意一点，常规的工厂一定是有效的实例。亦即，往往在 Model 的测试里，下面的测试是第一个要通过的：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s1"&gt;'normal build should be valid'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;valid?&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果以上的测试不通过，修改工厂的实现让其通过。只有在这个的基础上，才能去测试其他的验证方法，如：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s1"&gt;'username should be present'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;assert_not&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;username: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;valid?&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s1"&gt;'username should be unique'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:article&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;assert_not&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;username: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;username&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;valid?&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我在实践中，像 &lt;em&gt;"username should be valid"&lt;/em&gt; 这样的简单测试，我往往不会去写，而是当成一个天然的自信势必会通过的。这涉及到一个测试覆盖率的问题，究竟覆盖多少测试要根据实际情况和个人风格而定。&lt;/p&gt;
&lt;h3 id="其他测试"&gt;其他测试&lt;/h3&gt;
&lt;p&gt;测试说到这里，也算是作结了。其他测试可能包括异步任务的测试、应用框架的测试、核心类或者工具类的测试等等。这些测试方法，作为测试驱动开发的基本涵养，并不是我能够一两句话说清的。为其发声的大师实在是太多太多了，比如在&lt;a href="https://book.jd.com/" rel="nofollow" target="_blank" title=""&gt;京东图书&lt;/a&gt;里搜索 &lt;em&gt;测试驱动开发&lt;/em&gt; 会找大一大箩筐的图书条目。&lt;/p&gt;</description>
      <author>run99</author>
      <pubDate>Sun, 20 Oct 2019 16:22:53 +0800</pubDate>
      <link>https://ruby-china.org/topics/39171</link>
      <guid>https://ruby-china.org/topics/39171</guid>
    </item>
    <item>
      <title>聊聊 Web 接口设计和接口行为</title>
      <description>&lt;p&gt;关于 Web 接口的设计，现在网络上聊得最多得是 RESTful. 但很多人仅仅认为 RESTful 是个接口得设计规范，而忽略了它本质上是一个软件设计架构。&lt;/p&gt;

&lt;p&gt;在写这篇文章之前，我在网络上查阅了一些文档资料，并从中筛选出我认为写得不错的博客文档。它们是：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;阮一峰的两篇博文：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.ruanyifeng.com/blog/2011/09/restful.html" rel="nofollow" target="_blank" title=""&gt;理解 RESTful 架构&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.ruanyifeng.com/blog/2014/05/restful_api.html" rel="nofollow" target="_blank" title=""&gt;RESTful API 设计指南&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;和&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;来自于就浩这口的四篇博文：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://howardwchen.com/2017/08/28/talk-about-restful-popular-science/" rel="nofollow" target="_blank" title=""&gt;聊聊 RESTful - 科普篇&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://howardwchen.com/2017/09/18/talk-about-restful-popular-api-design-1/" rel="nofollow" target="_blank" title=""&gt;聊聊 RESTful - 接口设计篇（一）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://howardwchen.com/2017/09/25/talk-about-restful-popular-api-design-2/" rel="nofollow" target="_blank" title=""&gt;聊聊 RESTful - 接口设计篇（二）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://howardwchen.com/2017/10/09/talk-about-restful-popular-api-design-3/" rel="nofollow" target="_blank" title=""&gt;聊聊 RESTful - 接口设计篇（三）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;既然已经有这么多的轮子在那里了，我不需要再重复去扯关于 Restful 的各种细节了。所以，我就当下的各种资料，做一些总结性的梳理。是的，也许你不用去读我推荐给你的文章，而直接进入我的总结性的梳理。但你一定得有相关的基础储备，有的是 HTTP 的，有的是 RESTful 的。你的基础储备得有，哪怕是错误的，也是可以的。&lt;/p&gt;
&lt;h2 id="理解 RESTful"&gt;理解 RESTful&lt;/h2&gt;&lt;h3 id="你应该面向的是 HTTP 而不是 RESTful"&gt;你应该面向的是 HTTP 而不是 RESTful&lt;/h3&gt;
&lt;p&gt;当我们将 RESTful 挂在嘴边的时候，可能已经陷入了一个误区。我们忽略了一个事实：HTTP 本身就是 RESTful 的。例如，在 HTTP 里，URI（统一资源定位符）就是用来定位各种资源的。而资源本身可以展示成不同的格式，如 HTML 的、XML 的、JSON 的等等，使用&lt;code&gt;Content-Type&lt;/code&gt;这个响应头来标明响应体的格式。另外，GET、POST、PUT、DELETE 就是来表示对资源的操作的。HTTP 本质上就是 RESTful 的，这是因为，当初在设计 HTTP 的时候，潜意识中就是用的 RESTful 的指导思想。某种程度上说，RESTful 是从 HTTP 的规范中归纳出来的。而不是说，它是后来想出的接口设计规范，用于指导如何设计 HTTP 接口。&lt;/p&gt;

&lt;p&gt;所以说，当你使用 HTTP 定义你的接口的时候，它天生就应该是 RESTful. 你不应该去强调你的接口如何 RESTful，而应该说明，你的接口哪些部分没有采纳 RESTful.&lt;/p&gt;

&lt;p&gt;至于为什么我们现在看到的接口很多都不是那么的 RESTful 的。一个可能的原因是，HTTP 的设计是个逐步演化的过程。它最初可能就只有 GET、POST 这两个方法，至于其他的方法可能是后来再加上去的。还有一点，人都是懒惰的，一个开发者可能并不想去理解那么多的 HTTP 的概念，而造成了某种误用。&lt;/p&gt;
&lt;h3 id="用最简单的话来理解 RESTful"&gt;用最简单的话来理解 RESTful&lt;/h3&gt;
&lt;p&gt;REST，即是 Representational State Transfer 的缩写。它的翻译是”表现层状态转化“。我们就不去理解这个词组的意思了，仅仅需要知道的是，在 REST 世界里，你面向的是资源。&lt;/p&gt;

&lt;p&gt;资源，可以理解为一切的实体。这个实体，可以是具体的，例如一本书、一张图片，一首歌曲，一个人。也可以是抽象的，例如一种服务、一个行为。我们需要将转账、关注这些行为都要理解成一种资源。&lt;/p&gt;

&lt;p&gt;当我们能够定义和定位资源的时候，针对资源的操作大体只有四种，即 GET、POST、PUT、DELETE，即查看、创建、更新、删除这四种操作。这有点像数据库，面对表的数据只能有增删查改这四种一样。一个局限就是，除了这四种，你不能针对资源有其他的操作，其他的操作必须抽象成另一个资源。这就是面向资源的设计。&lt;/p&gt;

&lt;p&gt;资源的操作带来的结果是资源状态的改变。HTTP 是无状态的应用层传输协议，资源的状态只保存在服务器，要想对它做任何改变，只能通过操作改变它。&lt;/p&gt;

&lt;p&gt;最后再插入一点。我们用 URI（统一资源定位符）来定位资源，用方法（GET、POST、PUT、DELETE）来表示对资源的操作。另外，我们还会使用大量的头部表示各种元信息。元信息是相当重要的，却是最容易被忽视的部分。在响应体中，还会有一个状态码，顾名思义，它表示这次请求的状态转移的结果。&lt;/p&gt;
&lt;h2 id="我所觉得的 HTTP 的最佳实践"&gt;我所觉得的 HTTP 的最佳实践&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;我不要你觉得，我要我觉得。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;在这一篇里，我直接抛出在设计 HTTP 接口时的最佳实践。当然每个人所理解的最佳是不同的，我个人接受反驳，但未必回应。&lt;/p&gt;
&lt;h3 id="URI"&gt;URI&lt;/h3&gt;
&lt;p&gt;使用名词定义资源。另外，名词倾向于使用复数。例如用&lt;code&gt;/books&lt;/code&gt;表示一个集合，用&lt;code&gt;/books/:id&lt;/code&gt;表示单个的资源。唯一的使用单数的情况是，它特指某个单个资源。如&lt;code&gt;/system&lt;/code&gt;表示单例资源，&lt;code&gt;/user&lt;/code&gt;特指当前用户。&lt;/p&gt;
&lt;h3 id="格式"&gt;格式&lt;/h3&gt;
&lt;p&gt;最简单的是一律只使用 JSON. 当然你也可以同时支持其他的格式，如 XML、formData 等，但务必要清晰。&lt;/p&gt;

&lt;p&gt;如果只支持 JSON，那么请求体的&lt;code&gt;Accept&lt;/code&gt;必须注明&lt;code&gt;application/json&lt;/code&gt;，而响应体的&lt;code&gt;Content-Type&lt;/code&gt;也必须是&lt;code&gt;application/json&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="状态码"&gt;状态码&lt;/h3&gt;
&lt;p&gt;务必使用状态码表示状态，如 4xx 是客户端的错误，5xx 是服务端的错误。但 HTTP 的状态码只是一般性的错误表示，不应该作为开发者处理的字段。我一般会在错误的响应体中定义如下格式：&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="数据格式"&gt;数据格式&lt;/h3&gt;
&lt;p&gt;数据格式都要用一个字段包装起来，无论是&lt;strong&gt;请求体&lt;/strong&gt;还是响应体。这样做是为了保持一致性和方便以后的扩展。&lt;/p&gt;

&lt;p&gt;例如单个 book 资源：&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"book"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;和 books 资源集合：&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"books"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;book&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;book&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"总数"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上是严格按照 RESTful 所说的而产生的最佳实践的标准。但在我的实际开发中，我往往会做一些妥协。（就像写这篇文章一样，我会随性很多，并做出了很多的妥协）&lt;/p&gt;
&lt;h3 id="版本号"&gt;版本号&lt;/h3&gt;
&lt;p&gt;使用一个特殊的 Header 表示。&lt;/p&gt;
&lt;h3 id="鉴权"&gt;鉴权&lt;/h3&gt;
&lt;p&gt;我习惯使用一个自定义的&lt;code&gt;X-Token&lt;/code&gt;的头部，使用 jwt 加密。据说 OAuth 2.0 很流行，但我对这一块并不懂。&lt;/p&gt;
&lt;h3 id="可以接受的调整"&gt;可以接受的调整&lt;/h3&gt;
&lt;p&gt;首先，有些超出”增删查改“范畴的操作并没有抽象成资源，而是作为一个动作附加在资源后面。更确切地说，这一部分没有使用名词，而是作为动词体现在 URI 中。比如，对于关注，我可能是这样设计：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /books/:id/star
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里的 star 应理解为动词而不是名词。如果是名词，正如我前面所说的，它应该用复数。&lt;/p&gt;

&lt;p&gt;这是我作为 RESTful 的践行者做得不够彻底的地方。&lt;/p&gt;

&lt;p&gt;关于 PUT 和 PATCH 的语义，我想在&lt;a href="https://howardwchen.com/2017/09/18/talk-about-restful-popular-api-design-1/#POST%E4%B8%8EPUT" title=""&gt;聊聊 RESTful - 接口设计篇（一）&lt;/a&gt;里已经解释得很清楚了。在实际的实践中，我往往是把 PUT 作为局部更新来处理的，而 PATCH 和 PUT 一样，并且也不是按照文章说的那样去处理。&lt;/p&gt;

&lt;p&gt;关于版本号，我接受在 URI 中加入。这样会更简单。其实，我在实际开发过程中从没有声明过版本号，接口一但有调整都是直接调整，从没有维护过两个版本的情况。这与我只开发公司内部产品，而不是开放 API 有莫大的关系吧。&lt;/p&gt;
&lt;h2 id="面向失败的设计"&gt;面向失败的设计&lt;/h2&gt;
&lt;p&gt;我们听过面向对象的设计，刚刚也接触过面向资源的接口设计。这里我想提的一点，是面向失败的设计。我们现在所接触的系统，其规模大大超过了以往。我们在上手大部分项目的时候，从一开始，就要考虑到它的完备性，以应对方方面面可能出现的失败。那么，从接口设计开始，我们就要考虑全面一些吧。&lt;/p&gt;

&lt;p&gt;我们设想一下客户端调用接口的完整过程，它往往是：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;客户端发送请求&lt;/li&gt;
&lt;li&gt;服务端接收到请求的数据&lt;/li&gt;
&lt;li&gt;服务端对请求的数据做验证，希望它符合一定的格式，并且不会对系统的约束造成破坏&lt;/li&gt;
&lt;li&gt;如果包含鉴权系统，服务端需要验证鉴权信息，并且需要验证客户端的身份信息是否有对该资源有操作的权限&lt;/li&gt;
&lt;li&gt;如果 3、4 的检查都通过，服务端处理数据和请求&lt;/li&gt;
&lt;li&gt;服务端将处理的结果返还给客户端&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;从以上两点看到，服务器需要考虑到一个请求的主要在两个方面：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;请求的数据是否符合预期&lt;/li&gt;
&lt;li&gt;用户的身份是否对该资源有操作的权限&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这分别对应了，在面向失败的设计里，对系统行为预期的&lt;strong&gt;最低要求&lt;/strong&gt;：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;接口的调用不会破坏系统的约束&lt;/li&gt;
&lt;li&gt;最起码的不该被非法用户入侵&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;一切更优化的最低要求和行为，都是建立在这两点的基础上的。&lt;/p&gt;
&lt;h3 id="检验请求数据"&gt;检验请求数据&lt;/h3&gt;
&lt;p&gt;第一件事情就是要检验请求的数据。首先通用的检验我们就不展开说了，如你的数据是合法的 JSON 格式，这应该是在一开始就限制好的。&lt;/p&gt;

&lt;p&gt;针对一个合法的 JSON 数据本身，通常需要考虑的问题是：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;一个字段是可选的，还是必须的。如果是必须的，在缺乏该字段的时候，系统应该报错。如果是可选的，就需要考虑好它的默认值问题了。&lt;/li&gt;
&lt;li&gt;该字段的类型。如标量格式的类型：int、string、boolean 等；又如矢量格式的类型，如：array、object 等。&lt;/li&gt;
&lt;li&gt;预期的格式。如一个 string 的格式，它可能需要是一个合法的 email，也可能需要是一个合法的时间字符串，又或者它的长度需要限制在某个范围内。又如一个 int 的值，它的值需要限制在某个范围内。&lt;/li&gt;
&lt;li&gt;一定的约束。这种约束可能需要查询数据库才能知晓，但不能破坏。最常见的是唯一性约束。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="验证操作权限"&gt;验证操作权限&lt;/h3&gt;
&lt;p&gt;第二件事情就是验证操作权限，以防止没有权限的用户对它管辖外的资源造成的破坏。鉴权系统首先需要验证客户端是否提供了有效的身份信息，可选的方案如 jwt. 然后，它需要在两个维度下验证权限：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;操作的维度。这个维度是指，用户是否有对该资源有该操作的权限。&lt;/li&gt;
&lt;li&gt;字段的维度。这个维度是指，用户对哪些字段拥有操作的权限。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="暴露给客户端的数据"&gt;暴露给客户端的数据&lt;/h3&gt;
&lt;p&gt;在安全的接口设计里，我们需要考虑哪些数据是允许暴露给客户端的。这也包括两个维度：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;资源的维度。例如一个管理员只能查看某一个城市下的人口数据，则接口返回中需要筛选出只包含该城市的数据。&lt;/li&gt;
&lt;li&gt;字段的维度。一个例子是，在没有付费的普通用户下，需要限制查看某些字段的内容。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这一节提到了三点常见的失败可能。失败的可能错综复杂，将这三点单独拎出来说的原因是，它们是最常见的。所以，在框架选型和项目构建中，一开始就要将这三点囊括进来。现代的 WEB 框架往往能够采用一套通用的方式处理，包括用户代码和测试代码，算是为我接下来讲框架的内容埋下铺垫。&lt;/p&gt;</description>
      <author>run99</author>
      <pubDate>Mon, 07 Oct 2019 17:47:40 +0800</pubDate>
      <link>https://ruby-china.org/topics/39115</link>
      <guid>https://ruby-china.org/topics/39115</guid>
    </item>
    <item>
      <title>聊聊后端 API 开发的那些事儿</title>
      <description>&lt;p&gt;这是我第一次在 Ruby China 社区发帖。&lt;/p&gt;

&lt;p&gt;本来是想写一本书来定义一套标准的开发模式来的。后来想到自己的这些开发经验在真正的牛人面前根本不值得一提，就及时将这个想法扼杀在摇篮里了。但我依然想记下些什么东西，可能更多的是个人笔记的味道，是我自己对这些年开发体验的总结。同时，也希望我写的东西得到更多的交流，让我更能体系化地理解一些东西。&lt;/p&gt;

&lt;p&gt;我这次要写的，是关于后端 API 开发的。注意，这不是在探讨全栈的开发，而仅仅是后端 API 的设计与实现。这是因为，后端 API 是我平常工作的主要内容。我们公司的技术选型在一开始就选择了前后端分离，而我个人也在很早期的时候就接触到了 REST 的概念。&lt;/p&gt;

&lt;p&gt;我这次写的，是一个系列文章，我觉得一篇文章可能无法涵盖到我要讲的东西。首先，我要总结一下关于 Restful API 设计的本身。这部分内容是与语言无关的，是站在纯设计的角度去思考和阐述。然后，我要说一下有关框架的事情。由于我一直以来都是用 Ruby 开发的，所以我主要讲的就是 Rails 框架和 Grape 框架了。但是，限于篇幅的原因，有些内容可能无法深入展开了，大部分内容都是浅显的。最后要说什么呢？现在还没想好，可能会去实现一套脚手架吧，包含我开发中最常用的模式。&lt;/p&gt;

&lt;p&gt;接下来的更新时间，应该在一周以内更新一篇文章吧。总文章数大致在三到四篇的样子。&lt;/p&gt;

&lt;p&gt;暂时就这些吧。希望能顺利。期待！&lt;/p&gt;</description>
      <author>run99</author>
      <pubDate>Tue, 01 Oct 2019 11:45:56 +0800</pubDate>
      <link>https://ruby-china.org/topics/39100</link>
      <guid>https://ruby-china.org/topics/39100</guid>
    </item>
  </channel>
</rss>
