<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>smartepsh (Peiyuan Wang)</title>
    <link>https://ruby-china.org/smartepsh</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>如何处理数组的 strong params?</title>
      <description>&lt;h3 id="用途:"&gt;用途：&lt;/h3&gt;
&lt;p&gt;批量操作&lt;/p&gt;
&lt;h3 id="参数形式"&gt;参数形式&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;[{a:av1, b:bv1, c:cv1},{a:av2, b:bv2, c:cv2},{a:av3, b:bv3, c:cv3}]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{data: [{a:av1, b:bv1, c:cv1},{a:av2, b:bv2, c:cv2},{a:av3, b:bv3, c:cv3}]}&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="问题"&gt;问题&lt;/h3&gt;
&lt;p&gt;如何使用 strong params 处理传入的参数？&lt;/p&gt;</description>
      <author>smartepsh</author>
      <pubDate>Fri, 02 Dec 2016 11:03:44 +0800</pubDate>
      <link>https://ruby-china.org/topics/31762</link>
      <guid>https://ruby-china.org/topics/31762</guid>
    </item>
    <item>
      <title>求问 create! 报错和 rescue 之间的关系</title>
      <description>&lt;h3 id="前提条件："&gt;前提条件：&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;数据库有一个字段在迁移里设置的为 not null&lt;/li&gt;
&lt;li&gt;该字段并没有在 model 里进行非空约束&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="代码"&gt;代码&lt;/h3&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
  &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="vi"&gt;@model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="vi"&gt;@model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;
  &lt;span class="n"&gt;resuce&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;head: :forbidden&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;其中 params 未包含不为空的那个字段&lt;/p&gt;
&lt;h3 id="猜想"&gt;猜想&lt;/h3&gt;
&lt;p&gt;由于与不为空冲突，所以报错会被捕捉并 render forbidden&lt;/p&gt;
&lt;h3 id="现实"&gt;现实&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;200 OK……（创建成功应该是 201 created）&lt;/li&gt;
&lt;li&gt;数据没有成功保存&lt;/li&gt;
&lt;li&gt;不处理异常去掉 begin/end/rescue 的话，会报错 &lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="疑问"&gt;疑问&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;为什么不会按照猜想走，为什么不会报错？&lt;/li&gt;
&lt;li&gt;为什么渲染了 200 OK……？&lt;/li&gt;
&lt;li&gt;应该怎么做？（除了在 model 里设置不为空外）&lt;/li&gt;
&lt;li&gt;一般开发，是应该是数据库做非空约束，还是在 model 层做约束，还是两者都做？&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;我知道 model 层的非空不会给数据库做非空约束&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <author>smartepsh</author>
      <pubDate>Wed, 12 Oct 2016 17:31:09 +0800</pubDate>
      <link>https://ruby-china.org/topics/31300</link>
      <guid>https://ruby-china.org/topics/31300</guid>
    </item>
    <item>
      <title>gem ’ pg ‘的文档不见了。。。谁有备份麻烦发一份好吗</title>
      <description>&lt;p&gt;官网上写的&lt;code&gt;http://deveiate.org/code/pg&lt;/code&gt;&lt;a href="http://deveiate.org/code/pg" rel="nofollow" target="_blank" title=""&gt;文档&lt;/a&gt;的网站被删除了，现在有点问题想看文档。。。。不知道谁知道哪里有或者存的有备份的麻烦发一份给我。。。谢谢。。&lt;img title=":sob:" alt="😭" src="https://twemoji.ruby-china.com/2/svg/1f62d.svg" class="twemoji"&gt; &lt;/p&gt;</description>
      <author>smartepsh</author>
      <pubDate>Thu, 22 Sep 2016 11:55:26 +0800</pubDate>
      <link>https://ruby-china.org/topics/31136</link>
      <guid>https://ruby-china.org/topics/31136</guid>
    </item>
    <item>
      <title>关于 jsonapi 或者说 RESTful API 的批量操作疑问</title>
      <description>&lt;p&gt;最近在做基于 jsonapi 的设计，本来的设想倒是挺不错，但是由于客户可能有工厂较大批量的存在，导致数据可能有批量操作的发生，尤其是在&lt;code&gt;create&lt;/code&gt;操作上。&lt;/p&gt;

&lt;p&gt;想问下大家，有没有什么较好的解决办法？&lt;/p&gt;

&lt;p&gt;jsonapi 里的 post 操作一次仅允许提交一个数据。我的想法是根据需要批量操作的动作创建单独的控制器，比如&lt;code&gt;creates&lt;/code&gt;,然后定义&lt;code&gt;type&lt;/code&gt;和&lt;code&gt;value&lt;/code&gt;等字段，我在控制器里解析操作。插入对应的表中。&lt;/p&gt;

&lt;p&gt;但是感觉有点麻烦，不知道还有没有好的办法？&lt;/p&gt;</description>
      <author>smartepsh</author>
      <pubDate>Thu, 18 Aug 2016 14:25:21 +0800</pubDate>
      <link>https://ruby-china.org/topics/30846</link>
      <guid>https://ruby-china.org/topics/30846</guid>
    </item>
    <item>
      <title>如何获取 post 中的 jsonapi 参数</title>
      <description>&lt;p&gt;假设请求参数如下:
&lt;code&gt;curl -d '{ "data": { "type":"music","attributes":{"name": "acmes"}}} --header "Accept: application/vnd.api+json" --header "Content-Type: application/vnd.api+json"&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;也可能在参数中存在一个对象集合，包含多个对象&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;在 controller 中如何获取其中的参数呢？有没有什么方法或 gem 可以方便的获取其中的参数或参数组？&lt;/p&gt;

