API

APIとは

必要なデータだけをJSONなどの形式で返すサーバの仕組みのことをAPI、もしくはJSON APIと呼ぶ。

webAPI

json形式で情報をレスポンスするアクションはwebAPIと言われる。
railsではWebAPIにあたるアクションを書くコントローラのファイルは全てapiというディレクトリに置くルール。

コントローラにAPIフォルダを作成し、API化するコントローラのrbファイルを作成。

例
app/controllers/api/messages_controller.rb

同様にviewファイルもフォルダを作成してから作成

例
app/views/api/index.json.jbuilder

APIの作り方(rails)

  • HTMLを返すためのコントローラーを共用してAPIとして使えるようにする
  • API専用のコントローラーを作成する
  • APIを作成するためのgemを利用する

コントローラーでの振り分け

Railsには、コントローラーのアクションの中でHTMLとJSONといったフォーマット毎に条件分岐できる仕組みがある。

respond_to

コントローラーで利用できる。
JSONを返す場合はRubyのハッシュの状態のままrenderメソッドに渡すだけでJSONに変換してくれる。インスタンス単体ならそのまま渡してもOK。

respond_to do |format|
  format.html { render ... } # この中はHTMLリクエストの場合に呼ばれる
  format.json { render ... } # この中はJSONリクエストの場合に呼ばれる
end
respond_to do |format|
  format.json { 
    render json: { id: @user.id, name: @user.name }
  }
end
def create
  @todo = Todo.new(todo_params)
   if @todo.save
    respond_to do |format|
      format.html { redirect_to :root }
      format.json { render json: @todo}
    end
  else
    render :index
  end
end

ルーティングで振り分け

defaultオプションを用いてアクセスされた場合に常にjsonを返すようにも設定できる。

返却されるjson形式のデータ

インスタンスを渡すとそのモデルの情報のみがjson内に格納される。

format.json {render json: @message}

この場合jQuery側で@messageのインスタンス変数は呼べるが、アソシエーションを組んだモデル@message.userは呼べない。
なので@message.user.nameが欲しい時はインスタンスを投げるのではなく、json.jbuilderファイル内で以下のように別途定義してjsonのviewを返す。

json.body @message.body
json.name @message.user.name

jbuilder

rails newした際にgemfileにデフォルトで記述されているgem。
入力データをJSON形式で出力するテンプレートエンジン。

jbuilderを使用してJavaScriptファイルに返すデータを作成

viewを同じように該当するアクションと同じ名前にする必要がある。
viewと同じように該当コントローラの該当アクション名で.json.jbuilderファイルを作成する。


hogehoges_controllerの#createアクションに対応するjbuilderファイルを
view/hogehoges/create.json.builderに作成する。

json.jbuilderファイル内の記法

基本的にjson.KEY VALUEと記述する。
この時のkeyを用いてJavaScriptでDOM要素に出力する。

json.text  @comment.text
json.user_id  @comment.user.id
json.user_name  @comment.user.nickname

以下のようにインスタンスを渡してまとめて定義することもできる。

json.(@message, :content, :image)

ちなみにcontroller側の記述で
@comment = Comment.new(params) とせずに
comment = Comment.new(params) 
@抜きにした場合、
json.jbuilderファイル内でjson.text comment.textのように記述しても出力できない。
newは通るけれど、viewに投げるには@が符号になっているためこのような処理は通らない。
viewファイルを経由せずにcontroller側でハッシュにしてしまえば通る。

これは通る
@comment = Comment.new()
略
format.json
@をつけない場合。コントローラ内でハッシュにして渡せば通る。
view経由させるなら@は必須。

def create
  comment = Comment.create(comment_params)
  hoge = {text: comment.text, user_id: comment.user_id }
  respond_to do |format|
    format.html { redirect_to tweet_path(params[:tweet_id])  }
    format.json { render json: hoge}
  end
end

json.array!

json形式のデータを配列にしたい場合に使う。

json.array! @products do |product|
  json.id product.id
  json.title product.title
  json.image product.image_url
  json.detail product.detail
end

名前空間(namespace)

class Api::MessagesController < ApplicationController
  def index
  end
end

