Rails 6で認証認可入り掲示板APIを構築する #5 controller, routes実装

←[Rails 6で認証認可入り掲示板APIを構築する #4 postのバリデーション、テスト実装](https://qiita.com/rf_p/items/0b2ccd908cac4bb6ecc6)

# controllerを作る

前回はmodelを作ったので、今回はcontrollerを実装していきます。

```powershell
$ rails g controller v1/posts
```

実行するとcontrollerとrequest specファイルが生成されます。

とりあえずcontrollerを以下まで実装します。

```rb:app/controllers/v1/posts_controller.rb
# frozen_string_literal: true

module V1
  #
  #  post controller
  #
  class PostsController < ApplicationController
    before_action :set_post, only: %i[show update destroy]

    def index
      # TODO
    end

    def show
      # TODO
    end

    def create
      # TODO
    end

    def update
      # TODO
    end

    def destroy
      # TODO
    end

    private

    def set_post
      @post = Post.find(params[:id])
    end

    def post_params
      params.permit(:subject, :body)
    end
  end
end
```

* index: post一覧を取得する
* show: post1レコードの情報を取得する(R)
* create: post1レコードを作成する(C)
* update:post1レコードを更新する(U)
* destroy: post1レコードを削除する(D)

CRUDに沿ったcontrollerを、一旦ロジック無しで作ります。
なお、V1というnamespaceを切っているのはAPI開発ではよくやる手法です。
これにより後方互換の無いversion2を作る際、分離して開発がしやすくなります。

続いてroutesを設定します。

```diff:config/routes.rb
 # frozen_string_literal: true

 Rails.application.routes.draw do
+   namespace "v1" do
+     resources :posts
+   end
 end
```

これでCRUDのroutesが設定されます。確認してみましょう。

```powershell
$ rails routes
...
                               Prefix Verb   URI Pattern                                                                              Controller#Action
                             v1_posts GET    /v1/posts(.:format)                                                                      v1/posts#index
                                      POST   /v1/posts(.:format)                                                                      v1/posts#create
                              v1_post GET    /v1/posts/:id(.:format)                                                                  v1/posts#show
                                      PATCH  /v1/posts/:id(.:format)                                                                  v1/posts#update
                                      PUT    /v1/posts/:id(.:format)                                                                  v1/posts#update
                                      DELETE /v1/posts/:id(.:format)                                                                  v1/posts#destroy
...
```

# indexテストの実装

例によってテストを先に実装します。
挙動としては

* 登録されたpostを返す
* created_at降順でソート
* 20件でlimit

でいきます。
簡易的なテストアプリケーションのチュートリアルのためpagerは組み込みませんが、もしかしたら今後記事を書くかもしれません。

```spec/requests/v1/posts_controller.rb
# frozen_string_literal: true

require "rails_helper"

RSpec.describe "V1::Posts", type: :request do
  describe "GET /v1/posts#index" do
    before do
      create_list(:post, 3)
    end
    it "正常レスポンスコードが返ってくる" do
      get v1_posts_url
      expect(response.status).to eq 200
    end
    it "件数が正しく返ってくる" do
      get v1_posts_url
      json = JSON.parse(response.body)
      expect(json["posts"].length).to eq(3)
    end
    it "id降順にレスポンスが返ってくる" do
      get v1_posts_url
      json = JSON.parse(response.body)
      first_id = json["posts"][0]["id"]
      expect(json["posts"][1]["id"]).to eq(first_id - 1)
      expect(json["posts"][2]["id"]).to eq(first_id - 2)
    end
  end
end
```

* beforeは、同ブロック以下itで毎回事前に実行されます。
* `create_list(:post, 3)`はpostを3レコード生成しDBに保存する処理です。
* また、itブロック終了時にテストDBはrollbackされます。

つまり挙動をまとめると、
LINE 10：it "正常レスポンスコードが返ってくる"ブロック開始
LINE  8：3件分のpostが保存される
LINE 11：v1_posts_url(v1/posts/index)にgetリクエストを行う
LINE 12：レスポンスコードが:ok(200 正常)
LINE 13：it "正常レスポンスコードが返ってくる"ブロック終了。rollbackされpostレコードは0件に
LINE 14：it "件数が正しく返ってくる"ブロック開始
LINE  8：3件分のpostが保存される
LINE 15：v1_posts_url(v1/posts/index)にgetリクエストを行う
LINE 16：response.bodyをJSON.parseしてRubyの配列に変換
LINE 17：レスポンスのpostが3レコード
LINE 18：it "件数が正しく返ってくる"ブロック終了。rollbackされpostレコードは0件に
↓
...

となります。

この時点ではcontroller未実装なので、当然テストはコケます。

なお、最後のテストは厳密にはcreated_atの比較が必要ですが、簡易的にidで比較をしています。
本来はlimitのテストもすべきですが省略します。興味があれば、create_listで21件作って20件しか返ってこないことを確認するテストを実装してみてください。

## Tips.

ついでによく使うfactoryBotのメソッドを紹介しておきます。

* `build(:post)` postを1レコード、メモリ上に生成。saveしない限りDBには反映されない。Post.newに相当
* `create(:post)` postを1レコード生成しDBに保存。Post.create!に相当
* `build_list(:post, 5)` postを5レコード生成。buildの複数版
* `create_list(:post, 5)` postを5レコード生成。createの複数版

# example.comをhostsに追加

なお、requestsテストは以下対応が必要です。

```diff:config/application.rb
...
     config.hosts << ".amazonaws.com"
+    config.hosts << "www.example.com"
...
```

なぜならrspecのテストはwww.example.comからのリクエストとして認識されるためです。

# indexの実装

```diff:app/controllers/v1/posts_controller.rb
...
     def index
-      # TODO
+      posts = Post.order(created_at: :desc).limit(20)
+      render json: { posts: posts }
     end
...
```

これで一覧取得ができます。
試しにcurlでAPIを叩いてみます。

```powershell
$ curl localhost:8080/v1/posts
{"posts":[{"id":2,"subject":"","body":"hoge","created_at":"2020-09-06T01:07:52.628Z","updated_at":"2020-09-06T01:07:52.628Z"},{"id":1,"subject":"hoge","body":"fuga","created_at":"2020-09-05T13:50:01.797Z","updated_at":"2020-09-05T13:50:01.797Z"}]}
```

もし空のdataが返ってきた場合は、`rails c`からpostのレコードを生成してみてください。

ここまでできたら、rubocopやrspec実行を忘れずに行った後、git commitしましょう。


# 続き

→[Rails 6で認証認可入り掲示板APIを構築する #6 show, create実装](https://qiita.com/rf_p/items/11c1d310f3b67c7f4881)

【[連載目次へ](https://qiita.com/rf_p/items/a846553c11571f4177eb#%E7%9B%AE%E6%AC%A1)】