&lt;p&gt;我现在只能用&lt;code&gt;params[:data][:attributes][:name]&lt;/code&gt;这样来直接拿参数...&lt;/p&gt;</description>
      <author>smartepsh</author>
      <pubDate>Wed, 10 Aug 2016 11:31:55 +0800</pubDate>
      <link>https://ruby-china.org/topics/30787</link>
      <guid>https://ruby-china.org/topics/30787</guid>
    </item>
    <item>
      <title>AMS 针对 JSONAPI 媒体类型的更改问题</title>
      <description>&lt;p&gt;在使用 AMS 的过程中，发现返回的媒体类型一直是&lt;code&gt;application/json&lt;/code&gt;,而 JSONAPI 的文档中明确说明他们已注册的媒体类型是&lt;code&gt;vnd.api+json&lt;/code&gt;,作为一名强迫症患者，这是不能忍的......&lt;/p&gt;
&lt;h3 id="解决方案一"&gt;解决方案一&lt;/h3&gt;
&lt;p&gt;在 initializers 内，&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;api_mime_types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%W(
  application/vnd.api_json
  text/x-json
  applicaiton/json
)&lt;/span&gt;

&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s1"&gt;'applicaiton/vnd.api+json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_mime_types&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;效果不错...还简便，不过是替换方案...&lt;/p&gt;
&lt;h3 id="解决方案二"&gt;解决方案二&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://www.benjaminfleischer.com/journey-of-a-media-type-in-rails-part-1" rel="nofollow" target="_blank" title=""&gt;这篇文章&lt;/a&gt;里的内容，注册一个新的媒体类型。&lt;/p&gt;
&lt;h2 id="问题"&gt;问题&lt;/h2&gt;
&lt;p&gt;是不是正规来说，应该使用方法二？或者有没有什么其他的办法？(不要说不使用 AMS...)
&lt;a href="/jicheng1014" class="user-mention" title="@jicheng1014"&gt;&lt;i&gt;@&lt;/i&gt;jicheng1014&lt;/a&gt; 又 at 你了...&lt;img title=":joy:" alt="😂" src="https://twemoji.ruby-china.com/2/svg/1f602.svg" class="twemoji"&gt; &lt;/p&gt;</description>
      <author>smartepsh</author>
      <pubDate>Thu, 04 Aug 2016 17:26:22 +0800</pubDate>
      <link>https://ruby-china.org/topics/30731</link>
      <guid>https://ruby-china.org/topics/30731</guid>
    </item>
    <item>
      <title>使用 Rails 5 创建 API-Only 应用</title>
      <description>&lt;h2 id="使用Rails5的API-Only…...已使AMS返回的媒体类型为注册的标准类型:vnd.api+json"&gt;使用 Rails5 的 API-Only…...已使 AMS 返回的媒体类型为注册的标准类型:vnd.api+json&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;根据&lt;a href="https://ruby-china.org/topics/25822" title=""&gt;使用 Rails 构建 API 实践&lt;/a&gt;帖子改写，使用 Active Model Serializers 输出
因为是纯新手，所以想先盯着输出这里的东西，所以其他的东西，我就抱歉啦，标题代码逻辑什么的我都直接照抄啦，请不要告我侵权ಠ౪ಠ
全部完成后会 at 原作者的 &lt;a href="/kayakjiang" class="user-mention" title="@kayakjiang"&gt;&lt;i&gt;@&lt;/i&gt;kayakjiang&lt;/a&gt; &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="建议和原文同时食用,好多思路部分的东西我没有搬过来..."&gt;建议和原文同时食用，好多思路部分的东西我没有搬过来...&lt;/h2&gt;
&lt;p&gt;完成原文前 5 章，后续不打算做了，一个是因为后面只是部分 gem 的使用，还有&lt;code&gt;版本控制&lt;/code&gt;还没找到合适的解决方案。&lt;/p&gt;
&lt;h2 id="写在前面,关于AMS中JSONAPI的媒体类型"&gt;写在前面，关于 AMS 中 JSONAPI 的媒体类型&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://ruby-china.org/topics/30731" title=""&gt;单独发过一个帖子&lt;/a&gt;,在使用 AMS 做 JSONAPI 的过程中，我不知是不是因为自己不会使用的关系，导致返回的媒体类型为 application/json，而不是 JSONAPI 所注册过的 vpn.api+json.所以暂时通过以下方法进行更改：&lt;/p&gt;
&lt;h5 id="在initializer下:"&gt;在 initializer 下：&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;api_mime_types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%W(
  application/vnd.api_json
  text/x-json
  applicaiton/json
)&lt;/span&gt;

&lt;span class="no"&gt;Mime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt; &lt;span class="s1"&gt;'applicaiton/vnd.api+json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_mime_types&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;即可将返回的媒体类型修改为正确的 vpn.api+json，但是总感觉这种方法不是太合适。&lt;/p&gt;
&lt;h2 id="首先建立一个项目:"&gt;首先建立一个项目：&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;rails new demo --api&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;所需 Gem 会逐渐列出&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="一.加入第一个API resource"&gt;一。加入第一个 API resource&lt;/h2&gt;&lt;h3 id="配置路由"&gt;配置路由&lt;/h3&gt;&lt;h5 id="config/routes.rb"&gt;config/routes.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:show&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:destroy&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="UsersController"&gt;UsersController&lt;/h3&gt;
&lt;p&gt;生成控制器：&lt;code&gt;rails g controller usesrs&lt;/code&gt;&lt;/p&gt;
&lt;h5 id="app/controllers/users_controller.rb"&gt;app/controllers/users_controller.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsersController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;BaseController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
    &lt;span class="vi"&gt;@user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="vi"&gt;@user&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&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;h3 id="ActiveModel::Serializer"&gt;ActiveModel::Serializer&lt;/h3&gt;
&lt;p&gt;安装 gem:&lt;code&gt;gem 'active_model_serializers'&lt;/code&gt;&lt;code&gt;bundle install&lt;/code&gt;&lt;/p&gt;

&lt;hr&gt;
&lt;h4 id="共有3种输出格式(Adapter):"&gt;共有 3 种输出格式 (Adapter):&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;:attributes&lt;/code&gt;(default)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:json_api&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="修改默认输出格式为json_api(按需更改)"&gt;修改默认输出格式为 json_api(按需更改)&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;也可在具体文件内单独指定 Adapter 类型 (如：&lt;code&gt;adapter: :json_api&lt;/code&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h5 id="config/initializers/ams_adapter.rb(文件名任意,只要在initializers文件夹下即可)"&gt;config/initializers/ams_adapter.rb(文件名任意,只要在initializers文件夹下即可)&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ActiveModel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Serializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;adapter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:json_api&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="UserSerializer"&gt;UserSerializer&lt;/h3&gt;
&lt;p&gt;生成 Serializer:&lt;code&gt;rails g serializer user&lt;/code&gt;&lt;/p&gt;
&lt;h5 id="app/serializers/user_serializer.rb"&gt;app/serializers/user_serializer.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Api::V1::UserSerializer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveModel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Serializer&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="ss"&gt;:activated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="ss"&gt;:admin&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="User模型和users表"&gt;User 模型和 users 表&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;rails g model User&lt;/code&gt;&lt;/p&gt;
&lt;h5 id="db/migrate/xxxxxx_create_users.rb"&gt;db/migrate/xxxxxx_create_users.rb&lt;/h5&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class CreateUsers &amp;lt; ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :email
      t.string :name
      t.datetime :activated
      t.boolean :admin, default: false
      t.timestamps null: false
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;数据迁移：&lt;code&gt;rails db:migrate&lt;/code&gt;
种子数据：&lt;/p&gt;
&lt;h5 id="db/seeds.rb"&gt;db/seeds.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="s1"&gt;'test00@mail.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'test00'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;activated: &lt;/span&gt;&lt;span class="no"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;admin: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="s1"&gt;'test01@mail.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'test01'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;activated: &lt;/span&gt;&lt;span class="no"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;admin: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="s1"&gt;'test02@mail.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'test02'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;activated: &lt;/span&gt;&lt;span class="no"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;admin: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="s1"&gt;'test03@mail.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'test03'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;activated: &lt;/span&gt;&lt;span class="no"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;admin: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="s1"&gt;'test04@mail.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'test04'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;activated: &lt;/span&gt;&lt;span class="no"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;admin: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;}])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;创建种子数据：&lt;code&gt;rails db:seed&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="测试一下（并不是专业的测试）"&gt;测试一下（并不是专业的测试）&lt;/h3&gt;
&lt;p&gt;启动 rails 服务器：&lt;code&gt;rails s&lt;/code&gt;
使用 curl 请求：&lt;code&gt;curl -i http://localhost:3000/users/1.json&lt;/code&gt;
得到结果 (3 种 Adapter 为显示区别均已列出，后续部分只输出 JSON_API)&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#JSON_API&lt;/span&gt;
HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;block
X-Content-Type-Options: nosniff
Content-Type: application/json&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;utf-8
ETag: W/&lt;span class="s2"&gt;"26bfbe5f8e1fb23b6069c3625394a924"&lt;/span&gt;
Cache-Control: max-age&lt;span class="o"&gt;=&lt;/span&gt;0, private, must-revalidate
X-Request-Id: 75723ba3-d2e6-4099-9ffa-be3daccf673b
X-Runtime: 0.002995
Transfer-Encoding: chunked

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"data"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:&lt;span class="s2"&gt;"1"&lt;/span&gt;,&lt;span class="s2"&gt;"type"&lt;/span&gt;:&lt;span class="s2"&gt;"users"&lt;/span&gt;,&lt;span class="s2"&gt;"attributes"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;:&lt;span class="s2"&gt;"test00"&lt;/span&gt;,&lt;span class="s2"&gt;"email"&lt;/span&gt;:&lt;span class="s2"&gt;"test00@mail.com"&lt;/span&gt;,&lt;span class="s2"&gt;"activated"&lt;/span&gt;:&lt;span class="s2"&gt;"2016-07-20T05:20:11.904Z"&lt;/span&gt;,&lt;span class="s2"&gt;"admin"&lt;/span&gt;:false&lt;span class="o"&gt;}}}&lt;/span&gt;


&lt;span class="c"&gt;#JSON&lt;/span&gt;
HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;block
X-Content-Type-Options: nosniff
Content-Type: application/json&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;utf-8
ETag: W/&lt;span class="s2"&gt;"5b726a6e35cc53e1e21dab387723cf2d"&lt;/span&gt;
Cache-Control: max-age&lt;span class="o"&gt;=&lt;/span&gt;0, private, must-revalidate
X-Request-Id: ac41ba6b-269d-4d9e-9d10-1b276fcced47
X-Runtime: 0.126139
Transfer-Encoding: chunked

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:1,&lt;span class="s2"&gt;"name"&lt;/span&gt;:&lt;span class="s2"&gt;"test00"&lt;/span&gt;,&lt;span class="s2"&gt;"email"&lt;/span&gt;:&lt;span class="s2"&gt;"test00@mail.com"&lt;/span&gt;,&lt;span class="s2"&gt;"activated"&lt;/span&gt;:&lt;span class="s2"&gt;"2016-07-20T05:20:11.904Z"&lt;/span&gt;,&lt;span class="s2"&gt;"admin"&lt;/span&gt;:false&lt;span class="o"&gt;}}&lt;/span&gt;


