bitterharvest’s diary

A Bitter Harvestは小説の題名。作者は豪州のPeter Yeldham。苦闘の末に勝ちえた偏見からの解放は命との引換になったという悲しい物語

Haskellでウェブ・アプリケーション(2)

1.ブログの構築

ウェブで紹介されているHaskell web programmingは、簡潔で分かりやすい例題である。その内容に沿って説明し、yesogの活用法を学ぶこととする。

2.エコーで返す

最初の課題は、「/echo/テキストにアクセスしたら、そのテキストを返す」というサービスを開発する。このサービスの名前をEchoとし、サービスを実現してくれるモデル・ビュー・コントローラを作成することとする。まずは、コントローラの作成から始める(なお、このサービスは情報を蓄積しないので、モデルは必要ではない)。
現在のディレクトリがプロジェクトのディレクトリ(この場合にはyosog)にあることを確認して、次のコマンドを入力する。

yesod add-handler

route名、routeのパターン、メソッドのリストが要求されるので、それぞれ、Echo, /echo/#String, GETを入力する。ここで、route名はサービス名、routeのパターンはこのサービスを利用するときのURL(ここでは、~/echo/文字列となる)。メソッドはサービスの機能で、ここでは文字列の入力である。要求を受けたときの応答は次のようになる。

Name of route (without trailing R): Echo
Enter route pattern (ex: /entry/#EntryId): /echo/#String
Enter space-separated list of methods (ex: GET POST): GET

上記の応答によって以下の作業がなされる
1) config/routeファイルに次の事項が追加される。これらは、URLのパターン、ハンドラーの名前、HTTPのメソッドである。

/echo/#String EchoR GET

2) Handler/Echo.hsのファイルが作成される。
3) Application.hsにimport Handler.Echoが追加される。
4) cabalファイルにHandler.Echoが宣言される。
但し、Handler/Echo.hsのファイルは未完成なので、この部分

getEchoR = error "Not yet implemented: getEchoR"

を、次のように置き換える。これは、入力されたテキストをそのまま表示する。本来はビューで実現される機能だが、取りあえず、コントローラのところで、表示も含めて行うようにする。

getEchoR theText = defaultLayout [whamlet|<h1>#{theText}|]

再びコンパイルして展開する。

cabal install
yesod devel

ブラウザからホームページ
localhost:3000/echo/Yesod%20rocks!
にアクセスすると%20がスペースに変換され、Yesod rocks!と出力される。

3.外部からの攻撃に強力

外部から悪意があってこのサービスにアタックしてきたとする。今、文字以外のコードが含まれた次のものでアクセスされたとする。

http://localhost:3000/echo/I’m <script>alert(\”Bad!\”);</script>

この時、ホームページにはnot foundと出力され、このサービスに侵入されることはない。
これは、Haskellが型言語であることが功を奏している。少し詳しく説明すると、次のようになっている。
URL* typeで与えられたサービス要求は、String typeに変換され、さらに、Html typeへと変換される。その様子を示したのが以下である。この変換の過程の中で、String typeに属さないものは拒絶される。上記の例では、scriptを定義している部分がそうである(なお、これはHtml typeではある)。

http://localhost:3000/echo/some%20text%3Ca%3E    :: URL

          “some text<a>::String

     “som text &amp;lt;a&amp;gt;”                ::Html

3.おめかしする

プルグラムの内容をもう少し洗練化し、構造化するために、次の2点を追加する。
1) StringからData.Textに型を変える。
2) Handlerの中に記述されているビューの部分をtemplateディレクトリに移す。
1)を実行するために、Foundations.hsに

import Data.Text

を追加する。
config/routesを

/echo/#Text EchoR GET

に変更する。
また、Handler/Echo.hsを

module Handler.Echo where
import Import

getEchoR :: Text -> Handler Html
getEchoR theText = defaultLayout [whamlet|<h1>#{theText}|]

に変更する。
2)を実現するために、ファイルtemplates/echo.hamletを次のように

<h1> #{theText}

と作成する。
先ほど変更したHandler/Echo.hsを、さらに、

module Handler.Echo where
import Import

getEchoR :: Text -> Handler Html
getEchoR theText = defaultLayout $(widgetFile "echo")

に変更する。