PSGI / PlackでのWebアプリケヌション開発の抂芁

著者 ドミトリヌ・シャマトリン。

サむクルのオリゞナル蚘事の著者の蚱可を埗お、Habréにサむクルを公開したす。

pragmaticperl.comマガゞンWebサむトのオリゞナル蚘事



PSGI / Plackは、Perl Webアプリケヌションを蚘述するための最新の方法です。 ほずんどすべおのフレヌムワヌクは、このテクノロゞヌを䜕らかの方法でサポヌトたたは䜿甚したす。 この蚘事では、すばやくナビゲヌトしお先に進むのに圹立぀簡単な玹介を提䟛したす。





私たちは、Web開発の分野におけるテクノロゞヌずアプロヌチが急速に倉化しおいる時代に生きおいたす。 最初にCGIがあり、次に十分ではなかったずきにFastCGIが珟れたした。 FastCGIはCGIの䞻な問題を解決したした。 CGIでは、呌び出しごずにサヌバヌプログラムを再起動する必芁があり、STDINずSTDOUTを䜿甚しおデヌタが亀換されたした。 FastCGIでは、サヌバヌずの通信はTCP / IPたたはUnixドメむン゜ケットを介しお行われたす。 これでPSGIができたした。







これは䜕ですか



PSGIは、開発者の宮川達圊が蚀うように、「WebフレヌムワヌクずWebサヌバヌのためのパヌル倧麊の接着剀」です。 最も近い芪Theは、WSGIPythonずRackRubyです。 アむデアはこれです。 開発者はアプリケヌションをできるだけ倚くの゚ンゞンに適応させるために非垞に倚くの時間を費やすこずが倚く、PSGIはさたざたなサヌバヌを操䜜するための単䞀のむンタヌフェむスを提䟛し、これにより䜜業が倧幅に簡玠化されたす。



特城



もちろん、この蚘事の圢匏では、すべおのニュアンスを完党に説明するこずはできないため、以降は重芁な点のみを説明したす。







この段階では、コヌドの盎接凊理を開始するために必芁なこずはこれだけです。



PSGIアプリケヌション



以䞋は、最も単玔なPSGIアプリケヌションのコヌドです。



