
Ironは、Rustプログラミング言語で記述された高レベルのWebフレームワークであり、別の悪名高いハイパーライブラリに基づいて構築されています。 IronはRustを最大限に活用するように設計されています。
哲学
鉄は、可能な限り拡張性の原則に基づいて構築されています。
彼は自分の機能を拡張するための概念を紹介します:
- 「中間」タイプ-クエリ処理でエンドツーエンド機能を実装するために使用されます。
- 修飾子-人間工学に基づいた要求と応答の変更に使用
方法。
この記事では、修飾子と中間型の基本部分について学習します。
公式サイトIron
プロジェクト作成
まず、次のコマンドを使用して、Cargoを使用してプロジェクトを作成します。
cargo new rust-iron-tutorial
次に、 Cargo.toml
[dependencies]
セクションに依存関係iron = "0.4.0"
を追加します。
Ironを使用して最初のプログラムを作成します
Ironを使用した最初の単純なRustプログラムを作成してみましょう。これは、ポート3000上のすべての要求に「Hello habrahabr!」というテキストで応答します。
extern crate iron; use iron::prelude::*; use iron::status; fn main() { Iron::new(|_: &mut Request| { Ok(Response::with((status::Ok, "Hello habrahabr!\n"))) }).http("localhost:3000").unwrap(); }
cargo run
コマンドを使用してコードを実行し、コンパイルが完了してプログラムが起動したら、たとえばcurlを使用してサービスをテストします。
[loomaclin@loomaclin ~]$ curl localhost:3000 Hello habrahabr!
ここで何が起こっているのかを理解するためにプログラムを見てみましょう。 プログラムの最初の行は、 iron
パッケージをインポートします。
2行目では、 Request
などの最も重要なタイプのセットを含むプレリュードモジュールが接続されました
Response
、 IronRequest
、 IronResult
、 IronError
およびIron
。 3行目は、要求に応答するためのコードのリストを含むstatus
モジュールを接続します。 Iron::new
は、Ironの新しいインスタンスを作成します。これは、サーバーのベースオブジェクトです。 Handler
を実装するオブジェクトを引数として受け取りHandler
。 このケースでは、引数が送信されたリクエストへの可変参照であるクロージャーを渡します。
応答ヘッダーでmime-typeを指定します
ほとんどの場合、Webサービス(SOAP、REST)を構築するとき、含まれるコンテンツのタイプを示す応答を送信する必要があります。 このため、Ironには特別なツールがあります。
以下を行ってください。
対応する構造を接続します:
use iron::mime::Mime;
名前content_type
をバインドし、接続されたMime
タイプを使用して解析されたタイプの値を保存します。
let content_type = "application/json".parse::<Mime>().unwrap();
リクエストへの応答行を次のように変更します。
Ok(Response::with((content_type, status::Ok, "{}")))
プログラムを起動し、パフォーマンスを確認します。
[loomaclin@loomaclin ~]$ curl -v localhost:3000 * Rebuilt URL to: localhost:3000/ * Trying ::1... * Connected to localhost (::1) port 3000 (#0) > GET / HTTP/1.1 > Host: localhost:3000 > User-Agent: curl/7.49.1 > Accept: */* > < HTTP/1.1 200 OK < Content-Type: application/json < Date: Tue, 12 Jul 2016 19:53:21 GMT < Content-Length: 2 < * Connection #0 to host localhost left intact {}
応答ステータスコードを管理する
status
モジュールにあるStatusCode
列挙には、あらゆる種類のステータスコードが含まれています。 これを利用して、「クライアント」エラー404- NotFound
、リクエストに対する応答の形成に合わせて行を変更します。
Ok(Response::with((content_type, status::NotFound)))
検証:
[loomaclin@loomaclin ~]$ curl -v localhost:3000 * Rebuilt URL to: localhost:3000/ * Trying ::1... * Connected to localhost (::1) port 3000 (#0) > GET / HTTP/1.1 > Host: localhost:3000 > User-Agent: curl/7.49.1 > Accept: */* > < HTTP/1.1 404 Not Found < Content-Length: 2 < Content-Type: application/json < Date: Tue, 12 Jul 2016 20:55:40 GMT < * Connection #0 to host localhost left intact
注:実際、 status
モジュール全体は、Ironの基になっているhyper
ライブラリ内の対応する列挙型のラッパーです。
リクエストの転送
リダイレクトでは、 iron
はmodifiers
モジュールのRedirect
構造を使用します( modifiers
と混同しないでください)。 リダイレクトする必要がある宛先のURLで構成されます。
次の変更を加えて、適用してみましょう。
Redirect
構造を接続しRedirect
。
use iron::modifiers::Redirect;
status
モジュールの接続に、 Url
モジュールの接続を追加します。
use iron::{Url, status};
リダイレクトアドレスの解析された値を格納するurl
名をバインドします。
let url = Url::parse("https://habrahabr.ru/").unwrap();
Iron初期化ブロックを次のように変更します。
Iron::new(move |_: &mut Request | { Ok(Response::with((status::Found, Redirect(url.clone())))) }).http("localhost:3000").unwrap();
結果を確認します。
[loomaclin@loomaclin ~]$ curl -v localhost:3000 * Rebuilt URL to: localhost:3000/ * Trying ::1... * Connected to localhost (::1) port 3000 (#0) > GET / HTTP/1.1 > Host: localhost:3000 > User-Agent: curl/7.49.1 > Accept: */* > < HTTP/1.1 302 Found < Location: https://habrahabr.ru/ < Date: Tue, 12 Jul 2016 21:39:24 GMT < Content-Length: 0 < * Connection #0 to host localhost left intact
また、 modifiers
モジュールの別のRedirectRaw
構造体を使用することもできます。これには、構築するのに文字列のみが必要です。
HTTPリクエストのタイプを操作する
Request
構造には、受信したhttp要求のタイプを判別できるmethod
フィールドがあります。
要求の本文で送信されたデータをPut
タイプでファイルに保存するサービスを作成し、
ファイルからデータを読み取り、タイプGet
リクエストに応じてデータを転送します。
iexpect
およびitry
をさらに使用してエラー状況を処理するために、インポートされたiron
コンテナにmacro_use
属性を注釈しitry
。
#[macro_use] extern crate iron;
ファイルシステムと標準ライブラリからの入力/出力を操作するためのモジュールを接続します。
use std::io; use std::fs;
HTTPリクエストのタイプのリストを含むmethod
モジュールを接続します。
use iron::method;
受信したリクエストをreq
という名前に関連付けるようにIron
初期化ブロックを変更します。
Iron::new(|req: &mut Request| { ... ... ... }.http("localhost:3000").unwrap();
Get
およびPut
リクエストの2種類のmethod
フィールドサンプルとの比較をリクエスト処理に追加し、残りについてはBadRequest
ステータスコードの形式で回答を使用します。
Ok(match req.method { method::Get => { let f = iexpect!(fs::File::open("foo.txt").ok(), (status::Ok, "")); Response::with((status::Ok, f)) }, method::Put => { let mut f = itry!(fs::File::create("foo.txt")); itry!(io::copy(&mut req.body, &mut f)); Response::with(status::Created) }, _ => Response::with(status::BadRequest) }
Iron
iexcept
マクロiexcept
使用して、渡されたOption
オブジェクトを展開し、 Option
にNone
が含まれている場合None
マクロはデフォルトの修飾子status::BadRequest
Ok(Response::new())
をstatus::BadRequest
ます。
itry
マクロitry
、 IronError
エラーをラップするために使用されIronError
。
実行してパフォーマンスを確認しようとします。
PUT:
[loomaclin@loomaclin ~]$ curl -X PUT -d my_file_content localhost:3000 [loomaclin@loomaclin ~]$ cat ~/IdeaProjects/cycle/foo.txt my_file_content
GET:
[loomaclin@loomaclin ~]$ curl localhost:3000 my_file_content
POST:
[loomaclin@loomaclin ~]$ curl -X POST -v localhost:3000 * Rebuilt URL to: localhost:3000/ * Trying ::1... * Connected to localhost (::1) port 3000 (#0) > POST / HTTP/1.1 > Host: localhost:3000 > User-Agent: curl/7.49.1 > Accept: */* > < HTTP/1.1 400 Bad Request < Content-Length: 0 < Date: Tue, 12 Jul 2016 22:29:58 GMT < * Connection #0 to host localhost left intact
前処理および後処理を使用したエンドツーエンド機能の実装
iron
には、エンドツーエンド機能のためのBeforeMiddleware
、 AfterMiddleware
およびAroundMiddleware
もあり、
メインハンドラで開始する前と終了した後にリクエストを処理するためのロジックを実装できます。
使用例を書きましょう アオポ好き 指定されたタイプ:
サンプルコード extern crate iron; use iron::prelude::*; use iron::{BeforeMiddleware, AfterMiddleware, AroundMiddleware, Handler}; struct SampleStruct; struct SampleStructAroundHandler<H:Handler> { logger: SampleStruct, handler: H} impl BeforeMiddleware for SampleStruct { fn before(&self, req: &mut Request) -> IronResult<()> { println!(" ."); Ok(()) } } impl AfterMiddleware for SampleStruct { fn after(&self, req: &mut Request, res: Response) -> IronResult<Response> { println!(" ."); Ok(res) } } impl<H: Handler> Handler for SampleStructAroundHandler<H> { fn handle(&self, req: &mut Request) -> IronResult<Response> { println!(" ."); let res = self.handler.handle(req); res } } impl AroundMiddleware for SampleStruct { fn around(self, handler: Box<Handler>) -> Box<Handler> { Box::new(SampleStructAroundHandler { logger: self, handler: handler }) as Box<Handler> } } fn sample_of_middlewares(_:&mut Request) -> IronResult<Response> { println!(" ."); Ok(Response::with((iron::status::Ok, ", !"))) } fn main() { let mut chain = Chain::new(sample_of_middlewares); chain.link_before(SampleStruct); chain.link_after(SampleStruct); chain.link_around(SampleStruct); Iron::new(chain).http("localhost:3000").unwrap(); }
この例では、 SampleStruct,
BeforeMiddleware
がbefore
関数で、 AfterMiddleware
がafter
関数で実装されているSampleStruct,
構造を紹介します。 彼らの助けを借りて、すべてのスルーロジックを実装できます。 AroundMiddleware
、 Handler
AroundMiddleware
と共に使用され、追加のハンドラを追加します。 実装されたすべての追加
リクエスト処理のライフサイクルにおけるハンドラーは、特別な特性Chain
を使用して実行されます。これにより、プリプロセッサーとポストプロセッサーの呼び出しのチェーンを形成できます。
プログラムをテストします。
コンソールで:
[loomaclin@loomaclin ~]$ curl localhost:3000 , !
プログラムの出力:
Running `target/debug/cycle` . . . .
ルーティング
ルーティングなしでできるサーバーAPIは何ですか? Add it =)基本的な例を次のように記事の最初から修正します。
標準ライブラリのコレクションを接続します:
use std::collections:HashMap;
「パス-ハンドラー」形式のコレクションを保存する構造を宣言し、この構造に対して、このコレクションを初期化するコンストラクターと、ハンドラーを含む新しいルートをコレクションに追加する関数を記述します。
struct Router { routes: HashMap<String, Box<Handler>> } impl Router { fn new() -> Self { Router { routes: HashMap::new() } } fn add_route<H>(&mut self, path: String, handler: H) where H: Handler { self.routes.insert(path, Box::new(handler)); } }
Iron
とともに構造を使用するには、 handle
関数を使用してHandler
を実装する必要があります。
impl Handler for Router { fn handle(&self, req: &mut Request) -> IronResult<Response> { match self.routes.get(&req.url.path().join("/")) { Some(handler) => handler.handle(req), None => Ok(Response::with(status::NotFound)) } } }
handle
関数では、リクエストで渡されたパスを使用してコレクション内で対応するハンドラーを見つけ、リクエストでこのパスのハンドラーを呼び出します。 リクエストで渡されたパスがコレクションに「登録」されていない場合、 NotFound
エラーNotFound
とともに応答が返されNotFound
。
最後に実装する必要があるのは、ルーターの初期化と、ハンドラーを使用した必要なパスの登録です。
fn main() { let mut router = Router::new(); router.add_route("hello_habrahabr".to_string(), |_: &mut Request| { Ok(Response::with((status::Ok, "Hello Loo Maclin!\n"))) }); router.add_route("hello_habrahabr/again".to_string(), |_: &mut Request| { Ok(Response::with((status::Ok, " !\n"))) }); router.add_route("error".to_string(), |_: &mut Request| { Ok(Response::with(status::BadRequest)) }); ...
新しいパスを追加するには、上記で実装した関数を呼び出します。
ルーターを使用してIron
インスタンスを初期化します。
Iron::new(router).http("localhost:3000").unwrap();
テスト:
[loomaclin@loomaclin ~]$ curl localhost:3000/hello_habrahabr Hello Loo Maclin! [loomaclin@loomaclin ~]$ curl localhost:3000/hello_habrahabr/again ! [loomaclin@loomaclin ~]$ curl -v localhost:3000/error * Trying ::1... * Connected to localhost (::1) port 3000 (#0) > GET /error HTTP/1.1 > Host: localhost:3000 > User-Agent: curl/7.49.1 > Accept: */* > < HTTP/1.1 400 Bad Request < Date: Wed, 13 Jul 2016 21:29:20 GMT < Content-Length: 0 < * Connection #0 to host localhost left intact
おわりに
この記事で終わりになります。
考慮された機能に加えて、Ironは基本的な拡張のためのWebフレームワークに典型的な機能のほとんどを引き出します。
この記事は、Ironの基本的な知識を習得することを目的としており、この目標に対処できることを願っています。
ご清聴ありがとうございました!