前回の記事では、
RESTとは何か
Rails2.
RESTとは、
という問いに対して、
なお、
それでは、
ステートレスなクライアントサーバ方式
RESTの原則では、
統一インターフェース(Uniform Interface)
RESTを他のアーキテクチャと区別する中心となる概念が、
インターフェースを統一化することで、
RESTfulなURI
RESTの統一インターフェースに対する、
RESTを文と考える。すると、
具体的な例を挙げてみましょう。
POST http://bookmark/links
⇒投稿する(動詞)ブックマーク(名詞)リンク(名詞)
GET http://bookmark/links/1
⇒取得する(動詞)ブックマーク(名詞)リンク(名詞)1(名詞)
GET http://bookmark/links/1.xml
⇒取得する(動詞)ブックマーク(名詞)リンク(名詞)1(名詞)XML(名詞)
DELETE http://bookmark/links/1
⇒削除する(動詞)ブックマーク(名詞)リンク(名詞)1(名詞)
この観点からすると、
GET http://bookmark/links/destroy/1
⇒取得する(動詞)ブックマーク(名詞)リンク(名詞)削除(動詞)1(名詞)
統一インターフェースとしてのRESTfulなURIとは、
RESTのイメージだけでもつかんでいただけましたでしょうか?
Rails2.
Railsで作る小さなRESTful Webアプリケーション
さてこのあたりで、
それでは早速、
rails minicious
miniciousのDBスキーマ設計
ソーシャルブックマークにはどんなデータが必要でしょうか?
del.
Railsでのスキーマ定義を行うMigrationファイルを作成します。
./script/generate migration CreateTables
上記のコマンドの実行で、
今回のMigrationでは、
t.column :カラム名, :型名
t.column :カラム名, :型名
.
.
.
Rails2.
t.型名 :カラム名, :カラム名,.... (可変長の指定)
:型名ではなく、
SexyMigrationはそれだけではありません。
:has_
t.column :外部キーID, :integer
t.column :外部キーID, :integer
.
.
t.references :関連元テーブル名(単数形), 関連元テーブル名(単数形),...
t.
それではこれらの機能をもちいて、
class CreateTables < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.text :login, :password
t.timestamps
end
create_table :links do |t|
t.references :user
t.text :url, :notes
t.timestamps
end
create_table :tags do |t|
t.text :name
t.timestamps
end
create_table :assorts do |t|
t.references :link, :tag
t.timestamps
end
end
def self.down
drop_table :users
drop_table :links
drop_table :tags
drop_table :assorts
end
end
最初のDBスキーマ設計ができました。rakeコマンドを用いて、
rake db:migrate
Railsでのスキーマ設計はここで終了ではなく、
Modelの作成と関連の指定
同様にモデルの作成と、
さきにMigrationファイルを一括して作成したため、
./script/generate model user --skip-migration ./script/generate model link --skip-migration ./script/generate model tag --skip-migration ./script/generate model assort --skip-migration
class User < ActiveRecord::Base
has_many :links
end
class Link < ActiveRecord::Base
has_many :assorts
has_many :tags, :through=>:assorts
belongs_to :user
end
class Tag < ActiveRecord::Base
has_many :assorts
has_many :links, :through=>:assorts
end
class Assort < ActiveRecord::Base
belongs_to :link
belongs_to :tag
end
RESTful scaffold
前回の記事でも利用しましたが、
./script/plugin install scaffolding ./script/generate scaffold link url:string notes:text --skip-migration
RESTfulなログイン
実は、
手が止まるのは、
RESTの概念と、
今回述べる一つの解は、
- WebブラウザからのHTMLフォーム経由のアクセスの際はHTTPセッションを利用する
- RESTfulなURIに対するXMLアクセスの際はHTTPヘッダによるベーシック認証を利用する
サーバ側で状態を持たないはずのRESTでセッションを使ってしまっていますが、
セッションによるログイン
具体例を見てみましょう。
HTML経由のアクセスの場合はHTTPセッションを使います。ログイン用のモデルを持たない、
class SessionsController < ApplicationController
def create
if self.current_user = User.authenticate(params[:user][:login], params[:user][:password])
flash[:notice] = "Wellcome #{current_user.login}"[:wellcome_message]
redirect_to home_url
else
flash[:notice] = "Invalid login or password, try again."[:invalid_login_message]
redirect_to login_url
end
end
def destroy
session.delete
cookies.delete :login_token
flash[:notice] = "You have been logged out."[:logged_out_message]
redirect_to login_url
end
end
SessionControllerを使うために必要なRouting設定は以下のようになります。前回も少し触れましたが、
ActionController::Routing::Routes.draw do |map|
map.home '', :controller => 'links', :action => 'index'
map.resources :links
map.resource :session
map.login 'login', :controller => 'sessions', :action => 'new'
map.logout 'logout', :controller => 'sessions', :action => 'destroy'
end
rakeのroutesコマンドで、
通常、
% rake routes (in /home/user/rails/minicious) home / {:controller=>"links", :action=>"index"} links GET /links {:controller=>"links", :action=>"index"} formatted_links GET /links.:format {:controller=>"links", :action=>"index"} POST /links {:controller=>"links", :action=>"create"} POST /links.:format {:controller=>"links", :action=>"create"} new_link GET /links/new {:controller=>"links", :action=>"new"} formatted_new_link GET /links/new.:format {:controller=>"links", :action=>"new"} edit_link GET /links/:id/edit {:controller=>"links", :action=>"edit"} formatted_edit_link GET /links/:id/edit.:format {:controller=>"links", :action=>"edit"} link GET /links/:id {:controller=>"links", :action=>"show"} formatted_link GET /links/:id.:format {:controller=>"links", :action=>"show"} PUT /links/:id {:controller=>"links", :action=>"update"} PUT /links/:id.:format {:controller=>"links", :action=>"update"} DELETE /links/:id {:controller=>"links", :action=>"destroy"} DELETE /links/:id.:format {:controller=>"links", :action=>"destroy"} POST /session {:controller=>"sessions", :action=>"create"} POST /session.:format {:controller=>"sessions", :action=>"create"} new_session GET /session/new {:controller=>"sessions", :action=>"new"} formatted_new_session GET /session/new.:format {:controller=>"sessions", :action=>"new"} edit_session GET /session/edit {:controller=>"sessions", :action=>"edit"} formatted_edit_session GET /session/edit.:format {:controller=>"sessions", :action=>"edit"} session GET /session {:controller=>"sessions", :action=>"show"} formatted_session GET /session.:format {:controller=>"sessions", :action=>"show"} PUT /session {:controller=>"sessions", :action=>"update"} PUT /session.:format {:controller=>"sessions", :action=>"update"} DELETE /session {:controller=>"sessions", :action=>"destroy"} DELETE /session.:format {:controller=>"sessions", :action=>"destroy"} login /login {:controller=>"sessions", :action=>"new"} logout /logout {:controller=>"sessions", :action=>"destroy"}
Basic認証によるログイン
次は、
ここで特に重要なのはlogin_
class ApplicationController < ActionController::Base
helper :all
helper_method :current_user_id, :logged_in?
protect_from_forgery
protected
# XML でのアクセスの場合、HTTP のヘッダを利用して RESTful なリクエストの認証を行います
# 認証できなかった場合、エラーを返します
def login_required
respond_to do |format|
format.html { redirect_to login_path }
format.xml do
if user = authenticate_with_http_basic { |u, p| User.authenticate(u, p) }
@current_user = user
else
request_http_basic_authentication
end
end
end unless logged_in?
end
def current_user_id
if logged_in?
@current_user.login
else
"guest"
end
end
def current_user=(value)
if @current_user = value
session[:user_id] = @current_user.id
end
end
def current_user
@current_user ||= ((session[:user_id] && User.find_by_id(session[:user_id])) || 0)
end
def logged_in?
current_user != 0
end
end
それではlogin_
まず、
また、
認証を利用する側の、
class LinksController < ApplicationController
before_filter :login_required
.
.
end
RESTfulなURIに対するXMLリソースへのアクセス
ここまでの段階で、
なにはともあれ、
% script/server
Basic認証は、
loginのIDがuser、
% ruby -e "require 'base64';puts Base64.encode64('user:user')" % dXNlcjp1c2Vy
ここでは、
% wget http://localhost:3000/links.xml -d --header="Authorization: Basic dXNlcjp1c2Vy" -O - (略) ---request begin--- GET /links.xml HTTP/1.0 User-Agent: Wget/1.10.2 Accept: */* Host: localhost:3000 Connection: Keep-Alive Authorization: Basic dXNlcjp1c2Vy (略) ---response begin--- HTTP/1.1 200 OK Connection: close Date: Thu, 01 May 2008 19:44:49 GMT Set-Cookie: _minicious_session=BAh7BiIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo%250ASGFzaHsABjoKQHVzZWR7AA%253D%253D--8b433eee9710a010fb4879e70a25b36bfd7d7f23; path=/ Status: 200 OK X-Runtime: 0.05679 ETag: "00bb9b2de47ff02123610da2ec9592f8" Cache-Control: private, max-age=0, must-revalidate Server: Mongrel 1.1.4 Content-Type: application/xml; charset=utf-8 Content-Length: 351 (略) <?xml version="1.0" encoding="UTF-8"?> <links type="array"> <link> <created-at type="datetime">2008-04-29T19:31:03+09:00</created-at> <id type="integer">1</id> <notes></notes> <updated-at type="datetime">2008-04-29T19:31:03+09:00</updated-at> <url>http://gihyo.jp</url> </link> </links>
なお、
% wget http://user:user@localhost:3000/links.xml -O -
ERbファイルの修正
最後に、
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<title>minicious: <%= controller.action_name %></title>
<%= stylesheet_link_tag 'scaffold' %>
</head>
<body>
<h2 align="left">
<%= link_to 'minicious', home_path %>
</h2>
<table width="100%">
<tr>
<td align="left" width="50%"><%= link_to 'post', new_link_path %></td>
<td align="right" width="50%">
logged in as <%= current_user_id %> |
<% unless logged_in?%><%= link_to 'login', login_path %> |<% end %>
<%= link_to 'logout', logout_path %>
</td>
</table>
<hr>
<p style="color: green"><%= flash[:notice] %></p>
<%= yield %>
</body>
</html>
<h1>links</h1>
<table>
<% for link in @links %>
<tr>
<td><%= link_to link.url, link.url %></td>
<td><%=h link.notes %></td>
<td><%= link_to 'edit', edit_link_path(link) %> /</td>
<td><%= link_to 'destroy', link, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>
<br />
画面イメージとソースコード
以下がログイン画面です。ログイン画面で

作成したリンクの一覧は以下のように表示されます。

なお、
- minicious_
rails2_ 0002_ 2. zip
2008年5月20日18時以前のアーカイブには誤りがありました。それ以前に取得された方はお手数ですが再度取得していただくようお願いいたします。
現在ユーザ管理の画面が無いため、別のユーザアカウントを作成したい場合、直接 SQL 文でインサートを行うか、コンソールでUserオブジェクトを作成する必要があります。
以下は、
>> User.create({:login=>"gihyo", :password=>"gihyo"})
=> #<User id: 2, login: "gihyo", password: "gihyo", created_at: "2008-05-15 12:21:34", updated_at: "2008-05-15 12:21:34">
まとめと次回予告
Rails2.
次回は、