鉄の紹介: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などの最も重要なタイプのセットを含むプレリュードモジュールが接続されました
ResponseIronRequestIronResultIronErrorおよび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ライブラリ内の対応する列挙型のラッパーです。


リクエストの転送


リダイレクトでは、 ironmodifiersモジュールの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オブジェクトを展開し、 OptionNoneが含まれている場合Noneマクロはデフォルトの修飾子status::BadRequest Ok(Response::new())status::BadRequestます。
itryマクロitryIronErrorエラーをラップするために使用され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には、エンドツーエンド機能のためのBeforeMiddlewareAfterMiddlewareおよび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, BeforeMiddlewarebefore関数で、 AfterMiddlewareafter関数で実装されているSampleStruct,構造を紹介します。 彼らの助けを借りて、すべてのスルーロジックを実装できます。 AroundMiddlewareHandler 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の基本的な知識を習得することを目的としており、この目標に対処できることを願っています。


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



Source: https://habr.com/ru/post/J305714/


All Articles