Web基礎チュートリアル

Created by Tim McEwan, @tjmcewan / Translated by Daiki Kato, @DAIKI__0381

目標

情報がどのようにWebブラウザとやり取りされているか考えたことがありますか?このチュートリアルでは、HTTPについて調べることで、その仕組みを見ていきます。

今回は、Sinatraをツールとして使い、Webの基本的な原則をいくつか紹介します。Sinatraは、Rubyを使って最小限の労力でWebアプリを作成するための小さなフレームワークです。Sinatraで作られたものはこちらにリストアップされています。

SinatraはRailsとは異なります。どちらもWebアプリを書くのに役立つフレームワークですが、Sinatraには少ない機能と少ない魔法しかありません。

はじめに

HTTPは、アプリ(Rails Girlsアプリなど)とブラウザの間で情報を送るために使用されます。 HTTPによる通信の基本は、リクエストとレスポンスがペアになっています。リクエストはブラウザからサーバー(あなたのアプリなど)に送られ、レスポンスはサーバーからあなたのブラウザに返され、ユーザーが閲覧することができます。

ブラウザでリクエストを送るには、URLを使用する必要があります。URLには、サーバーが正しいレスポンスを送れるように、どのような情報をリクエストしているのか、多くの情報が含まれています。

URLには、以下の内容が含まれます;

Breakdown of a URL

URLは欲しいリソースを示しますが、そのリソースに対して実行する必要のあるアクションは、HTTP動詞を使って指定する必要があります。

HTTPの代表的な動詞は

Rails Girlsアプリで投稿やノートの表示、作成、編集、削除をできるようにしたとき、これらの動詞をすべて使ったことでしょう。

サーバーへのリクエストは、URLとHTTP動詞の両方を含める必要があります。

以下の演習では、どのようにブラウザがアプリと通信するのかを示すために、リソースフォルダ名とHTTP動詞の組み合わせを使った簡単なコーヒーリストアプリケーションを作成します。

あなたのアプリは、ブラウザ上でこのように表示されます:

Sinatra Coffee App

0. Sinatraをインストール - “Hello World”

まずはSinatraを起動させるところから始めましょう。

ターミナルで、gemをインストールします:

gem install sinatra
gem install sinatra-contrib

1. “Hello World”

app.rbというファイルを作成し、以下をSublime Textに貼り付けます:

require "sinatra"
require "sinatra/reloader"

get "/" do
"Hello world!"
end

そして、ターミナルに戻って、このコードを実行します:

ruby app.rb

次のURLにアクセスして、現在のサイトを確認します: http://localhost:4567

今ブラウザに表示されている情報は、GETリクエストに対するレスポンスです。

http://localhost:4567 にアクセスすると、SinatraサーバーにGETリクエストを送信することになります。

ブラウザに表示されるのは、Sinatraサーバーからのレスポンスです。

”/”と書いたところは、ルートURLを指定していますが、好きなパス名にすることができます。

同じファイルに、上のブロックと同じ構文を使用して、さらに訪問するページを作ってみてください。好きなだけページを追加して、好きなように表示させることができます。

e.g.

get "/page-name" do
 "This is text on the page"
 end

必要であれば、コマンドプロンプトでCtrl+Cを押してアプリを停止することができます。(Railsアプリの場合と同様です!)しかし、変更を確認するために停止したり開始したりする必要はありません。

もし行き詰まったら、app.rbこのファイルのようになっていることを確認してください。

3. HTML フォーム - パラメータを取得

アプリにコーヒーを掲載するには、どのコーヒーが欲しいのか、いくらなのかを送信するためのHTMLフォームが必要です。

これは、いくつかの情報を含むリクエストをSinatraサーバーに送信することを意味します。

サーバーに情報を送るには、フォームを使用します。

先程のget "/"を次のように置き換えてください:

get "/" do
"
<html>
<body>
<form action='/' method='get'>
What: <input name='what'>
Cost: <input name='cost'>
<button type='submit'>add coffee</button>
</form>
<!-- coffees go here -->
</body>
</html>
"
end

