APIのPOST処理を実行
前回は以下の記事のように、RailsでAPIを使用してGET処理を行いました。
ti-tomo-knowledge.hatenablog.com
今回はPOST処理の方を行い、データの保存まで行いたいと思います。
最終的に実行したいAPIコマンドは以下になります。
curl -X POST -H "Content-Type: application/json" -H "Origin: http://localhost:8080" -d '{"title":"EEEE", "body":"FFFF"}' localhost:3000/api/articles
前回作成したarticlesというテーブル(モデル)に対してデータを挿入する簡単な処理です。
急に出てきた
-H "Origin: http://localhost:8080"
の部分ですが、これはクロスサイトオリジンのテストをする時用に使用します。
簡単に言ってしまえば、APIでは色々な場所から自由にアクセスされてしまうと困るものがあります。
そのような場合に、指定のドメイン以外からのアクセスを遮断する設定(CORS対策)を入れる必要があるのですが、その設定が正しく動いているか確認するために接続元(Origin)を色々変えてテストしようと思います。
APIの記述
クロスサイトオリジンの部分は後述するとして、まずはAPIを実行できるようにしましょう。
rake routesを見ると、
Prefix Verb URI Pattern Controller#Action api_articles GET /api/articles(.:format) api/articles#index POST /api/articles(.:format) api/articles#create api_article GET /api/articles/:id(.:format) api/articles#show PATCH /api/articles/:id(.:format) api/articles#update PUT /api/articles/:id(.:format) api/articles#update DELETE /api/articles/:id(.:format) api/articles#destroy
/api/articlesのPOST処理ではcreateメソッドで処理がされるようなので、コントローラに処理を書きましょう。
module Api class ArticlesController < ApplicationController def create articles = Article.new({title: params[:title], body: params[:body]}) articles.save render json: { status: 'OK' } end end end
これで実行すると、、、
以下のようなログが吐かれているので、無事にINSERTできたことがわかりますね。
app/controllers/api/articles_controller.rb:10 Article Create (26.9ms) INSERT INTO `articles` (`title`, `body`, `created_at`, `updated_at`) VALUES ('EEEE', 'FFFF', '2019-07-25 02:13:58', '2019-07-25 02:13:58') app/controllers/api/articles_controller.rb:10
ほんとこういう確認Rails楽だわ〜
CORSの設定
さて次はCORSの設定をしましょう。
やり方は「config/initializers/cors.rb」のファイルにもご丁寧に書いてあるのですが、
まずはrack-corsをインストールします。
Gemfileに以下を記述して(コメントアウト外して)
gem 'rack-cors'
インストールです。
bundle install
次にcors.rbのコメントアウトを消して以下のようにします。
Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins 'localhost:3000' resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head], credentials: true end end
これはlocalhost:3000からのアクセスだけ許可する(allow)という意味です。
※Cookie(クッキー)の送信をする場合は、合わせてcredentials: trueを入れておきましょう。
ではこの状態でもう一度実行してみましょう。
curl -X POST -H "Content-Type: application/json" -H "Origin: http://localhost:8080" -d '{"title":"EEEE", "body":"FFFF"}' localhost:3000/api/articles
はい、がんがんINSERTされます。
って、Originがlocalhost:8080なのにINSERTされちゃだめだろう!笑
どうにもcurlからやると上手くcorsの設定が効かずに弾いてくれません。
色々調べたのですがどうにもわからなかったので、fetch APIで実行することにしました。
適当なサイトを開いてdeveloperツールのconsole画面を開き、以下のコマンドを実行します。
const obj = {"title":"EEEE", "body":"FFFF"}; const method = "POST"; const body = JSON.stringify(obj); const headers = { 'Content-Type': 'application/json' }; fetch("http://localhost:3000/api/articles", {method, headers, body}).then((res)=> res.json()).then(console.log).catch(console.error);
以下のメッセージが出てくるので上手く行ってる(ブロックされてる)みたいですね。
Access to fetch at 'http://localhost:3000/api/articles' from origin 'http://labs.opentone.co.jp' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
CORSの設定を環境ごとに分ける
ただしこれでは開発環境では良いですが、本番環境ではlocalhost:3000からのアクセスを許可してしまうことになります。
そこで、環境ごとに許可するOriginを場合分けしましょう。
やり方は様々で、環境変数で指定する方法などもありますが、今回はymlの設定ファイルを別途で用意して環境ごとの設定値を入れることにします。
configディレクトリ直下に、request.ymlというファイルを作り、以下のように環境ごとの値をいれましょう。
default: &default domain: localhost:3000, localhost:8080 development: <<: *default test: <<: *default production: domain: production_domain.com
※production_domain.comはあくまで例です。環境に合わせて設定してください。
次にapplication.rbに以下を追記しましょう。
config.x.request = ActiveSupport::InheritableOptions.new(config_for(:request))
最後にcors.rbのoriginsを書き換えればOKです。
Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins Rails.application.config.x.request['domain'] resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head], credentials: true end end
これで環境ごとにoriginsの値を変更できますね。
次回はPOSTされた値のバリデート処理について書こうと思います。