Rails で投稿のURLをランダムな文字列にする
どうも、最近Ruby on Rails
でサービスを作ろうと思って絶賛手探り中、たくチャレ(@takuchalle)です。
ツイートや記事のように何かを投稿するようなアプリを考えた場合、Rails でそのまま何も考えずに作るとhttps://hogehoge.com/posts/1
やhttps://hogehoge.com/posts/4
みたいに連番のURLでアクセスするようになると思います。
今回は、この URL を連番ではなくランダムになるように実装する方法を紹介します。
ランダムにしたい理由
連番だと URL が容易に推測されて、アクセス制御に漏れがあった場合セキュリティリスクになってしまいます1。 特に個人情報を扱っているサイトでは致命的です。
また、サービスを立ち上げたばかりの時に URL が若い番号になってしまい、新規ユーザに対してあまり盛り上がっていない印象を与えてしまいます。
この2つの理由から僕は投稿のURLやユーザのURLは連番ではなく、ランダムな文字列にしたほうが良いと考えています。
対象バージョン
- Ruby 2.5.1
- Rails 5.2.1
想定アプリ
Twitter のような短文を投稿できるアプリで、簡易のためログインなしで匿名で投稿できることを想定しています。
次の流れで実装していきます。
Post
モデルを作成PostsController
を作成- 投稿して連番の URL のままアクセスできることを確認
- ランダムな URL を生成するように修正
- 生成したランダムな URL でアクセスできることを確認
最終的なコードはGitHub
に置いておきます。
Post モデルの作成
content
というテキストフィールドのみを持つPost
モデルを生成して、マイグレーションします。
$ bundle exec rails g model Post content:text
$ bundle exec rails db:migrate
PostsController の作成
次にコントローラの作成です。
今回は投稿と閲覧だけできればいいので、new
,show
アクションを持つコントローラを作成します。
$ bundle exec rails g controller Posts new show
上記コマンドでルーティングが書き換わります。投稿された時のcreate
アクションも必要なので以下のようにconfig/routes.rb
を書き換えます。
Rails.application.routes.draw do
resources :posts, only: %i[new create show]
end
この状態でローカルサーバを立ち上げて、localhost:3000/posts/new
にアクセスできることを確認してください。
投稿フォーム作成
次に投稿フォームを作って投稿できるようにし、投稿内容を確認できるようにします。
このあたりの内容は分かってるものとして進みます。基本的に最低限のことしかやっておらず、エラーハンドリングは何も考えてないので突っ込まないでください。
修正するファイルは3つです。
まずコントローラを修正します。app/controllers/posts_controller.rb
を開いて次のように修正します。
class PostsController < ApplicationController
def new
@post = Post.new
end
def create
@post = Post.new(post_params)
if @post.save
redirect_to @post
end
end
def show
@post = Post.find(params[:id])
end
private
def post_params
params.require(:post).permit(:content)
end
end
次に投稿フォームの作成です。app/views/posts/new.html.erb
を開いて次のように修正します。本当に最低限の実装のフォームです。
<%= form_for(@post) do |f| %>
<%= f.label :content %>
<%= f.text_area :content, placeholder: "Type anything..." %>
<%= f.submit "Post" %>
<% end %>
最後に投稿した内容を表示します。app/views/posts/show.html.erb
を開いて次のように修正します。投稿した内容だけ表示します。
<%= @post.content %>
この状態でローカルサーバを立ち上げて、localhost:3000/posts/new
にアクセスし、投稿できることを確認してください。この時にリダイレクトされる URL も確認してみてください。localhost:3000/posts/1
になってるはずです。
ランダムな文字列の生成
ランダムな文字列を URL にしてアクセスできるようにPost
モデルにtoken
を追加します。モデルの生成時にtoken
にランダムな文字列を入れて、URL として使います。
まずPost
モデルにtoken
カラムを追加してマイグレーションします。
$ bundle exec rails g migration add_token_to_posts token:string
$ bundle exec rails db:migrate
SecureRandom
モジュールのurlsafe_base64
を使ってランダムな文字列を生成します。生成タイミングとしてはデータベースに保存される前ならいつでもいいのでbefore_create
コールバックに指定しておきます。
require 'securerandom'
class Post < ApplicationRecord
before_create :generate_token
private
def generate_token
self.token = SecureRandom.urlsafe_base64
end
end
本来ならtoken
がユニークになってるかのチェックが必要です。どのタイミング・どのレイヤでやるべきかまだ分からないのでいつか追記します。2
この状態でローカルサーバを立ち上げて、localhost:3000/posts/new
にアクセスし、投稿できることを確認してください。ブラウザ上からtoken
は分かりませんので、rails console
を使って投稿したデータを見るとtoken
が保存されていることが分かります。
irb(main):003:0> Post.find(2)
Post Load (0.2ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
=> #<Post id: 2, content: "aaaaa", created_at: "2018-11-22 06:37:57", updated_at: "2018-11-22 06:37:57", token: "4HfKquKEbAE79dW95chhYA">
この場合、token
に"4HfKquKEbAE79dW95chhYA"
が入ってることが分かります。
ランダムなURLに変更
先ほど生成したtoken
でアクセスできるようにします。以下のようにconfig/routes.rb
を書き換えます。差分が分かりにくいですが、末尾に, param: :token
がつきました。
Rails.application.routes.draw do
resources :posts, only: %i[new create show], param: :token
end
これでURLの文字列をparams[:token]
で取得できるようになります。それを使ってapp/controllers/posts_controller.rb
を以下のように書き換え、検索とリダイレクトを行うようにします。ハイライトされている部分が変わりました。
class PostsController < ApplicationController
def new
@post = Post.new
end
def create
@post = Post.new(post_params)
if @post.save
redirect_to post_url(token: @post.token)
end
end
def show
@post = Post.find_by(token: params[:token])
end
private
def post_params
params.require(:post).permit(:content)
end
end
この状態でローカルサーバを立ち上げて、localhost:3000/posts/new
にアクセスし、投稿できることを確認してください。この時にリダイレクトされた URL が連番の ID ではなく、ランダムな文字列になっていたら成功です!
まとめ
Ruby on Rails
で URL にランダムな文字列を使う方法を紹介しました。
ここで紹介したのは本当に最低限の実装ですので、セキュリティ面や性能面で気を付ける必要がある部分は残っていますが、ランダムな文字列を URL に使う方法の雰囲気は掴めたのではないでしょうか。
GitHub
にコードを登録してあるので試してみてください。
プルリクでも Twitter のリプライ(@takuchalle)でもフィードバックお待ちしています。