&lt;span class="c"&gt;#attributes&lt;/span&gt;
HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;block
X-Content-Type-Options: nosniff
Content-Type: application/json&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;utf-8
ETag: W/&lt;span class="s2"&gt;"f90830c5ced0658dc653b158c52ffb3d"&lt;/span&gt;
Cache-Control: max-age&lt;span class="o"&gt;=&lt;/span&gt;0, private, must-revalidate
X-Request-Id: 3fc863d4-2b9f-497b-94dc-adb840af82e3
X-Runtime: 0.124103
Transfer-Encoding: chunked

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:1,&lt;span class="s2"&gt;"name"&lt;/span&gt;:&lt;span class="s2"&gt;"test00"&lt;/span&gt;,&lt;span class="s2"&gt;"email"&lt;/span&gt;:&lt;span class="s2"&gt;"test00@mail.com"&lt;/span&gt;,&lt;span class="s2"&gt;"activated"&lt;/span&gt;:&lt;span class="s2"&gt;"2016-07-20T05:20:11.904Z"&lt;/span&gt;,&lt;span class="s2"&gt;"admin"&lt;/span&gt;:false&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="二.增加认证"&gt;二。增加认证&lt;/h2&gt;&lt;h3 id="给User模型增加authentication_token属性"&gt;给 User 模型增加 authentication_token 属性&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;rails g migration add_authentication_token_to_users&lt;/code&gt;&lt;/p&gt;
&lt;h5 id="db/migrate/xxxxxx_add_authentication_token_to_users.rb"&gt;db/migrate/xxxxxx_add_authentication_token_to_users.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddAuthenticationTokenToUsers&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt;
    &lt;span class="n"&gt;add_column&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:authentication_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&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;code&gt;rails db:migrate&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="生成authentication_token"&gt;生成 authentication_token&lt;/h3&gt;&lt;h5 id="app/model/user.rb"&gt;app/model/user.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;before_create&lt;/span&gt; &lt;span class="ss"&gt;:generate_authentication_token&lt;/span&gt;
  &lt;span class="n"&gt;has_secure_password&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_authentication_token&lt;/span&gt;
    &lt;span class="kp"&gt;loop&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authentication_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SecureRandom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;authentication_token: &lt;/span&gt;&lt;span class="n"&gt;authentication_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reset_auth_token!&lt;/span&gt;
    &lt;span class="n"&gt;generate_authentication_token&lt;/span&gt;
    &lt;span class="n"&gt;save&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;h3 id="sessions endpoint"&gt;sessions endpoint&lt;/h3&gt;
&lt;p&gt;生成 sessions 控制器：&lt;code&gt;rails g controller sessions&lt;/code&gt;&lt;/p&gt;
&lt;h5 id="app/controllers/sessions_controller.rb"&gt;app/controllers/sessions_controller.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SessionsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
    &lt;span class="vi"&gt;@user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="n"&gt;create_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@user&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="vi"&gt;@user.authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;create_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:password&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@user&lt;/span&gt;
      &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;serializer: &lt;/span&gt;&lt;span class="no"&gt;SessionSerializer&lt;/span&gt;
      &lt;span class="c1"&gt;#必须显示指定serializer,不知原因,求解释&lt;/span&gt;
      &lt;span class="c1"&gt;#应该是根据对象类型自动匹配到user上了&lt;/span&gt;
      &lt;span class="c1"&gt;#也可以直接修改UserSerializer添加token字段&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;api_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_params&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&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;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="ss"&gt;:password&lt;/span&gt;&lt;span class="p"&gt;)&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;h3 id="给User模型增加password"&gt;给 User 模型增加 password&lt;/h3&gt;
&lt;p&gt;安装 Gem:&lt;code&gt;gem 'bcrypt'&lt;/code&gt;&lt;code&gt;bundle install&lt;/code&gt;&lt;/p&gt;
&lt;h5 id="app/models/user.rb"&gt;app/models/user.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;has_secure_password&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;增加 password_digest 属性：&lt;code&gt;rails g migration add_password_digest_to_users&lt;/code&gt;&lt;/p&gt;
&lt;h5 id="db/migrate/xxxxxx_add_password_digest_to_users.rb"&gt;db/migrate/xxxxxx_add_password_digest_to_users.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddPasswordDigestToUsers&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt;
    &lt;span class="n"&gt;add_column&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:password_digest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&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;code&gt;rails db:migrate&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="给已存在的测试用户增加密码和token"&gt;给已存在的测试用户增加密码和 token&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;rails c&lt;/code&gt;打开终端，执行：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'123123'&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;reset_auth_token!&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="实现和current_user相关的方法"&gt;实现和 current_user 相关的方法&lt;/h3&gt;&lt;h5 id="app/controllers/application_controller.rb"&gt;app/controllers/application_controller.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;API&lt;/span&gt;
  &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:current_user&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="实现api_error方法"&gt;实现 api_error 方法&lt;/h3&gt;&lt;h5 id="app/controllers/application_controller.rb"&gt;app/controllers/application_controller.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;lass&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;API&lt;/span&gt;
&lt;span class="c1"&gt;#原文使用render nothing: true方法&lt;/span&gt;
&lt;span class="c1"&gt;#运行时报warning说5.1要移除nothing了&lt;/span&gt;
&lt;span class="c1"&gt;#这里改为官方建议的head方法&lt;/span&gt;
  &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;api_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
  &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;head: :unauthorized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="o"&gt;+&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;h3 id="SessionSerializer"&gt;SessionSerializer&lt;/h3&gt;
&lt;p&gt;生成 Serializer:&lt;code&gt;rails g serializer session&lt;/code&gt;&lt;/p&gt;
&lt;h5 id="app/serializers/session_serializer.rb"&gt;app/serializers/session_serializer.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SessionSerializer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveModel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Serializer&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="ss"&gt;:admin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="ss"&gt;:token&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;token&lt;/span&gt;
    &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authentication_token&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;如上文所说，可以不要这个 Serializer，直接修改 UserSerializer 就可以&lt;/p&gt;
