(Ruby on Rails)クッションページを作ってみた話
きっかけ
LINEのRuby系のオプチャにてクッションページが上手く作れないという方が居たので作ってみました。
クッションページとは?
アダルトサイトの年齢確認のページのような、本来のURLの表示の前にユーザに再度アクセスの意思を問い合わせるページを指してクッションページと呼ぶ事が多いようです。
外部リンクに対してクッションページが存在する場合は、ユーザーへの移動確認の他に、クリックのアクセス解析や広告枠の確保などが目的の場合もあるようです。[1]
ここでは本文中のリンクをクリックした際にそのリンク先に本当に移動してもよいか問い合わせるクッションページを作成します。
手順
事前準備
Docker
と同様のDockerfile & docker-compose.yml を使用しています。
Railsプロジェクトの作成
コンテナ内の展開先で以下のコマンドを叩いていきます。
今回はクッションページを作ることが主目的なのでできるだけ使わないものは省かれるようにオプションを選択してみたつもり[2]です。
rails _6.1.4.1_ new . --force --minimal --skip-activerecord --skip-test
同ディレクトリで以下のコマンドを実行してブラウザで http://localhost:3000/ にアクセスするととりあえず動作していることが確認できました。
rails s -p 3000 -b '0.0.0.0'
クッションページの作成
クッションページなのでコントローラー名は「cushions」[3]としました。
showにクッションページの本体を作成します。
indexにはクッションページへのリンクのサンプルを配置するので一緒に作成します。
rails generate controller cushions index show
上記コマンドでルーティングも自動的に設定してくれるのですが、今回の目的に沿った形に書き換えます。
Rails.application.routes.draw do
root to: 'cushions#index'
get 'cushions/', to: 'cushions#index'
get 'cushions/:url', to: 'cushions#show', constraints: { url: /.+/ }
end
- / や /cushions/ にアクセスした場合はクッションページへのリンクのサンプルページを表示します
- /cushions/にパラメータが付いている場合はそれをURLと判断してクッションページを表示します
ここが本題です。
上記コマンドで生成されているはずのコントローラとビュー、ヘルパーを先に確認してください。
# rails generate controller cushions index show
require "cgi"
class CushionsController < ApplicationController
def index
# 「<br>」は検索条件、「#hash」はハッシュです。
sample_url = 'https://duckduckgo.com/?q=<br>&ia=web#hash'
@plain_url = sample_url
end
def show
@urlescaped_url = params[:url]
@url = CGI.unescape(@urlescaped_url)
end
end
# rails generate helper cushions
require 'cgi'
#
# CushionsHelper
#
module CushionsHelper
#
# URLをエスケープする
#
# @param [string] raw_url url
#
# @return [string] escaped url
#
def cushions_escape(raw_url)
CGI.escape(raw_url)
end
#
# クッションURLを作成する
#
# @param [string] raw_url url
#
# @return [string] クッションURL
#
def cushions_url(raw_url)
urlescaped_url = cushions_escape(raw_url)
"/cushions/#{urlescaped_url}"
end
#
# クッションURLを埋めたリンクタグを作成する(base : link_to)
#
# @param [string] raw_url url
# @param [hash] html_options link_toに送るhash
#
# @return [string] クッションURLを埋めたリンクタグ
#
def cushions_link_to(raw_url, html_options = {})
link_to raw_url, cushions_url(raw_url), { target: '_blank' }.merge(html_options || {})
end
end
<%= cushions_link_to @plain_url, {id: 'special_id'} %>(通常は別のタブが開きます)
<%= link_to @url, @url %>は当サイトではない外部のページです。
大切な点は
- URLを埋め込む際に事前にエスケープ、もしくはエンコードをしておく
- クッションページではそれに対応するアンエスケープ、もしくはデコードをする
ことです。
処理の流れとしては
- コントローラで事前に表示するページのURLを抽出し、それらをエスケープしたうえでURLをクッションページに差し替える
- サンプルページの方では、外部リンクのURLを表示はするけど、実際のリンク先はクッションページにする
- クッションページのコントローラでパラメータのURLをエスケープされる前のURLに戻す
- クッションページでは普通にリンクを張る
となります。
補足
config/routes.rb のクッションページの設定に関して
URLには通常、ドメインなどの区切り文字として「.(ドット)」が使用されます。
一方でRailsのルーティングでも意味があり、「.」以下はformat(拡張子的なもの)扱いされます。
したがって、クッションページにURLを含める際は
- 「.」が無い状態(BASE64エンコードするなど)にする
- 「.」が含まれていてもルーティングでformat扱いされないようにする
のどちらかが必要になります。
今回は「constraints」を個別に設定することで「.」が含まれていてもルーティングでformat扱いされないようにすることとしました。
ソース全体
クッションページの作成 セクション以降で作成されるファイル全体は以下のGitHubリポジトリに置いてあります。
まとめ
- 本来のリンク先URLをURL以外の形に変形して
- クッションページへそれを渡して
- クッションページで元の状態に戻す
お疲れさまでした。
Discussion