地方でリモートワーク in Iwate

東京の受託開発会社でリモートワークしてます。

deviseでログイン前にapiリクエストしたときにカスタマイズjsonを返したいとき

スポンサーリンク

f:id:ihatov08:20160728001100j:plain deviseでログイン前にapiリクエストしたときにカスタマイズjsonを返したいときのカスタマイズ方法です。

ref: How To: Redirect to a specific page when the user can not be authenticated · plataformatec/devise Wiki

CustomFailureクラスを作成

Devise::FailureAppを継承したCustomFailureを作成します。respondメソッドをオーバーライドしてcontent typeがapplication/jsonだったらjson返すように設定しました。

lib/custom_failure.rb

class CustomFailure < Devise::FailureApp
  def respond
    if request.content_type == "application/json"
      self.status = 401
      self.content_type = "application/json"
      self.response_body = {
        result: false,
        message: I18n.t('devise.failure.unauthenticated'),
        error_code: Constant::ApiErrorCode::UNAUTHENTICATED
        }.to_json
    elsif http_auth?
      http_auth
    elsif warden_options[:recall]
      recall
    else
      redirect
    end
  end
end

respondメソッドにelsifでrequestのcontent_typeがapplication/jsonだったらという条件分岐を加えればOKですね!

devise/failure_app.rb at master · plataformatec/devise · GitHub

libの読み込み設定

libをはじめに読み込まれるように設定します。

config/application.rb

config.autoload_paths += %W(#{config.root}/lib)

確認方法です。

www.tom08.net

deviseの設定に加える

deviseの設定に加えればOKです!

config/initializers/devise.rb

  config.warden do |manager|
    manager.failure_app = CustomFailure
  end

Rspec

テストも追加してみました!libのテストをするときは

rails_helper.rb

require File.expand_path('../../config/environment', __FILE__)

が記述されているか確認してください!

また該当specファイルでrequireしましょう!

spec/lib/custom_failure_spec.rb

require 'rails_helper'
require 'custom_failure'

RSpec.describe 'lib/CustomFailure', type: :request do
  describe 'POST /api/v1/users/update_setting.json' do
    let(:request_header) do
      { 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json' }
    end
    let(:user) do
      '{"user_email": "test@test.com", "user_token": "dammy", "email": "change@test.com"}'
    end
    before do
      @user = create(:user, email: "test@test.com", authentication_token: "dammy")
    end
    context "認証前にリクエスト" do
      before do
        post api_v1_users_update_setting_path(format: :json), user, request_header
      end
      it "401を返す" do
        expect(response).to have_http_status(401)
      end
      it "application/jsonで返す" do
        expect(response.content_type).to eq('application/json')
      end
      it "JSONに含まれるキーが適切である" do
        result = JSON.parse(response.body)
        expect(result).to have_key('result')
        expect(result).to have_key('message')
        expect(result).to have_key('error_code')
      end
      it "レスポンスパラメータが適切である" do
        body = JSON.parse(response.body)
        expect(body["result"]).to eq false
        expect(body["message"]).to eq I18n.t('devise.failure.unauthenticated')
        expect(body["error_code"]).to eq 1
      end
    end
  end
end