&lt;h3 id="配置和session相关的路由"&gt;配置和 session 相关的路由&lt;/h3&gt;&lt;h5 id="config/routes.rb"&gt;config/routes.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:sessions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="又一个不正经的测试"&gt;又一个不正经的测试&lt;/h3&gt;&lt;h4 id="使用正确的数据获取token"&gt;使用正确的数据获取 token&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;url -i -X POST -d "user[email]=test00@mail.com&amp;amp;user[password]=123123" http://localhost:3000/sessions&lt;/code&gt;
得到结果：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;block
X-Content-Type-Options: nosniff
Content-Type: application/json&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;utf-8
ETag: W/&lt;span class="s2"&gt;"b71c6480196ab554d31d298a293adc75"&lt;/span&gt;
Cache-Control: max-age&lt;span class="o"&gt;=&lt;/span&gt;0, private, must-revalidate
X-Request-Id: 0e0507cf-81a1-4972-aaa0-764945bd820c
X-Runtime: 0.093730
Transfer-Encoding: chunked

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"data"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:&lt;span class="s2"&gt;"1"&lt;/span&gt;,&lt;span class="s2"&gt;"type"&lt;/span&gt;:&lt;span class="s2"&gt;"users"&lt;/span&gt;,&lt;span class="s2"&gt;"attributes"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;:&lt;span class="s2"&gt;"test00"&lt;/span&gt;,&lt;span class="s2"&gt;"admin"&lt;/span&gt;:false,&lt;span class="s2"&gt;"token"&lt;/span&gt;:&lt;span class="s2"&gt;"zKif6laUSlSJY4EDi3owNcJEFPKm87bvFKpXMLTROMRT2DZrbtsBMBWJalab6wS96a3ntR7yTar5Z3yVYEDeOg=="&lt;/span&gt;&lt;span class="o"&gt;}}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;顺利得到了 token&lt;/p&gt;

&lt;hr&gt;
&lt;h4 id="使用不正确的数据"&gt;使用不正确的数据&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;curl -i -X POST -d "user[email]=test00@mail.com&amp;amp;user[password]=123" http://localhost:3000/sessions&lt;/code&gt;
密码改为错误的"123"&lt;/p&gt;

&lt;p&gt;得到结果：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;HTTP/1.1 401 Unauthorized
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;block
X-Content-Type-Options: nosniff
Content-Type: text/plain&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;utf-8
Cache-Control: no-cache
X-Request-Id: 99856444-b644-43d4-99db-34c1a2afac4d
X-Runtime: 0.068964
Transfer-Encoding: chunked
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们得到了 401 Unauthorized 错误。&lt;/p&gt;
&lt;h2 id="三.Authenticate User"&gt;三.Authenticate User&lt;/h2&gt;&lt;h3 id="使用token和email看能否识别出用户"&gt;使用 token 和 email 看能否识别出用户&lt;/h3&gt;
&lt;p&gt;首先实现 authenticate_user! 方法&lt;/p&gt;
&lt;h5 id="app/controllers/application.rb"&gt;app/controllers/application.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;API&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;authenticate_user!&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;   &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HttpAuthentication&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;token_and_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="n"&gt;user_email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blank?&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="ss"&gt;:options&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;+&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;user_email&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="n"&gt;user_email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SecurityUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;secure_compare&lt;/span&gt;&lt;span class="p"&gt;(&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;authentication_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;unauthenticated!&lt;/span&gt;
&lt;span class="o"&gt;+&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;然后实现 update API，并加入&lt;code&gt;before_action :authenticate_user!, only:［:update]&lt;/code&gt;&lt;/p&gt;
&lt;h5 id="app/conctrollers/users_controller.rb"&gt;app/conctrollers/users_controller.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;lass&lt;/span&gt; &lt;span class="no"&gt;UsersController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="ss"&gt;:authenticate_user!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:update&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="o"&gt;+&lt;/span&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="vi"&gt;@user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="vi"&gt;@user.update_attributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;update_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="vi"&gt;@user&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_params&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&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;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;)&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;h5 id="app/controllers/application_controller.rb"&gt;app/controllers/application_controller.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;API&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;unauthenticated!&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;   &lt;span class="n"&gt;api_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;+&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;h4 id="不正经的测试"&gt;不正经的测试&lt;/h4&gt;
&lt;p&gt;使用 curl 提交修改 2 号用户的用户名:
&lt;code&gt;curl -i -X PUT -d "user[name]=gg-user" --header "Authorization: Token token=4Vrayf/5g+720JRWQV4BQkk+uT5UeBmgR6LsacBNJfCwXWI3rgNknN97pyxsg8YWJIm0oNq4vKdqIhcatcHhsQ==, email=test01@mail.com" http://localhost:3000/users/2&lt;/code&gt;
  得到结果：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;block