同名のコントローラ名を使いたい場合、コントローラ名の前に::をつけて別名を付与できる。
名前空間をつけることにより、同名であっても区別できる。

Api::MessagesControllerとする場合には、相当する位置にmessages_controller.rbファイルを置く必要がある。
つまり、controller/api/messages_controller.rbに置く。
名前空間を増やしてディレクトリを深くする場合には、ディレクトリにそった命名が必要。
controller/api/hoge/fuga/messages_controller.rbなら
Api::Hoge::Fuga::MessagesControllerと命名する。
でなければ404not foundとなる。

なお対応するviewファイルはそれより1段深い階層に設置する。
(コントローラの分だけ深くなる)
views/api/messages/index.json.jbuilder

resourcesでnamespaceのネスト

通常通りネストした場合と大きな変化はない。
単にネストの親のidの次にnamespace名を挟むだけ。

  resources :groups  do
    namespace :api do
      resources :messages
    end
  end
      group_api_messages GET    /groups/:group_id/api/messages(.:format)          api/messages#index
                         POST   /groups/:group_id/api/messages(.:format)          api/messages#create
   new_group_api_message GET    /groups/:group_id/api/messages/new(.:format)      api/messages#new
  edit_group_api_message GET    /groups/:group_id/api/messages/:id/edit(.:format) api/messages#edit
       group_api_message GET    /groups/:group_id/api/messages/:id(.:format)      api/messages#show
                         PATCH  /groups/:group_id/api/messages/:id(.:format)      api/messages#update
                         PUT    /groups/:group_id/api/messages/:id(.:format)      api/messages#update
                         DELETE /groups/:group_id/api/messages/:id(.:format)      api/messages#destroy

resourcesのオプション

formatオプションをつけるとアクセスした際のレスポンスを設定できる。

resources :messages,only: :index, defaults: { format: 'json' }

rake routesすると以下のように変化する。
api/messages#index {:format=>"json"}

Ajax

$.ajax()は以下の4つの要素を送信する。
この要素の記載に続けて、.doneと.failを記載して送信後の処理を行う。

typeHTTP通信の種類を記述する。通信方法は、GETとPOSTの2種類がある。
urlリクエストを送信する先のURLを記述する。
dataサーバに送信する値を記述する。
dataTypeサーバから返されるデータの型を指定する。
$(function() {
  function buildHTML(todo) {
    var html = $('<li class="todo">').append(todo.content);
    return html;
  }

  $('.js-form').on('submit', function(e) {
    e.preventDefault();
    var textField = $('.js-form__text-field');
    var todo = textField.val();
    $.ajax({
      type: 'POST',
      url: '/todos.json',
      data: {
        todo: {
          content: todo
        }
      },
      dataType: 'json'
    })
    .done(function(data) {
      var html = buildHTML(data);
      $('.todos').append(html);
      textField.val('');
    })
    .fail(function() {
      alert('error');
    });
  });
});

渡すデータ

dataは基本的にハッシュで渡す。
formの場合はハッシュのネストで渡すと通常のフォームと同じ形式になるが、
FormDataオブジェクトで渡すのが普通。

data: { keyword: input },

返ってくるデータ

$.ajax({})
.done(function(DATA){})

$.ajax({})の後の.done(function(DATA){})のDATA部分はajaxでリクエストを投げた結果、返ってきた値が入る。
入れる値を指定しているのではなくて、返ってきた値をDATAと命名し、以降の関数内でDATAという変数名で用いる。
通常の引数を渡すという感覚ではなく、引数はすでに持っていてそれに名前をつける感覚。

リダイレクトするパス

ajax関数のurlに何も指定しなかった場合、リクエストのURLは現在ブラウザに表示されているパスになる。
urlに文字列で値を指定すると、現在ブラウザに表示されているURLの後に繋がる形になる。
“/”を付けるとrootからのパスになる。

http://hogehoge.com/fugaの時
$.ajax{
  url: "hoge"
}
とすると
"http://hogehoge.com/fuga/hoge"
へリダイレクトする。

$.ajax{
  url: "/hoge"
}
とすると
"http://hogehoge.com/hoge"
へリダイレクトする。

コメント