1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AbstractController::DoubleRenderError

Posted at

🚨 エラー内容

Render and/or redirect were called multiple times in this action. 
Please note that you may only call render OR redirect, and at most once per action. 
Also note that neither redirect nor render terminate execution of the action, 
so if you want to exit an action after redirecting, 
you need to do something like "redirect_to(...); return". 
(AbstractController::DoubleRenderError)

🔍 概要

以下のようなコードを書いたところ、AbstractController::DoubleRenderError が発生しました。

class ApiController < ApplicationController
  before_action :verify_hmac

  def update
    # 実際の処理
  end

  private

  def verify_hmac
    check_hmac_timestamp
    # HMAC 検証処理…
    return if signature_not_match

    render json: { error: 'Invalid HMAC signature' }, status: :unauthorized
  end

  def check_hmac_timestamp
    timestamp = request.headers['X-HMAC-Timestamp']

    begin
      Time.iso8601(timestamp)
    rescue ArgumentError
      render json: { error: 'Invalid timestamp format' }, status: :bad_request
      return
    end
  end
end

⚠️ 問題の原因

上記のように verify_hmac を before_action に指定していますが、その中で呼び出している check_hmac_timestamp の中でも render を実行しています。

Rails では before_action で render または redirect_to を呼び出すと、それ以降のアクション(updateなど)はスキップされますが、メソッドチェーンの途中で render を呼んでも処理は中断されないため、そのまま verify_hmac に戻り、さらに render が呼ばれてしまいます。

つまり、同じリクエスト内で複数回 render を実行してしまうことにより DoubleRenderError が発生しているというわけです。


✅ 対策

✔ 対策1:両メソッドを before_action に分けて定義する

class ApiController < ApplicationController
  before_action :check_hmac_timestamp
  before_action :verify_hmac

  def update
    # アクション処理
  end

  private

  def verify_hmac
    return if signature_valid?

    render json: { error: 'Invalid HMAC signature' }, status: :unauthorized
  end

  def check_hmac_timestamp
    timestamp = request.headers['X-HMAC-Timestamp']

    begin
      Time.iso8601(timestamp)
    rescue ArgumentError
      render json: { error: 'Invalid timestamp format' }, status: :bad_request
    end
  end
end

補足:before_action は定義された順に実行されるため、順番にも注意してください。

✔ 対策2:check_hmac_timestamp では render せず、呼び出し元で処理する

def verify_hmac
  unless valid_timestamp?
    render json: { error: 'Invalid timestamp format' }, status: :bad_request
    return
  end

  unless signature_valid?
    render json: { error: 'Invalid HMAC signature' }, status: :unauthorized
    return
  end
end

def valid_timestamp?
  timestamp = request.headers['X-HMAC-Timestamp']
  Time.iso8601(timestamp)
  true
rescue ArgumentError
  false
end
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?