分かりやすくするために、このフォームでは同じURL(”/”)に情報を送信しています。

ブラウザを更新すると、先程作成したフォームが表示されるはずです。

では、フォームを送信したときに、ブラウザがサーバーに送信する内容を確認してみましょう。フォームにテキストを入力し、「add coffee」ボタンをクリックします。ターミナルに戻ってSinatraのログをチェックすると、次のようなものが表示されるはずです:

GET /?what=flat+white&cost=3.50 HTTP/1.1

Coach: パラメータ名がどこから来ているのか、クエスチョンマークは何をしているのかについて話しましょう。

なかなかうまくいかない場合は、このファイルのようなコードになるようにしてください。

4. Webインスペクタ - リクエストヘッダー

ブラウザで、Webコンソールを開きます。(ほとんどのブラウザでは、ページ上で右クリックし、「検証」を選択することで開くことができます。)Chromeを使用している場合は、「ネットワーク」タブを探してください。

ブラウザを更新し、Webインスペクタで「localhost」の行をクリックし、「ヘッダー」タブで「ソースを表示」をクリックします。これと似たようなものが表示されるはずです:

GET / HTTP/1.1
Host: localhost:4567
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.70 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8

Coach: HTTPヘッダーとは何か、その意味を説明しましょう。

注目すべきは1行目で、HTTP動詞と呼び出されたURLを示しています。

Note: もしSinatraのログに”backup from WEBrick”(WEBrickはRubyに組み込まれているWebサーバーです)と表示されている場合、更新するたびに複数のGETリクエストが表示されるかもしれません。実際に発行されているのは1つのリクエストだけなので、他のリクエストは無視しても大丈夫です。

5. グローバル変数

フォームからアプリにコーヒーの情報が送信されていますが、まだ何もしていません。コーヒーの情報がサーバーに送信されたら、それを保存する必要があります。

分かりやすくするために、コーヒーの詳細を変数に格納することにします。リクエスト間で利用できるように、グローバル変数を使用する必要があります。これは、Railsがモデル層を呼び出す処理を大幅に簡略化したものです。

これを app.rb のどこかに追加します(慣例では、一番上の require 行の下にあるはずです):

$coffees = []

これにより、アプリの初回起動時に空の配列が作成されます。

Hint: このグローバル変数は、サーバーが再起動するたびに空の配列にリセットされるため、あまり長い間存在することはありません。今回はSinatraのreloaderを使っているので、app.rbファイルを保存するたびにリセットされることになります。しかし、心配する必要はありません、今回の目的にはうまく合致しています。

6. 情報の受信 - コーヒーの保存

ここで、リクエストを受け取ったときに、その情報を $coffees で定義した配列に格納する必要があります。フォームに必要事項を入力して送信ボタンをクリックすると、サーバーに送信される情報がURLの末尾の?の後に配置され、サーバーに送り返されることに注意してください。

SinatraはURLの末尾からその情報を取得し、paramsというハッシュとして利用できるようにします。例えば、以下のような感じです:

params = {coffee = 'flat white', value ='2'}

paramsハッシュからそれらを取り出し、$coffeesに追加するコードを書く必要があります。

始める前に、まずフォーム内の <!-- Coffees go here --> を次のように置き換えます:

#{ $coffees.inspect }

これで、ブラウザのHTMLに$coffeesが表示され、$coffeesにコーヒーが追加されているかどうか(つまり、あなたのコードが機能しているかどうか)がわかるようになります。

Hint: グローバル変数は配列なので、行き詰まった場合は、Rubyの配列に関するドキュメントを参照してください。

もし行き詰まったら、ここをチェックしてください。

パラメータが正しく格納されていれば、ブラウザを更新して、毎回新しいハッシュが配列 $coffees に追加されることを確認できるはずです。

7. 整える

getコードの中にある大きなHTMLの塊のせいで、このアプリが何をするのかがちょっとわかりにくくなっています。これを独自のメソッドに移しましょう。

HTMLフォームをgetコードから切り取って、templateというメソッドに貼り付けます(すべてのコードを同じファイルに保存してください)。こんな感じで:

def template
# put your HTML form string here
end

ここで、getコードのフォームを、次のようにtemplateというメソッドの単純な呼び出しに置き換えてください:

get "/" do
$coffees << params
template
end

これにより、フォームを再利用することが容易になります。

Hint: 何か問題が発生した場合は、 ここをチェックしてください。

8. GETを乗り越える

コーヒーの保存のところで見たように、ブラウザを更新すると、アプリは繰り返しグローバル変数である$coffeesに情報を追加します。これは、getコードがリクエストされるたびに、URLからパラメータを保存しているためです。

これは、GETリクエストはアプリに情報を追加するようなことをしてはいけないということを言及する良いポイントです。この例のようにうまくいくこともありますが、意図しない副作用が簡単に発生してしまいます。この場合は POST リクエストを使用するのがよいでしょう。

GETリクエストはリソースの取得を求めるもので、POSTリクエストはリソースの作成を求めるものであることを覚えておいてください。

では、この知識をもとに、フォームのメソッドを POST に変更してみましょう:

Uh oh! Sinatraのエラーページを見たことのない方は、Sinatraの素敵なエラーページへようこそ。下のほうにあるメッセージにはこう書いてあるはずです:

Try this:
post '/' do
 "Hello World"
end

これは、リクエストされたルートが存在しないことを伝えるSinatraの方法です。HTTPエラー番号404 page not found として、ご存知の方もいるかもしれません。

9. 投稿を追加

Sinatraにpostコードを追加してみましょう(getは外さないでね!):

いつものように、進捗状況をこちらで確認することができます。

コーヒーを送信した後にページを更新すると、ページを読み込むためにフォームの再送信が必要であるというブラウザからの警告が表示されるはずです:

Chrome's confirm resubmission dialog

フォームを送信する際に、このような警告が表示されることがあります。この警告があることで、更新の結果について考えるようになり、同じコーヒーを不用意に何度も追加することを避けられるでしょう。

GETリクエストはリソースの取得を依頼するもので、POSTリクエストはリソースの作成を依頼するものであることを忘れないでください。GETリクエストでページを更新する場合、同じページを繰り返し表示するように要求しているだけなので、通常は問題ないでしょう。

しかし、POSTリクエストでページを更新した場合、POSTリクエストを再送することになり、更新するたびに新しいリソースを作成する可能性があります。そのため、ブラウザは更新を許可する前に警告を表示します。

10. リダイレクトを追加

このフォームの再送信問題を回避するために、POSTに対するレスポンスを受け取るとすぐに別のページを読み込むようにブラウザに指示することができます。 これは、”リダイレクト”として知られる特別なHTTPレスポンスを使用して行います。

templateメソッドの呼び出しの代わりに、ブラウザを別の場所にリダイレクトすることができます。

Sinatraでは、このようになります:

redirect "/"

まずは試してみてから、こちらを確認してください。

これは、ブラウザが移動すべき場所を指定する location ヘッダを持つ特別なリダイレクトレスポンス (HTTP 303) を送り返すものです:

HTTP/1.1 303 See Other
<>
Location: http://localhost:4567/

この動作を確認するには、ChromeのWebインスペクタ(ネットワークタブ)を見て、アプリにコーヒーを送ってみてください:

Chrome's network tab showing a post/redirect/get

最初の行は、ブラウザが POST リクエストメソッドでフォームに送信していることを示しています。受け取ったレスポンスはHTTP 303で、Locationヘッダを含んでいます。そして、その場所(ルートURLである”/”)に対して GET リクエストを発行し、そこから得られたレスポンス(これがHTMLテンプレートです)をレンダリングしています。

これで、好きなだけ更新することができます。やっていることは POST ではなく GET を使ったリクエストです。ブラウザは、ページを表示するためにフォームを送信する必要はもうありません。

これでチュートリアルは終了です。あなたは素晴らしい仕事をしました!

遊んでくれてありがとうございました!

追加ガイド

もし早く終わった、続けてみたいという方は、ぜひお試しください: