9
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Ruby on RailsAdvent Calendar 2016

Day 22

パラメータを引き継いでPOSTやPUTにリダイレクトっぽいことをする

Last updated at Posted at 2016-12-21

誰しも一度は考えたことがあると思います、POSTやPUTにリダイレクトしたい・・・できればパラメータも引き継いで、と。

しかし、HTTP/1.1 プロトコルの制約があるため、これを実現するのは不可能だと思われます。

ググってみると、sessionにパラメータを一時的に保存して、ページ遷移後に復元するという方法を見かけました。

RailsでPOSTリクエストではリダイレクトできないことへの対応

しかし、この方法はGET以外にリダイレクトさせたい場合は手間が増えますし、汎用化も難しい気がします。

そこで、別の方法を考えてみました。

概要

実現方法をざっくり説明すると、空ページをrenderし、そこに引き継ぎたいパラメータを埋め込んだフォームを設置して自動サブミットする、と言う感じです。特定の処理専用にこれを実現するのは簡単ですが、今回は汎用的に実現する実装を行いました。

実装:paramsをhidden_field_tagに展開

まずはapplication_helperあたりに下記のようなメソッドを定義します。今回はhelperに定義しましたが、viewから呼び出せればどこでも構いません。

def parse_from_params(params, anc = nil)
  @tags ||= []
  params.each do |row|
    key = if anc
            f, b = anc.split
            f + "\[#{row.first} \]" + b.to_s
          else
            row.first.to_s
          end
    if row.last.is_a?(Hash)
      parse_from_params(row.last, key)
    else
      @tags << hidden_field_tag(key.delete(' '), row.last)
    end
  end
  @tags.reduce(:+)
end

かなり雑にコーディングしたので分かりにくい思いますが、簡単に説明すると、paramsを再帰的に分解して、hidden_field_tagを生成する、って感じです。

例えばparamsが以下のような状態の場合、

params = {a: 1, b: {c: 2, d: 3, e: {f: 4}}}

parse_from_paramsは以下のhidden_field_tagを生成します。

hidden_field_tag "a", 1
hidden_field_tag "b[c]", 2
hidden_field_tag "b[d]", 3
hidden_field_tag "b[e[f]]", 4

実装:踏み台viewをrenderするメソッド

次に、application_controllerあたりに以下のメソッドを定義します。

private

def redirect_keeped_params(url, **options)
  @url = url
  params_option = options.first
  @params = { params_option.first => params_option.last }
  @method = options.last.last
  render_text = <<-EOS
    <%= form_tag @url, method: @method, id: "redirect_form" do %>
      <%= parse_from_params(@params) %>
      <%= javascript_tag 'document.getElementById("redirect_form").submit()' %>
    <% end %>
  EOS
  render inline: render_text
end

先ほどhelperに定義したparse_from_paramsによって生成されたhidden_field_tagを含むフォームが埋め込まれた空ページをrenderします。そして、描画されたら自動でsubmitし、引数で指定したurlに遷移します。

使用例

例えば、以下のようなcontrollerがあるとします。

class HogeController < ApplicationController

  def new
     #何らかの処理
  end

  def create
     #何らかの処理
  end
end

このHogeControllerのcreateアクションに、外部のcontrollerからリダイレクトしたい場合を考えます。createアクションのurlは、hoge_urlとします。

controller側の処理としては、リダイレクトしたいタイミングでredirect_keeped_paramsを呼び出すだけです。引数には遷移先のpathと、渡したいパラメータ、遷移先のリクエストメソッドを指定します。

class FugaController < ApplicationController

  def new
     #何らかの処理
    redirect_keeped_params(hoge_path, fuga: permited_params, method: :post)
  end

  private

  def permited_params
    params.require(:fuga).permit(以下略)
  end
end

このように、普段redirect_toを使ってるような感覚で、POSTやPUTに遷移することが可能です。

最後に

力技ではありますが、最小限の実装で目的を実現することができました。しかし、この方法にはいくつか問題もあります。例えば、ページ遷移時に一瞬白いページが表示されてしまうことです。ユーザビリティ的にはあまり影響はありませんが、やはりスマートではないので、何か対策を考えたいです。他にも、ブラウザバック時の挙動も考えたほうがよさそうです。現状だとブラウザバックすると、踏み台ページに遷移し自動サブミットになるので、結局バックする前のページに戻ってしまいます。

まあそもそも、このような手法を検討しなければならないようなアプリケーションは、基本的な設計から見直すべきかもしれませんが・・・

9
10
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
9
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?