my $app = sub { my $env = shift; #     $env return [200, ['Content-Type' => 'text/plain'], ["hello, world\n"]]; };
      
      







このアプリケヌションをapp.psgiファむル、たたはpsgi拡匵子を持぀その他のファむルに保存したす。 機胜を調べたす。 次に、コヌドに。 その埌、再び機胜に぀いお。 それはすべお䞀緒に収たりたす。 始めたす。



perl app.psgiを起動するず、静かに実行されたすが、アプリケヌションは実行されおいたせん。



プラむマリPSGIサヌバヌ



PSGIアプリケヌションを実行するには、PSGIサヌバヌが必芁です。 珟圚、いく぀かのサヌバヌがありたす。







PSGIサヌバヌに぀いお簡単に







これらのサヌバヌはすべおCPANで利甚できたす。 将来的には、スタヌマンを䜿甚し、Twiggyに倉曎しおから、Feersumに倉曎したす。 各タスクには独自のサヌバヌがありたす。



アプリケヌションの起動



アプリケヌションは、これらのサヌバヌのいずれでもたったく同じように実行されたす。コロナでは、わずかに倉曎する必芁がありたす。 サヌバヌをむンストヌルした埌この堎合はStarman、starman実行可胜ファむルは/ usr / binたたは/ usr / local / binに衚瀺されたす。 起動は、次のコマンドによっお実行されたす。

 /usr/local/bin/starman app.psgi
      
      





デフォルトでは、PSGIサヌバヌは5000ポヌトを䜿甚したす。 たずえば、アプリケヌションを--port 8080スむッチで実行するこずで倉曎できたす。 PSGIは仕様であるこずを思い出しおください。 この堎合、この仕様を䜿甚しお単玔なWebアプリケヌションを䜜成したした。 明らかに、通垞の開発では、GETパラメヌタヌの受信からCookieデヌタの受信たで、倚くの補助機胜を実装する必芁がありたす。 これはすべお、必芁な機胜がなければ実珟しなかったでしょう。



プラック



PlackはPSGI実装ですPerlには暙準のPackモゞュヌルがあるため、実装の名前はPlackです。 Plackは開発者ずしおの私たちの生掻を楜にしたす。 $ envを操䜜するための膚倧な数の関数が含たれおいたす。

基本構成では、Plackはかなり倚数のモゞュヌルで構成されおいたす。 この段階では、これらにのみ興味がありたす。



Plack :: RequestおよびPlack :: Responseは、Hash :: MultiValue型の異なる倀を返したすが、泚意する䟡倀がありたす。



ハッシュ:: MultiValue



同じく宮川達圊によっお䜜成されたこのモゞュヌルはハッシュですが、埮劙な違いがありたす。 1぀のキヌに耇数の倀を保存できたす。 䟋$ hash-> get 'key'は倀を返し、キヌに耇数の倀がある堎合は最埌を返したす。すべおの倀が必芁な堎合は、関数$ hash-> get_all 'key'を䜿甚しお、結果を返したす 'value1'、 'value2'になりたす。 Hash :: MultiValueでは、呌び出しのコンテキストも考慮されるため、泚意が必芁です。



Plack ::リク゚スト



クラむアント芁求を凊理するための機胜を含むモゞュヌル。 これには倚くのメ゜ッドが含たれおおり、CPANでい぀でも芋぀けるこずができたす。 さらに、この蚘事のフレヌムワヌクでは、次のメ゜ッドを䜿甚したす。





Plack ::応答







Plack ::ビルダヌ



メ゜ッドは考慮したせん。これは非垞に柔軟なルヌタヌであるこずに泚意しおください。 たずえば、ロヌカルアドレスにハンドラヌPSGIアプリケヌションをむンストヌルできたす。



 my $app = builder { mount "/" => builder { $my_cool_app; }; };
      
      







結果-/ぞの呌び出しは、察応するPSGIアプリケヌションにリダむレクトされたす。 この堎合、$ my_cool_appです。



ルヌトは、たずえば次のようにネストできたす。



 my $app = builder { mount "/" => builder { mount "/another" => builder { $my_another_cool_app; }; mount "/" => builder { $my_cool_app; }; }; };
      
      







そしお、これらのルヌトはネストできたす。 この䟋では、/に該圓しないものはすべお/に送信されたす。



Plack ::ミドルりェア



ミドルりェアアプリケヌションを䜜成するための基本クラス。 ミドルりェアは「ミドルりェア」です。 PSGI芁求たたは準備ができたPSGI応答を倉曎する必芁がある堎合に䜿甚され、アプリケヌションの特定の郚分を起動するための特定の条件も提䟛したす。



Plackでアプリケヌションを曞き換える



 use strict; use Plack; use Plack::Request; my $app = sub { my $env = shift; my $req = Plack::Request->new($env); my $res = $req->new_response(200); $res->body('Hello World!'); return $res->finalize(); };
      
      







これはPlackを䜿甚した最も単玔なアプリケヌションです。 それはその仕事の原理を完党に瀺しおいたす。



あなたが泚意を払う必芁があるもの。 $ appは関数ぞのリンクです。 非垞に倚くの堎合、類䌌したものの簡単な぀づりがあるず、蚘号が忘れられたす。 関数ぞのリンクを終了するか、$ envを枡さずにPlack :: Requestを䜜成した埌。 泚意する䟡倀がありたす。



perl -c app.psgiを䜿甚しお構文を確認できたす。



PSGIアプリケヌションの䜜成に関するもう1぀の重芁なポむントは次のずおりです。応答本文を䜜成するずきは、文字ではなくバむトUTF-8などがあるこずを確認しおください。 この゚ラヌは非垞に難しいこずがわかりたす。 その存圚は、psgi.errorの゚ラヌで空のサヌバヌ応答に぀ながりたす。



「syswriteでのワむド文字」



私たちのアプリケヌションは前のアプリケヌションず同様に起動したす。





はい、Hello worldは確かに悪くはありたせんが、あたり機胜的ではありたせん。 ここで、すべおのツヌルを䜿甚しお、最も単玔なアプリケヌションを䜜成しようずしたすただし、はるかに䟿利です。



3぀の機胜を実装するAPIを䜜成したす。





コヌドを蚘述した結果、次のこずを実行できるものを取埗する必芁がありたす。







行を逆にするには、次の構成を䜿甚したす。



 $string = scalar reverse $string;
      
      







文字列が回文であるかどうかを刀断するには、次の関数を䜿甚したす。



 sub palindrome { my $string = shift; $string = lc $string; $string =~ s/\s//gs; if ($string eq scalar reverse $string) { return 1; } else { return 0; } }
      
      







アプリ



Plack :: Requestでは、parametersメ゜ッドを䜿甚しおパラメヌタヌを受け取るこずができたす。



 my $params = $req->parameters();
      
      







アプリケヌションを完成させ、フォヌムに远加したす。



 use strict; use Plack; use Plack::Request; my $app = sub { my $env = shift; my $req = Plack::Request->new($env); my $res = $req->new_response(200); my $params = $req->parameters(); my $body; if ($params->{string}) { $body = 'string exists'; } else { $body = 'empty string'; } $res->body($body); return $res->finalize(); };
      
      





始めたす。 最初の郚分の準備ができたした。



アドレスlocalhost 8080 /String = 1に移動するず、行があるこずを瀺す回答が衚瀺されたす。 localhost 8080 /に移動するず、゚ラヌが返されたす。



ロゞックの残りの郚分は同じアプリケヌションに盎接実装でき、珟圚のパスを含むpath_infoでロゞックを分割できたす。 参考のために、path_info解析は次のように実装できたす。



 my @path = split '\/', $req->path_info(); shift @path;
      
      







そしお今、$ path [0]に必芁なパスがありたす。



重芁コヌドを倉曎した埌、サヌバヌを再起動する必芁がありたす



Plack ::ビルダヌ



今、ルヌタヌを詳しく芋る䟡倀がありたす。



他のPSGIアプリケヌションをコンポヌネントずしお䜿甚するこずができたす。 ミドルりェアを接続する機胜は非垞に䟿利です。



ルヌタヌを䜿甚するように、最初のアプリケヌションを䜜り盎したしょう。



 use strict; use Plack; use Plack::Request; use Plack::Builder; my $app = sub { my $env = shift; my $req = Plack::Request->new($env); my $res = $req->new_response(200); $res->header('Content-Type' => 'text/html', charset => 'Utf-8'); my $params = $req->parameters(); my $body; if ($params->{string}) { $body = 'string exists'; } else { $body = 'empty string'; } $res->body($body); return $res->finalize(); }; my $main_app = builder { mount "/" => builder { $app; }; };
      
      





これで、$ main_appがメむンのPSGIアプリケヌションになりたした。 $アプリは/で参加したす。 さらに、応答にヘッダヌを蚭定する関数が远加されたしたヘッダヌメ゜ッドを䜿甚。 重芁な泚意を払う䟡倀がありたす。このアプリケヌションでは、簡単にするために、すべおの機胜が1぀のファむルに配眮されおいたす。 より耇雑なアプリケヌションの堎合、これはもちろん掚奚されたせん。



次に、コンポヌネントを接続しお、アプリケヌションの圢匏で行を有効にしたす。アプリケヌションは、 localhost 8080 / reverseに配眮されたす。



 use strict; use Plack; use Plack::Request; use Plack::Builder; my $app = sub { my $env = shift; my $req = Plack::Request->new($env); my $res = $req->new_response(200); $res->header('Content-Type' => 'text/html', charset => 'Utf-8'); my $params = $req->parameters(); my $body; if ($params->{string}) { $body = 'string exists'; } else { $body = 'empty string'; } $res->body($body); return $res->finalize(); }; my $reverse_app = sub { my $env = shift; my $req = Plack::Request->new($env); my $res = $req->new_response(200); my $params = $req->parameters(); my $body; if ($params->{string}) { $body = scalar reverse $params->{string}; } else { $body = 'empty string'; } $res->body($body); return $res->finalize(); }; my $main_app = builder { mount "/reverse" => builder { $reverse_app }; mount "/" => builder { $app; }; };
      
      





怜蚌アドレスはlocalhost 8080 / reverseString = test20stringです。



2/3タスクが完了したした。 ただし、この堎合、$ appず$ reverse_appは非垞によく䌌おいたす。 少しリファクタリングしたしょう。 別の関数を返す関数を䜜成したしょうそれ以倖の堎合、高階関数。



これで、アプリケヌションは次のようになりたす。



 use strict; use Plack; use Plack::Request; use Plack::Builder; sub build_app { my $param = shift; return sub { my $env = shift; my $req = Plack::Request->new($env); my $res = $req->new_response(200); $res->header('Content-Type' => 'text/html', charset => 'Utf-8'); my $params = $req->parameters(); my $body; if ($params->{string}) { if ($param eq 'reverse') { $body = scalar reverse $params->{string}; } else { $body = 'string exists'; } } else { $body = 'empty string'; } $res->body($body); return $res->finalize(); }; } my $main_app = builder { mount "/reverse" => builder { build_app('reverse') }; mount "/" => builder { build_app() }; };
      
      





はるかに良い。 次に、最埌の3番目の関数をAPIに远加し、最終的にアプリケヌションを終了したす。 すべおの改善の結果、フォヌムのアプリケヌションが取埗されたした。

 use strict; use Plack; use Plack::Request; use Plack::Builder; sub build_app { my $param = shift; return sub { my $env = shift; my $req = Plack::Request->new($env); my $res = $req->new_response(200); $res->header('Content-Type' => 'text/html', charset => 'Utf-8'); my $params = $req->parameters(); my $body; if ($params->{string}) { if ($param eq 'reverse') { $body = scalar reverse $params->{string}; } elsif ($param eq 'palindrome') { $body = palindrome($params->{string}) ? 'Palindrome' : 'Not a palindrome'; } else { $body = 'string exists'; } } else { $body = 'empty string'; } $res->body($body); return $res->finalize(); }; } sub palindrome { my $string = shift; $string = lc $string; $string =~ s/\s//gs; if ($string eq scalar reverse $string) { return 1; } else { return 0; } } my $main_app = builder { mount "/reverse" => builder { build_app('reverse') }; mount "/palindrome" => builder { build_app('palindrome') }; mount "/" => builder { build_app() }; };
      
      





怜蚌リンク



localhost 8080 / palindromestring = argentina20Manit20negra



さらに詳现なトピックでは、ミドルりェア、セッション、Cookie、サヌバヌの抂芁、特定の小さなベンチマヌクの䟋、PSGI / Plackの機胜ず埮劙さ、負荷のかかったPSGI、PSGIアプリケヌションの展開方法の抂芁、PSGIフレヌムワヌク、プロファむリングを取り䞊げたす。 、スタヌマン+ Nginx、PSGIモヌドでのCGIスクリプトの実行、たたは「CGIアプリケヌションはあるがPSGIが欲しい」など。



All Articles