はじめに
これまでの記事で、Web技術の歴史とMVCアーキテクチャについて学んできました。
- 【記事1】Web技術の歴史とRackの正体を理解する
- 【記事2】Rails初心者が完全理解すべきMVCアーキテクチャ
今回は最終回として、実際のHTTPリクエスト/レスポンスの中身とActiveRecordの仕組みを徹底解説します。
これで「Railsで何が起きているのか」が完全に理解できます!
対象読者
- HTTPリクエスト/レスポンスの中身を知りたい方
- ActiveRecordが何をしているのか理解したい方
- ERBテンプレートの仕組みを知りたい方
- ブラウザとサーバーのやり取りを完全に理解したい方
HTTPリクエストとは
HTTP(HyperText Transfer Protocol)は、ブラウザとサーバーが通信するためのプロトコル(約束事です。
リクエストの構成要素
HTTPリクエストは以下の要素で構成されています:
- HTTPメソッド:何をしたいか(GET、POST等)
- パス:どのページか(/users、/posts/1等)
- ヘッダー:追加情報(Cookie、認証等)
- ボディ:送信データ(POSTの場合)
実例1:ユーザー一覧を表示する
シナリオ
ブラウザのアドレスバーに https://example.com/users と入力してEnter
HTTPリクエスト
GET /users HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Chrome/120.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ja,en-US;q=0.9,en;q=0.8
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cookie: _session_id=abc123xyz; user_id=42
リクエストの解説
| 要素 | 値 | 意味 |
|---|---|---|
| HTTPメソッド | GET |
データを取得したい(見るだけ) |
| パス | /users |
ユーザー一覧ページ |
| Host | example.com |
アクセス先のドメイン |
| User-Agent | Mozilla/5.0... |
ブラウザの種類とバージョン |
| Accept | text/html... |
HTMLを受け取りたい |
| Cookie | _session_id=... |
ログイン状態などを保持 |
サーバー内部の処理
1. Webサーバー(Nginx)がリクエスト受信
↓
2. Rack「GETリクエスト、/usersへのアクセスだな」
↓
3. Railsルーティング
GET /users → UsersController#index
↓
4. UsersController#index実行
# app/controllers/users_controller.rb
class UsersController < ApplicationController
def index
# Step 1: Modelに全ユーザー取得を依頼
@users = User.all
# Step 2: 自動的にViewが呼ばれる
end
end
# app/models/user.rb
class User < ApplicationRecord
# User.all が呼ばれると...
end
ActiveRecordが内部で実行するSQL:
SELECT `users`.* FROM `users`
取得結果(Rubyオブジェクトに変換):
[
#<User id: 1, name: "田中太郎", email: "tanaka@example.com">,
#<User id: 2, name: "佐藤花子", email: "sato@example.com">,
#<User id: 3, name: "鈴木一郎", email: "suzuki@example.com">
]
Viewでの処理
<!-- app/views/users/index.html.erb -->
<h1>ユーザー一覧</h1>
<ul>
<% @users.each do |user| %>
<li>
<strong><%= user.name %></strong>
(<%= user.email %>)
</li>
<% end %>
</ul>
ERBの処理:
-
<% @users.each do |user| %>→ ループ開始(画面に表示しない) -
<%= user.name %>→ 値を取得して表示 - 最終的にHTMLを生成
生成されるHTML:
<h1>ユーザー一覧</h1>
<ul>
<li>
<strong>田中太郎</strong>
(tanaka@example.com)
</li>
<li>
<strong>佐藤花子</strong>
(sato@example.com)
</li>
<li>
<strong>鈴木一郎</strong>
(suzuki@example.com)
</li>
</ul>
HTTPレスポンス
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 1234
Set-Cookie: _session_id=abc123xyz; path=/; HttpOnly
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Cache-Control: no-cache
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ユーザー一覧</title>
</head>
<body>
<h1>ユーザー一覧</h1>
<ul>
<li>
<strong>田中太郎</strong>
(tanaka@example.com)
</li>
<li>
<strong>佐藤花子</strong>
(sato@example.com)
</li>
<li>
<strong>鈴木一郎</strong>
(suzuki@example.com)
</li>
</ul>
</body>
</html>
レスポンスの解説
| 要素 | 値 | 意味 |
|---|---|---|
| ステータスコード | 200 OK |
成功! |
| Content-Type | text/html; charset=utf-8 |
HTMLを返す、文字コードはUTF-8 |
| Content-Length | 1234 |
データサイズ(バイト) |
| Set-Cookie | _session_id=... |
セッション情報を保存 |
| X-Frame-Options | SAMEORIGIN |
セキュリティ設定 |
| Body | <!DOCTYPE html>... |
実際のHTMLデータ |
ブラウザでの処理
- HTTPレスポンスを受信
-
Content-Type: text/html→ 「HTMLだな」と認識 - HTMLをパース(解析)
- 画面に表示
実例2:新規ユーザー登録
シナリオ
ユーザー登録フォームに入力して「登録」ボタンをクリック
新規登録フォーム(GET)
まず、フォームを表示するためのリクエスト:
GET /users/new HTTP/1.1
Host: example.com
Controller:
def new
@user = User.new # 空のUserオブジェクト
end
View:
<!-- app/views/users/new.html.erb -->
<h1>新規ユーザー登録</h1>
<%= form_with model: @user, url: users_path do |f| %>
<div>
<%= f.label :name, "名前" %>
<%= f.text_field :name %>
</div>
<div>
<%= f.label :email, "メールアドレス" %>
<%= f.email_field :email %>
</div>
<div>
<%= f.label :password, "パスワード" %>
<%= f.password_field :password %>
</div>
<%= f.submit "登録" %>
<% end %>
生成されるHTML:
<h1>新規ユーザー登録</h1>
<form action="/users" method="post">
<input type="hidden" name="authenticity_token" value="...">
<div>
<label for="user_name">名前</label>
<input type="text" name="user[name]" id="user_name">
</div>
<div>
<label for="user_email">メールアドレス</label>
<input type="email" name="user[email]" id="user_email">
</div>
<div>
<label for="user_password">パスワード</label>
<input type="password" name="user[password]" id="user_password">
</div>
<input type="submit" value="登録">
</form>
フォーム送信(POST)
ユーザーが「登録」ボタンをクリック:
POST /users HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 156
authenticity_token=xyz789&user[name]=山田太郎&user[email]=yamada@example.com&user[password]=secret123
リクエストの解説
| 要素 | 値 | 意味 |
|---|---|---|
| HTTPメソッド | POST |
データを作成する |
| パス | /users |
ユーザーを作成 |
| Content-Type | application/x-www-form-urlencoded |
フォームデータ |
| Body | user[name]=山田太郎&... |
送信データ |
サーバー内部の処理
Railsのルーティング:
POST /users → UsersController#create
Controller:
class UsersController < ApplicationController
def create
# Step 1: パラメータからUserオブジェクトを作成
@user = User.new(user_params)
# Step 2: データベースに保存
if @user.save
# 成功:ユーザー詳細ページへリダイレクト
redirect_to @user, notice: "ユーザーを登録しました"
else
# 失敗:再度入力画面を表示
render :new, status: :unprocessable_entity
end
end
private
def user_params
# Strong Parameters:許可されたパラメータのみ受け取る
params.require(:user).permit(:name, :email, :password)
end
end
Modelでの処理:
class User < ApplicationRecord
# バリデーション
validates :name, presence: true
validates :email, presence: true, uniqueness: true
validates :password, presence: true, length: { minimum: 6 }
# パスワードの暗号化
has_secure_password
end
ActiveRecordが実行するSQL(成功時):
BEGIN
INSERT INTO `users` (`name`, `email`, `password_digest`, `created_at`, `updated_at`)
VALUES ('山田太郎', 'yamada@example.com', '$2a$12$...', '2024-01-15 10:30:00', '2024-01-15 10:30:00')
COMMIT
HTTPレスポンス(成功時)
HTTP/1.1 302 Found
Location: /users/4
Set-Cookie: _session_id=new_session_id; path=/; HttpOnly
Content-Type: text/html; charset=utf-8
<html><body>You are being <a href="/users/4">redirected</a>.</body></html>
レスポンスの解説
| 要素 | 値 | 意味 |
|---|---|---|
| ステータスコード | 302 Found |
リダイレクト(別ページへ移動) |
| Location | /users/4 |
この新しいユーザーのページへ |
| Set-Cookie | 新しいセッションID | セッション更新 |
ブラウザの処理
-
302 Foundを受信 -
Location: /users/4を見る - 自動的に
/users/4へGETリクエストを送る - ユーザー詳細ページが表示される
HTTPメソッドの使い分け
RESTful設計
RailsはREST(Representational State Transfer)という設計思想に従っています。
| HTTPメソッド | 意味 | 用途 | 例 |
|---|---|---|---|
| GET | 取得(読み取り) | ページ表示、データ取得 | ユーザー一覧、詳細表示 |
| POST | 作成 | 新規データ作成 | ユーザー登録 |
| PATCH/PUT | 更新 | 既存データ更新 | プロフィール編集 |
| DELETE | 削除 | データ削除 | アカウント削除 |
Railsでの対応
# config/routes.rb
resources :users
これで以下のルートが自動生成:
| HTTPメソッド | パス | Controller#Action | 用途 |
|---|---|---|---|
| GET | /users | users#index | 一覧表示 |
| GET | /users/new | users#new | 新規作成フォーム |
| POST | /users | users#create | 新規作成実行 |
| GET | /users/:id | users#show | 詳細表示 |
| GET | /users/:id/edit | users#edit | 編集フォーム |
| PATCH/PUT | /users/:id | users#update | 更新実行 |
| DELETE | /users/:id | users#destroy | 削除実行 |
HTTPステータスコード
サーバーは処理結果をステータスコードで返します。
主要なステータスコード
| コード | 意味 | 説明 | 例 |
|---|---|---|---|
| 200 | OK | 成功 | ページ正常表示 |
| 201 | Created | 作成成功 | ユーザー登録成功 |
| 204 | No Content | 成功(返すデータなし) | 削除成功 |
| 301 | Moved Permanently | 恒久的リダイレクト | ドメイン変更 |
| 302 | Found | 一時的リダイレクト | ログイン後の遷移 |
| 400 | Bad Request | 不正なリクエスト | パラメータエラー |
| 401 | Unauthorized | 認証が必要 | ログインが必要 |
| 403 | Forbidden | アクセス権限なし | 管理者のみ |
| 404 | Not Found | ページが存在しない | 存在しないURL |
| 422 | Unprocessable Entity | バリデーションエラー | 入力エラー |
| 500 | Internal Server Error | サーバーエラー | バグ、例外 |
| 503 | Service Unavailable | サービス利用不可 | メンテナンス中 |
Railsでの使い方
# 成功(200 OK)
def show
@user = User.find(params[:id])
# デフォルトで200が返る
end
# リダイレクト(302 Found)
def create
if @user.save
redirect_to @user # 302
end
end
# バリデーションエラー(422 Unprocessable Entity)
def create
if @user.save
redirect_to @user
else
render :new, status: :unprocessable_entity # 422
end
end
# Not Found(404)
def show
@user = User.find_by(id: params[:id])
if @user.nil?
render file: "#{Rails.root}/public/404.html", status: :not_found
end
end
ActiveRecordの仕組み
ActiveRecordは、RailsのORM(Object-Relational Mapping)です。
ORMとは
ORMは、データベースのテーブルとRubyのオブジェクトを対応づける技術です。
データベース(SQL) ⇔ Ruby(オブジェクト)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
usersテーブル ⇔ Userクラス
1レコード(行) ⇔ 1つのUserオブジェクト
カラム(列) ⇔ 属性(name、email等)
ActiveRecordの魔法
SQL を書かなくてもデータベース操作ができます。
例1:全件取得
ActiveRecord:
users = User.all
実行されるSQL:
SELECT `users`.* FROM `users`
例2:条件検索
ActiveRecord:
adults = User.where("age >= ?", 20)
実行されるSQL:
SELECT `users`.* FROM `users` WHERE (age >= 20)
例3:新規作成
ActiveRecord:
user = User.create(name: "太郎", email: "taro@example.com")
実行されるSQL:
INSERT INTO `users` (`name`, `email`, `created_at`, `updated_at`)
VALUES ('太郎', 'taro@example.com', '2024-01-15 10:00:00', '2024-01-15 10:00:00')
例4:更新
ActiveRecord:
user = User.find(1)
user.update(name: "新しい名前")
実行されるSQL:
UPDATE `users` SET `name` = '新しい名前', `updated_at` = '2024-01-15 10:05:00'
WHERE `users`.`id` = 1
例5:削除
ActiveRecord:
user = User.find(1)
user.destroy
実行されるSQL:
DELETE FROM `users` WHERE `users`.`id` = 1
ActiveRecordの主要メソッド
検索系
# 全件取得
User.all
# 1件取得(IDで)
User.find(1)
# 1件取得(条件で)
User.find_by(email: "test@example.com")
# 複数件取得(条件で)
User.where(age: 20)
User.where("age > ?", 20)
# 並び替え
User.order(created_at: :desc)
User.order("name ASC")
# 件数制限
User.limit(10)
# 組み合わせ
User.where("age > ?", 20).order(created_at: :desc).limit(10)
作成系
# 方法1:new + save
user = User.new(name: "太郎", email: "taro@example.com")
user.save
# 方法2:create(newとsaveを同時に)
User.create(name: "太郎", email: "taro@example.com")
# 方法3:create!(失敗時に例外)
User.create!(name: "太郎", email: "taro@example.com")
更新系
user = User.find(1)
# 方法1:属性変更 + save
user.name = "新しい名前"
user.save
# 方法2:update
user.update(name: "新しい名前")
# 方法3:update!(失敗時に例外)
user.update!(name: "新しい名前")
削除系
user = User.find(1)
# 削除
user.destroy
# または
User.destroy(1)
# 複数削除
User.where("age < ?", 18).destroy_all
バリデーション
ActiveRecordは、データベースに保存する前にバリデーション(検証)を行います。
class User < ApplicationRecord
# 存在チェック
validates :name, presence: true
# 一意性チェック
validates :email, uniqueness: true
# 長さチェック
validates :password, length: { minimum: 6 }
# 形式チェック
validates :email, format: { with: /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i }
# カスタムバリデーション
validate :custom_validation
private
def custom_validation
if name == "admin"
errors.add(:name, "adminは使用できません")
end
end
end
使い方:
user = User.new(name: "", email: "invalid")
if user.save
# 保存成功
else
# 保存失敗
puts user.errors.full_messages
# => ["Name can't be blank", "Email is invalid"]
end
# valid? で事前チェック
user.valid? # => false
user.errors.full_messages # エラーメッセージの配列
ERBテンプレートの仕組み
ERB(Embedded Ruby)は、HTMLにRubyコードを埋め込むテンプレートエンジンです。
ERBの基本文法
<%# これはコメント(表示されない) %>
<% # Rubyコードを実行(画面に表示しない) %>
<% if @user.admin? %>
<p>管理者です</p>
<% end %>
<%= # Rubyコードを実行して結果を表示 %>
<p>こんにちは、<%= @user.name %>さん</p>
<%== # HTMLエスケープせずに表示(XSS注意!) %>
<%== @post.body_html %>
データの埋め込み
Controller:
def index
@users = User.all
end
View:
<h1>ユーザー一覧(全<%= @users.count %>名)</h1>
<table>
<thead>
<tr>
<th>ID</th>
<th>名前</th>
<th>メール</th>
<th>登録日</th>
</tr>
</thead>
<tbody>
<% @users.each do |user| %>
<tr>
<td><%= user.id %></td>
<td><%= user.name %></td>
<td><%= user.email %></td>
<td><%= user.created_at.strftime("%Y年%m月%d日") %></td>
</tr>
<% end %>
</tbody>
</table>
条件分岐
<% if @user.admin? %>
<p>管理者メニュー</p>
<%= link_to "ユーザー管理", admin_users_path %>
<% elsif @user.premium? %>
<p>プレミアム会員</p>
<%= link_to "特典ページ", premium_path %>
<% else %>
<p>一般会員</p>
<%= link_to "アップグレード", upgrade_path %>
<% end %>
ループ処理
<!-- each -->
<% @posts.each do |post| %>
<article>
<h2><%= post.title %></h2>
<p><%= post.body %></p>
</article>
<% end %>
<!-- each_with_index -->
<% @items.each_with_index do |item, index| %>
<p><%= index + 1 %>. <%= item.name %></p>
<% end %>
<!-- times -->
<% 5.times do |i| %>
<p>繰り返し <%= i + 1 %></p>
<% end %>
パーシャル(部分テンプレート)
共通部分を切り出して再利用できます。
_user.html.erb:
<div class="user-card">
<h3><%= user.name %></h3>
<p><%= user.email %></p>
</div>
index.html.erb:
<h1>ユーザー一覧</h1>
<% @users.each do |user| %>
<%= render 'user', user: user %>
<% end %>
<!-- または短縮記法 -->
<%= render @users %>
ブラウザの開発者ツールで確認
実際のHTTPリクエスト/レスポンスは、ブラウザの開発者ツールで確認できます。
確認方法
-
開発者ツールを開く
- Chrome/Edge:F12 または Ctrl+Shift+I(Mac: Cmd+Option+I)
- Firefox:F12
- Safari:Cmd+Option+I
-
Networkタブを選択
-
ページをリロード
-
リクエストをクリックして詳細を見る
- Headers:リクエスト/レスポンスのヘッダー
- Preview:レスポンスのプレビュー
- Response:レスポンスの生データ
- Timing:処理時間
確認できる情報
- HTTPメソッド(GET、POST等)
- ステータスコード(200、404等)
- リクエストヘッダー
- レスポンスヘッダー
- Cookie
- レスポンスボディ(HTML、JSON等)
- 処理時間
まとめ
HTTPリクエスト/レスポンスの流れ
1. クライアント:HTTPリクエスト送信
├─ HTTPメソッド(GET、POST等)
├─ パス(/users等)
├─ ヘッダー(Cookie等)
└─ ボディ(POSTデータ)
↓
2. サーバー:リクエスト処理
├─ ルーティング
├─ Controller実行
├─ Model(ActiveRecord)でDB操作
└─ View(ERB)でHTML生成
↓
3. サーバー:HTTPレスポンス返却
├─ ステータスコード(200、302等)
├─ ヘッダー(Content-Type等)
└─ ボディ(HTML、JSON等)
↓
4. クライアント:レスポンス受信・表示
ActiveRecordの役割
- SQLを書かずにデータベース操作
- Rubyのオブジェクトとして扱える
- バリデーション機能
- 関連付け(has_many等)
ERBテンプレートの役割
- HTMLにRubyコードを埋め込む
- Controllerから渡されたデータを表示
- 条件分岐やループ処理
- 最終的にHTMLを生成
重要ポイント
- HTTPはリクエスト/レスポンスの往復
- HTTPメソッドで「何をするか」を表現
- ステータスコードで「結果」を表現
- ActiveRecordはSQLを自動生成
- ERBはHTMLテンプレートにデータを埋め込む
- Content-Typeでデータ形式を指定
シリーズ完結!
お疲れ様でした!これで3部作が完結です。
- 【記事1】Web技術の歴史とRackの正体を理解する
- 【記事2】Rails初心者が完全理解すべきMVCアーキテクチャ
- 【記事3】HTTPリクエスト/レスポンスとActiveRecordを理解する
この3つの記事で、Railsの全体像が完全に理解できたはずです!
次のステップ
さらに学びたい方へ:
- API開発:JSON形式でデータを返す
- 認証/認可:Devise、Pundit
- テスト:RSpec、Minitest
- 非同期処理:Sidekiq、Active Job
- フロントエンド:Hotwire、React、Vue
参考資料
この記事が役に立ったら、ぜひいいねやストックをお願いします!
質問やフィードバックもお気軽にコメントください 😊
最後まで読んでいただき、ありがとうございました!