鉄の紹介:Rustの精錬鉱石



Ironは、Rustプログラミング言語で記述された高レベルのWebフレームワークであり、別の悪名高いハイパーライブラリに基づいて構築されています。 IronはRustを最大限に活用するように設計されています。




哲学



鉄は、可能な限り拡張性の原則に基づいて構築されています。

彼は自分の機能を拡張するための概念を紹介します:









この記事では、修飾子と中間型の基本部分について学習します。







公式サイトIron







プロジェクト作成



まず、次のコマンドを使用して、Cargoを使用してプロジェクトを作成します。







cargo new rust-iron-tutorial --bin
      
      





次に、 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の基本的な知識を習得することを目的としており、この目標に対処できることを願っています。







ご清聴ありがとうございました!








All Articles