振り返り
前回 poketo7878-dev.hatenablog.com
までにRouterで複数URLに対応したHTTPサーバーを建てられるようになりました。 しかし、出力されるのは常に文字列でHTMLのページではありませんでした。
今回はHandlebarというテンプレートエンジンを利用してHTMLページをユーザーに表示できるようにしました。
handlebar-ironをCargo.tomlに追加
前回までと同様にCargo.toml
に以下のdependencyを追加します。
handlebars = "0.20.5"
また、今回はHTMLページでformを表示し、POSTしてみたいと思うので、 formやURLなどのパラメータをパース及び取得するためのcrateもdependencyに追加しておきます。
params = "0.4.1"
全体
まずはコードの全体です
extern crate iron; extern crate router; extern crate handlebars_iron as hbs; extern crate params; use std::collections::HashMap; use std::error::Error; use iron::prelude::*; use iron::status; use router::{Router, url_for}; use hbs::{Template, HandlebarsEngine, DirectorySource}; fn main() { fn top_handler(req: &mut Request) -> IronResult<Response> { let mut resp = Response::new(); let mut data = HashMap::new(); data.insert(String::from("greeting_path"), format!("{}", url_for(req, "greeting", HashMap::new()))); resp.set_mut(Template::new("index", data)).set_mut(status::Ok); return Ok(resp); } fn greet_handler(req: &mut Request) -> IronResult<Response> { use params::{Params, Value}; let map = req.get_ref::<Params>().unwrap(); return match map.find(&["name"]) { Some(&Value::String(ref name)) => { Ok(Response::with( (status::Ok, format!("Hello {}", name).as_str()))) }, _ => Ok(Response::with((status::Ok, "Hello world"))) } } //Create Router let mut router = Router::new(); router.get("/", top_handler, "index"); router.post("/greet", greet_handler, "greeting"); //Create Chain let mut chain = Chain::new(router); // Add HandlerbarsEngine to middleware Chain let mut hbse = HandlebarsEngine::new(); hbse.add(Box::new( DirectorySource::new("./src/templates/", ".hbs"))); if let Err(r) = hbse.reload() { panic!("{}", r.description()); } chain.link_after(hbse); println!("Listen on localhost:3000"); Iron::new(chain).http("localhost:3000").unwrap(); }
細かく見ていきましょう
Router
//Create Router let mut router = Router::new(); router.get("/", top_handler, "index"); router.post("/greet", greet_handler, "greeting");
前回同様Routerを作成しています。今回はgreetの名前はurlの一部ではなくpostのパラメータとして送信することにしました。
Chain
let mut chain = Chain::new(router);
前回は作成したRouterを直接HandlerとしてIronに渡していましたが、今回はChain
というオブジェクトを作成しています。
これはMiddlewareの連鎖を表現したオブジェクトで、ここにミドルウェアをつなげていくことができます。
HandlebarEngine
// Add HandlerbarsEngine to middleware Chain let mut hbse = HandlebarsEngine::new(); hbse.add(Box::new( DirectorySource::new("./src/templates/", ".hbs"))); if let Err(r) = hbse.reload() { panic!("{}", r.description()); } chain.link_after(hbse);
ここではHandlebarsEngineを作成し、 Handlebarのテンプレートファイルを設置するディレクトリのルートディレクトリ、 Handlebarのファイルとして扱うファイルの拡張子を指定しています
今回の場合は./src/templates/
をルートディレクトリとして指定しています、また拡張子は.hbs
としています。
このようにディレクトリを指定したら
if let Err(r) = hbse.reload() { panic!("{}", r.description()); }
ディレクトリ中のファイルをロードしています。
そして最後に先ほどのChainの末尾にHandlebarsEngineを追加しています。
chain.link_after(hbse);
テンプレートの描画
fn top_handler(req: &mut Request) -> IronResult<Response> { let mut resp = Response::new(); let mut data = HashMap::new(); data.insert(String::from("greeting_path"), format!("{}", url_for(req, "greeting", HashMap::new()))); resp.set_mut(Template::new("index", data)).set_mut(status::Ok); return Ok(resp); }
indexページのハンドラ中では、レスポンスのボディとしてTemplate::new("index", data)
を指定しています。
このようにすることで、さきほどしたルートディレクトリからの相対パスで指定したhandlebarファイルを指定できます。
ファイル中で利用する変数のデータはJSONに変換可能なデータ構造、今回の場合はHashMap等で渡すことができます。 参考URL
トップページのテンプレートファイルは
<h1>Hello, world</h1> <form action={{greeting_path}} method="POST"> <input type="text" name="name"/> <input type="submit" value="Hello!"/> </form>
このようにformのaction URLをgreetingのページのURLに設定しています。
このようにすると
のようにformの表示されたHTMLページが描画されます。
POSTのパラメータの処理
さて、先ほどのページのformに値をいれてsubmitするとその値はnameというパラメータ名でPOSTされてきます。
この値をハンドラ中で利用するには
let map = req.get_ref::<Params>().unwrap(); return match map.find(&["name"]) { Some(&Value::String(ref name)) => { Ok(Response::with( (status::Ok, format!("Hello {}", name).as_str()))) }, _ => Ok(Response::with((status::Ok, "Hello world"))) }
このように、まずリクエスト中からParams型の値を取り出し、 Paramsに対してfindメソッドで値を検索した結果のOptionalをmatchすることで利用できます。
今回の場合は取り出してきたnameを使ってHello 名前という形で表示しています。
まとめ
今回は前回までテキストのみの表示だったページをHandlebarのテンプレートエンジンを用いることでHTMLの表示を可能にしました。
また、POST等のパラメータを取得するためのparams
crateを利用しパラメータをハンドラ中で取得できるようになりました。
次回はHTMLだけでなくJSONなどのデータ型をユーザーからのリクエストに応じて返せるようにしたいと思います。