X-Content-Type-Options: nosniff
Content-Type: application/json&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;utf-8
ETag: W/&lt;span class="s2"&gt;"d6a64e0caf907e3c60b69c31c2a9d23d"&lt;/span&gt;
Cache-Control: max-age&lt;span class="o"&gt;=&lt;/span&gt;0, private, must-revalidate
X-Request-Id: 054a8ccc-ac68-4cd6-8fb1-6ac33ee191f4
X-Runtime: 0.611735
Transfer-Encoding: chunked

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"data"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:&lt;span class="s2"&gt;"2"&lt;/span&gt;,&lt;span class="s2"&gt;"type"&lt;/span&gt;:&lt;span class="s2"&gt;"users"&lt;/span&gt;,&lt;span class="s2"&gt;"attributes"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;:&lt;span class="s2"&gt;"gg-user"&lt;/span&gt;,&lt;span class="s2"&gt;"email"&lt;/span&gt;:&lt;span class="s2"&gt;"test01@mail.com"&lt;/span&gt;,&lt;span class="s2"&gt;"activated"&lt;/span&gt;:&lt;span class="s2"&gt;"2016-07-20T05:20:11.905Z"&lt;/span&gt;,&lt;span class="s2"&gt;"admin"&lt;/span&gt;:false,&lt;span class="s2"&gt;"authentication-token"&lt;/span&gt;:&lt;span class="s2"&gt;"4Vrayf/5g+720JRWQV4BQkk+uT5UeBmgR6LsacBNJfCwXWI3rgNknN97pyxsg8YWJIm0oNq4vKdqIhcatcHhsQ=="&lt;/span&gt;&lt;span class="o"&gt;}}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到用户名已经被更改了;再使用错误的 token 看看效果：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;HTTP/1.1 401 Unauthorized
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;block
X-Content-Type-Options: nosniff
Content-Type: text/plain&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;utf-8
Cache-Control: no-cache
X-Request-Id: ac4241f9-7125-4142-a281-3ba060061b9f
X-Runtime: 0.002108
Transfer-Encoding: chunked
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;返回 401 错误，如愿。&lt;/p&gt;
&lt;h2 id="四.增加授权Authorization"&gt;四。增加授权 Authorization&lt;/h2&gt;&lt;h3 id="使用pundit进行权限认证"&gt;使用 pundit 进行权限认证&lt;/h3&gt;
&lt;p&gt;安装 Gem: &lt;code&gt;gem 'pundit'&lt;/code&gt;&lt;code&gt;bundle install&lt;/code&gt;&lt;/p&gt;
&lt;h5 id="添加引用app/controllers/application.rb"&gt;添加引用 app/controllers/application.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;API&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Pundit&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行安装：&lt;code&gt;rails g pundit:install&lt;/code&gt;
会生成一个&lt;code&gt;app/policies/application_policy.rb&lt;/code&gt;文件&lt;/p&gt;
&lt;h5 id="app/controllers/application.rb"&gt;app/controllers/application.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;API&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Pundit&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将 policies 目录放入 rails 自动加载路径中&lt;/p&gt;
&lt;h5 id="config/application.rb"&gt;config/application.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ApiDemo&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Application&lt;/span&gt;
  &lt;span class="o"&gt;+&lt;/span&gt;  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;autoload_paths&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app/policies'&lt;/span&gt;&lt;span class="p"&gt;)&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;h3 id="创建和User相关的权限机制"&gt;创建和 User 相关的权限机制&lt;/h3&gt;&lt;h5 id="app/policies/user_policy.rb"&gt;app/policies/user_policy.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserPolicy&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationPolicy&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show?&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create?&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update?&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;if&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;admin?&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&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;id&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;destroy?&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;if&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;admin?&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&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;id&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Scope&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationPolicy&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Scope&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;
      &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;
    &lt;span class="k"&gt;end&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;h3 id="使用UserPolicy"&gt;使用 UserPolicy&lt;/h3&gt;&lt;h5 id="app/controllers/users_controller.rb"&gt;app/controllers/users_controller.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsersController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;
    &lt;span class="vi"&gt;@user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="c1"&gt;#return api_error(status: 403) if !UserPolicy.new(current_user, @user).update?&lt;/span&gt;
    &lt;span class="c1"&gt;#或可使用此方法,无需捕获异常&lt;/span&gt;
    &lt;span class="n"&gt;authorize&lt;/span&gt; &lt;span class="vi"&gt;@user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:update?&lt;/span&gt;
    &lt;span class="vi"&gt;@user.update_attributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;update_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="vi"&gt;@user&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;h5 id="app/controllers/application_controller.rb"&gt;app/controllers/application_controller.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;API&lt;/span&gt;

&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;rescue_from&lt;/span&gt; &lt;span class="no"&gt;Pundit&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;NotAuthorizedError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;with: :deny_access&lt;/span&gt;

&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;deny_access&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;   &lt;span class="n"&gt;api_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;+&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;h4 id="测试依然不正经"&gt;测试依然不正经&lt;/h4&gt;
&lt;p&gt;使用 user/1 的数据去更新 user/2 的:
&lt;code&gt;curl -i -X PUT -d "user[name]=gg22-user" --header "Authorization: Token token=zKif6laUSlSJY4EDi3owNcJEFPKm87bvFKpXMLTROMRT2DZrbtsBMBWJalab6wS96a3ntR7yTar5Z3yVYEDeOg==, email=test00@mail.com" http://localhost:3000/users/2&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;得到结果：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;HTTP/1.1 403 Forbidden
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;block
X-Content-Type-Options: nosniff
Content-Type: text/plain&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;utf-8
Cache-Control: no-cache
X-Request-Id: 3263a340-13af-44ab-ad04-872a015f79fe
X-Runtime: 0.018907
Transfer-Encoding: chunked
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;返回 403，如愿。&lt;/p&gt;
&lt;h2 id="五.微博模型及分页"&gt;五。微博模型及分页&lt;/h2&gt;&lt;h3 id="建立micropost模型"&gt;建立 micropost 模型&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;rails g model Micropost&lt;/code&gt;&lt;/p&gt;
&lt;h5 id="db/migrate/xxx_create_microposts.rb"&gt;db/migrate/xxx_create_microposts.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateMicroposts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt;
    &lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="ss"&gt;:microposts&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;
      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;
      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;integer&lt;/span&gt; &lt;span class="ss"&gt;:user_id&lt;/span&gt;
      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timestamps&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="ss"&gt;:false&lt;/span&gt;
    &lt;span class="k"&gt;end&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;code&gt;rails db:migrate&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="准备100条微博数据:"&gt;准备 100 条微博数据：&lt;/h3&gt;&lt;h5 id="db/seeds.rb"&gt;db/seeds.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;   &lt;span class="no"&gt;Micropost&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;user_id: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;title: &lt;/span&gt;&lt;span class="s2"&gt;"title-&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;content: &lt;/span&gt;&lt;span class="s2"&gt;"content-&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;rails db:seed&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="MicropostsController"&gt;MicropostsController&lt;/h3&gt;
&lt;p&gt;生成控制器：&lt;code&gt;rails g controller microposts&lt;/code&gt;
配置路由：&lt;/p&gt;
&lt;h5 id="config/routes.rb"&gt;config/routes.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="ss"&gt;path: &lt;/span&gt;&lt;span class="s1"&gt;'/user/:user_id'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;   &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:microposts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;only&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;+&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;h4 id="定义index方法"&gt;定义 index 方法&lt;/h4&gt;&lt;h5 id="app/controllers/microposts_controller.rb"&gt;app/controllers/microposts_controller.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MicropostsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:user_id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="c1"&gt;#使用分页函数&lt;/span&gt;
    &lt;span class="vi"&gt;@microposts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&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;microposts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;#添加meta数据方法&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="vi"&gt;@microposts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;meta: &lt;/span&gt;&lt;span class="n"&gt;paginate_meta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@microposts&lt;/span&gt;&lt;span class="p"&gt;)&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;h3 id="分页"&gt;分页&lt;/h3&gt;
&lt;p&gt;使用 kaminair 进行分页
安装 Gem:&lt;code&gt;gem 'kaminari'&lt;/code&gt;&lt;code&gt;bundle install&lt;/code&gt;&lt;/p&gt;
&lt;h4 id="实现paginate和meta数据方法"&gt;实现 paginate 和 meta 数据方法&lt;/h4&gt;&lt;h5 id="app/controllers/application_controller.rb"&gt;app/controllers/application_controller.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;API&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;paginate_meta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;   &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;     &lt;span class="ss"&gt;current_page: &lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;     &lt;span class="ss"&gt;next_page: &lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next_page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;prev_page: &lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prev_page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;     &lt;span class="ss"&gt;total_pages: &lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;total_pages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;     &lt;span class="ss"&gt;total_count: &lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;total_count&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;#对于新手(我)来说,关于参数的深坑...&lt;/span&gt;
&lt;span class="c1"&gt;#构造url的时候,参数之间请添加转义符连接,不要单独使用&amp;amp;&lt;/span&gt;
&lt;span class="c1"&gt;#不要问我怎么知道的...😂&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;   &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:per_page&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;     &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;per&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:per_page&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;   &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;
&lt;span class="o"&gt;+&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;h3 id="MicropostSerializer"&gt;MicropostSerializer&lt;/h3&gt;
&lt;p&gt;生成 Serializer:&lt;code&gt;rails g serializer micropost&lt;/code&gt;&lt;/p&gt;
&lt;h5 id="app/serializer/MicropostSerializer.rb"&gt;app/serializer/MicropostSerializer.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MicropostSerializer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveModel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Serializer&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="ss"&gt;:content&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="添加关联"&gt;添加关联&lt;/h4&gt;&lt;h5 id="app/models/user.rb"&gt;app/models/user.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:microposts&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h5 id="app/model/micropost.rb"&gt;app/model/micropost.rb&lt;/h5&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Micropost&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:users&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="测试依旧不正经,json数据经过在线工具添加缩进"&gt;测试依旧不正经，json 数据经过在线工具添加缩进&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;curl -i -X GET http://localhost:3000/user/1/microposts?per_page=3&lt;/code&gt;
得到结果：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;block
X-Content-Type-Options: nosniff
Content-Type: application/json&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;utf-8
ETag: W/&lt;span class="s2"&gt;"c1d821a3d3a878d1d86b3819c8589f39"&lt;/span&gt;
Cache-Control: max-age&lt;span class="o"&gt;=&lt;/span&gt;0, private, must-revalidate
X-Request-Id: 4f4a6d88-8041-4ac3-ad10-02a3ed4bf886
X-Runtime: 0.033613
Transfer-Encoding: chunked

&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"data"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"id"&lt;/span&gt;: &lt;span class="s2"&gt;"1"&lt;/span&gt;, 
      &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"microposts"&lt;/span&gt;, 
      &lt;span class="s2"&gt;"attributes"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"title"&lt;/span&gt;: &lt;span class="s2"&gt;"title-0"&lt;/span&gt;, 
        &lt;span class="s2"&gt;"content"&lt;/span&gt;: &lt;span class="s2"&gt;"content-0"&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;, 
    &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"id"&lt;/span&gt;: &lt;span class="s2"&gt;"2"&lt;/span&gt;, 
      &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"microposts"&lt;/span&gt;, 
      &lt;span class="s2"&gt;"attributes"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"title"&lt;/span&gt;: &lt;span class="s2"&gt;"title-1"&lt;/span&gt;, 
        &lt;span class="s2"&gt;"content"&lt;/span&gt;: &lt;span class="s2"&gt;"content-1"&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;, 
    &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"id"&lt;/span&gt;: &lt;span class="s2"&gt;"3"&lt;/span&gt;, 
      &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"microposts"&lt;/span&gt;, 
      &lt;span class="s2"&gt;"attributes"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"title"&lt;/span&gt;: &lt;span class="s2"&gt;"title-2"&lt;/span&gt;, 
        &lt;span class="s2"&gt;"content"&lt;/span&gt;: &lt;span class="s2"&gt;"content-2"&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;]&lt;/span&gt;, 
  &lt;span class="s2"&gt;"links"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"self"&lt;/span&gt;: &lt;span class="s2"&gt;"http://localhost:3000/user/1/microposts?page%5Bnumber%5D=1&amp;amp;page%5Bsize%5D=3&amp;amp;per_page=3"&lt;/span&gt;, 
    &lt;span class="s2"&gt;"next"&lt;/span&gt;: &lt;span class="s2"&gt;"http://localhost:3000/user/1/microposts?page%5Bnumber%5D=2&amp;amp;page%5Bsize%5D=3&amp;amp;per_page=3"&lt;/span&gt;, 
    &lt;span class="s2"&gt;"last"&lt;/span&gt;: &lt;span class="s2"&gt;"http://localhost:3000/user/1/microposts?page%5Bnumber%5D=34&amp;amp;page%5Bsize%5D=3&amp;amp;per_page=3"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;, 
  &lt;span class="s2"&gt;"meta"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"current-page"&lt;/span&gt;: 1, 
    &lt;span class="s2"&gt;"next-page"&lt;/span&gt;: 2, 
    &lt;span class="s2"&gt;"prev-page"&lt;/span&gt;: null, 
    &lt;span class="s2"&gt;"total-pages"&lt;/span&gt;: 34, 
    &lt;span class="s2"&gt;"total-count"&lt;/span&gt;: 100
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;curl -i -X GET http://localhost:3000/user/1/microposts?per_page=3\&amp;amp;page=8&lt;/code&gt;
得到结果：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;block
X-Content-Type-Options: nosniff
Content-Type: application/json&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;utf-8
ETag: W/&lt;span class="s2"&gt;"27662403e9ec3ef2fca76266b97cbdb6"&lt;/span&gt;
Cache-Control: max-age&lt;span class="o"&gt;=&lt;/span&gt;0, private, must-revalidate
X-Request-Id: 1f17e152-b4a7-4176-aec9-915a689bab10
X-Runtime: 0.006062
Transfer-Encoding: chunked

&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"data"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"id"&lt;/span&gt;: &lt;span class="s2"&gt;"22"&lt;/span&gt;, 
      &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"microposts"&lt;/span&gt;, 
      &lt;span class="s2"&gt;"attributes"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"title"&lt;/span&gt;: &lt;span class="s2"&gt;"title-21"&lt;/span&gt;, 
        &lt;span class="s2"&gt;"content"&lt;/span&gt;: &lt;span class="s2"&gt;"content-21"&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;, 
    &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"id"&lt;/span&gt;: &lt;span class="s2"&gt;"23"&lt;/span&gt;, 
      &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"microposts"&lt;/span&gt;, 
      &lt;span class="s2"&gt;"attributes"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"title"&lt;/span&gt;: &lt;span class="s2"&gt;"title-22"&lt;/span&gt;, 
        &lt;span class="s2"&gt;"content"&lt;/span&gt;: &lt;span class="s2"&gt;"content-22"&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;, 
    &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"id"&lt;/span&gt;: &lt;span class="s2"&gt;"24"&lt;/span&gt;, 
      &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"microposts"&lt;/span&gt;, 
      &lt;span class="s2"&gt;"attributes"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"title"&lt;/span&gt;: &lt;span class="s2"&gt;"title-23"&lt;/span&gt;, 
        &lt;span class="s2"&gt;"content"&lt;/span&gt;: &lt;span class="s2"&gt;"content-23"&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;]&lt;/span&gt;, 
  &lt;span class="s2"&gt;"links"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"self"&lt;/span&gt;: &lt;span class="s2"&gt;"http://localhost:3000/user/1/microposts?page%5Bnumber%5D=8&amp;amp;page%5Bsize%5D=3&amp;amp;per_page=3"&lt;/span&gt;, 
    &lt;span class="s2"&gt;"first"&lt;/span&gt;: &lt;span class="s2"&gt;"http://localhost:3000/user/1/microposts?page%5Bnumber%5D=1&amp;amp;page%5Bsize%5D=3&amp;amp;per_page=3"&lt;/span&gt;, 
    &lt;span class="s2"&gt;"prev"&lt;/span&gt;: &lt;span class="s2"&gt;"http://localhost:3000/user/1/microposts?page%5Bnumber%5D=7&amp;amp;page%5Bsize%5D=3&amp;amp;per_page=3"&lt;/span&gt;, 
    &lt;span class="s2"&gt;"next"&lt;/span&gt;: &lt;span class="s2"&gt;"http://localhost:3000/user/1/microposts?page%5Bnumber%5D=9&amp;amp;page%5Bsize%5D=3&amp;amp;per_page=3"&lt;/span&gt;, 
    &lt;span class="s2"&gt;"last"&lt;/span&gt;: &lt;span class="s2"&gt;"http://localhost:3000/user/1/microposts?page%5Bnumber%5D=34&amp;amp;page%5Bsize%5D=3&amp;amp;per_page=3"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;, 
  &lt;span class="s2"&gt;"meta"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"current-page"&lt;/span&gt;: 8, 
    &lt;span class="s2"&gt;"next-page"&lt;/span&gt;: 9, 
    &lt;span class="s2"&gt;"prev-page"&lt;/span&gt;: 7, 
    &lt;span class="s2"&gt;"total-pages"&lt;/span&gt;: 34, 
    &lt;span class="s2"&gt;"total-count"&lt;/span&gt;: 100
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果使用 JSON_API，会自动添加 links&lt;/p&gt;
&lt;h3 id="切记"&gt;切记&lt;/h3&gt;
&lt;p&gt;curl 里的连接，如果需要传递多个参数，参数之间请使用&lt;code&gt;\&amp;amp;&lt;/code&gt;,不要单独使用&lt;code&gt;&amp;amp;&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="现有问题"&gt;现有问题&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;没搞清为啥原文可以用 BaseController...或者说原文忘加 include 了？&lt;/li&gt;
&lt;li&gt;为什么 curl 里的地址还需要使用转义符&lt;/li&gt;
&lt;li&gt;AMS 的返回的媒体类型问题该如何正确的解决？&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>smartepsh</author>
      <pubDate>Wed, 20 Jul 2016 13:31:10 +0800</pubDate>
      <link>https://ruby-china.org/topics/30573</link>
      <guid>https://ruby-china.org/topics/30573</guid>
    </item>
  </channel>
</rss>
