RESTアーキテクチャWebアプリケーションコントローラーレイヤー

この記事では、Webフレームワークでコントローラー層を開発した経験を共有したいと思います。 このレイヤーに必要なもの:





基本的に、最後の2つのポイントに焦点を当てたいと思います。したがって、特にこれらは明らかなことなので、残りは非常に表面的です。



HTTPリクエストとレスポンスの抽象化



さまざまな言語の大多数のWebフレームワークとは異なり、PHPには(少なくとも平均的な構成では)要求オブジェクトを操作するための通常のインターフェイスがありません。 バラバラで、常に均一に構造化された情報を含まない特別な変数が多数あります(たとえば、 $_POST



$_FILES



のデータ構造の違いに値するだけ$_FILES



)。



したがって、PHPの最高の伝統では、 Net_HTTP_Request



クラスのオブジェクトの形で自転車をすばやく発明しました。 その主な機能:要求ヘッダーのパラメーターとフィールドへの便利なアクセス、ファイルオブジェクトの形式でのアップロードなど。



1 <?php <br>

2 $ id = $ request [ ' id ' ] ;<br>

3 $ auth = $ request -> header [ ' Authorization ' ] <br>

4 foreach ( $ request [ ' upload ' ] as $ line ) { /* ... */ } <br>

5 $ stores = $ request [ ' upload ' ] -> copy_to ( $ permanent_path ) ;<br>

6 ?> <br>









同様に、応答はNet_HTTP_Response



クラスのオブジェクトによって表されます。これは、本文を文字列、反復子、またはファイルオブジェクトなどとして表す、応答ヘッダーを使用した便利な作業を提供します。



1 <?php <br>

2 $ file = IO_FS :: File ( $ path ) ;<br>

3 return Net_HTTP :: Response () - > <br>

4 status ( Net_HTTP :: OK ) - > <br>

5 content_type ( MIME :: type_for_file ( $ file )) - > <br>

6 body ( $ file ) ;<br>

7 ?> <br>









リクエストハンドラー、ミドルウェア



要求処理は、標準インターフェイスを実装するサービスオブジェクトによって実行されます。



1 <?php <br>

2 interface WS_ServiceInterface { <br>

3 public function run ( WS_Environment $ env ) ;<br>

4 } <br>

5 ?> <br>









run()



メソッドの結果は、 Net_HTTP_Response



クラスのオブジェクトです。 WS_Environment



クラスのオブジェクトは、サービスオブジェクト間の情報交換を目的としており、各サービスは環境パラメーターの値を作成、読み取り、または書き込みできます。 デフォルトでは、環境には$env->request



クラスの$env->request



要素が含まれています。



処理中のサービスが別のサービスを呼び出し、要求、応答、または環境を変更すると、いわゆるミドルウェアコンポーネントが取得され、WSGI標準によってその名前が使用されます。 アプリケーション構成、データベースへの接続、キャッシング、認証、そして最も興味深いディスパッチを実装する、簡単なミドルウェアサービスのセットがあります。



一般に、それは明らかな前文でした;最後に、救急車に移ります。



RESTベースのディスパッチ



多くの既存のフレームワークでは、ディスパッチはさまざまなアプリケーションメソッドの呼び出しをURL文字列に適用される正規表現に一致させることに基づいています。 RESTサポートの必要性は、このスキームを介したさまざまなREST指向のアドオンの出現につながり、まずHTTP要求メソッドの分析を提供します。



一連の正規表現に基づくディスパッチには、主にいくつかの欠点があります。

  1. 大規模なアプリケーションの場合、異種ルールの大規模で複雑なシステムを取得するリスクがあり、サポートが困難です。
  2. 規則システムは静的であり、そのプロセスでスケジューリングの結果に直接影響を与えることは困難です。
  3. 任意のレベルのネストを持つ組み込みリソースのスケジューリングは困難です。


ライブラリのスケジューリングスキームを選択するプロセスでは、さまざまな既存のオプションを検討しましたが、 Java標準JAX-RS (JSR-311)で提案されているアプローチが最も気に入っています。



なぜこの標準を選んだのですか?



PHP固有のものを考慮して、標準の簡易バージョンを実装しました。



リクエストマネージャーはサービスオブジェクトとして実装されます(
  WS_ServiceInteface 
) そのrun()



メソッドは、アプリケーションリソースセットの記述に基づいて、ディスパッチャーによって作成されたカスタムリソースオブジェクトに処理を委任します。



リソースは、単に任意のクラスのオブジェクトです。 標準の親クラスから継承する必要はなく、標準のインターフェイスを実装する必要はありません。 同時に、ユーザーアプリケーションでは、コードの重複を避けるために継承階層を導入するのが理にかなっていますが、フレームワークではこれを必要としません。



リソースクラスは、3つのタイプに分類できる一連のメソッドを実装します。



アプリケーションリソース



リソースのセットがアプリケーションを形成します。 アプリケーションにはそのリソースの説明が含まれており、内部DSLを使用して説明を作成します。



リソースメソッドの呼び出しについて決定するには、使用可能なリソースとそのメソッドの完全な説明が必要です。 1つまたは複数のリソースのコードが実際に実行されるため、すべてのクラスをロードする必要はありません。 したがって、アプリケーションを構成するリソースの説明は、実際の実装から分離する必要があります。



サンプルアプリケーション図:



1 <?php <br>

2 $ companies = WS_REST_DSL :: Application () - > <br>

3 begin_resource ( ' company ', ' App.WS.Company ', ' company/{name:[a-zA-Z][a-zA-Z-]+} ' ) - > <br>

4 sublocator ( ' blog ' ) - > // - <br>

5 sublocator ( ' vacancies ' ) - > // - <br>

6 for_format ( ' html ' ) - > <br>

7 index () - > // /company/Techart/ - <br>

8 end - > <br>

9 end - > <br>

10 begin_resource ( ' blog ', ' App.WS.Blog ', null ) - > <br>

11 sublocator ( ' entry ', ' {\d+:id} ' ) - > <br>

12 for_format ( ' html ' ) - > <br>

13 get_for ( ' {page_no:\d+} ', ' index ' ) - > // /company/Techart/blog/5.html - <br>

14 post () - > // - create() <br>

15 index () - > // /company/Techart/blog/ - <br>

16 end - > <br>

17 for_format ( ' rss ' ) - > <br>

18 get ( ' index_rss ' ) - > // /company/Techart/blog/index.rss - RSS- <br>

19 end - > <br>

20 end - > <br>

21 begin_resource ( ' entry ', ' App.WS.Entry ', null ) - > <br>

22 for_format ( ' html ' ) - > <br>

23 index () - > // /company/Techart/blog/82715/ - <br>

24 get_for ( ' print ', ' print_version ' ) - > // /company/Techart/blog/82715/print.html - <br>

25 put () - > // , - update() <br>

26 delete () - > // , - delete() <br>

27 end - > <br>

28 end - > <br>

29 begin_resource ( ' vacancies ', ' App.WS.Job ', null ) - > <br>

30 for_format ( ' html ' ) - > <br>

31 index () - > // /company/Techart/vacancies/ - <br>

32 end - > <br>

33 end - > <br>

34 end ;<br>

35 ?> <br>









この図では、リソースは3つのパラメーターで説明されています。



URLパターンは、名前付きパラメーターを使用した正規表現です(そうでない場合でも!)。



HTTPメソッド



HTTPメソッドは、さまざまなタイプの要求を処理し、応答を生成します。 メソッド記述パラメーター:



リソースクラスコンストラクターとリソースメソッドは、任意の引数セットを持つことができます。 URLパターンに、メソッドの引数と名前が一致するパラメーターが含まれている場合、リソースのコンストラクターまたはメソッドが呼び出されると、パラメーター値が自動的に置き換えられます。 さらに、 $env



$request



、および$format



などのいくつかの定義済み標準パラメーターがあり$format



。 引数名がテンプレートパラメータセットの一部ではなく、事前定義されたパラメータではない場合、 null



代入されます。



メソッドごとに、プレゼンテーション形式のリストを指定できます。 要求された形式は、HTTP要求のヘッダーまたは要求されたドキュメントの拡張子によって決まります。 各形式に個別のメソッドを提供するか、 $format



パラメーターを使用して1つのメソッドで処理を実行できます。 形式は、個々のメソッドとリソース全体の両方に対して指定できます。



サブロケーター



ネストされたリソースに対応するクラスのインスタンスの作成は、処理中に動的に実行できます。 これを行うには、いわゆるサブリソースロケーターを使用します。 リソース記述にメソッドが存在する場合、メソッドはサブロケーターですが、HTTPマスクにメソッドが指定されていません。 サブロケーターは要求を処理せず、代わりに、ネストされたリソースクラスのインスタンスを作成します。 したがって、特定のリソースを作成する決定は、特定の外部条件に応じて、リクエストの処理時に行うことができます。



リソースクラス



上記の例では、いくつかのリソースクラスのスケルトンは次のようになります。



1 <?php <br>

2 // - <br>

3 class App_WS_Resource { <br>

4 protected $ env ;<br>

5 protected $ db ;<br>

6 <br>

7 public function __construct ( WS_Environment $ env ) { <br>

8 $ this -> env = $ env ;<br>

9 $ this -> db = $ env -> db;<br>

10 } <br>

11 } <br>

12 <br>

13 // blog <br>

14 class App_WS_Blog extends App_WS_Resource { <br>

15 <br>

16 public function index ( $ page_no = 1 ) { /* $page_no URL */ } <br>

17 <br>

18 public function index_rss () { /* RSS- */ } <br>

19 <br>

20 public function entry ( $ id ) { <br>

21 // - <br>

22 // <br>

23 if ( $ entry = $ this -> db -> blog -> entries [ $ id ]) <br>

24 return new App_WS_Entry ( $ this -> env, $ entry ) ; <br>

25 } <br>

26 <br>

27 public function create () { /* */ } <br>

28 } <br>

29 <br>

30 // entry <br>

31 class App_WS_Entry extends App_WS_Resource { <br>

32 protected $ entry ;<br>

33 <br>

34 public function __construct ( WS_Environment $ env , App_DB_Entry $ entry ) { <br>

35 parent :: __construct ( $ env ) ;<br>

36 $ this -> entry = $ entry ;<br>

37 } <br>

38 <br>

39 public function index () { /* */ } <br>

40 public function print_version () { /* */ } <br>

41 public function update () { /* */ } <br>

42 public function delete () { /* */ } <br>

43 } <br>









スケジューリングアルゴリズム



JAX-RS標準で説明されいるアルゴリズムの簡略版であるスケジューリングアルゴリズムは、次のようになります。

  1. 要求ヘッダーまたはドキュメント拡張機能により、必要なプレゼンテーション形式を決定します。
  2. リソースの説明を見て、それぞれのパスをURLの先頭と比較します。
  3. URLがパターンと一致するリソースが見つからず、形式がサポートされているもののリストに含まれている場合-404;
  4. リソースが見つかりました。メソッドを探しています。 リソースのパスに一致する部分をURLから削除します。
  5. メソッドURLパターンをURLの残りの部分の先頭と照合することにより、リソースメソッドのすべての説明を調べます。
  6. URLパターンと表示形式に一致するメソッドまたはサブロケーターがない場合-404;
  7. 適切なURLパターン、形式、およびHTTPマスクを持つメソッドが見つかった場合、リソースクラスのオブジェクトを作成し、メソッドを呼び出してパラメーターの置換を実行します。 実行結果-応答を表すオブジェクト、作業は完了します。
  8. 適切なURLパターンを持つサブロケーターが見つかった場合、リソースクラスのオブジェクトを作成し、メソッドを呼び出してパラメーターの置換を実行します。
  9. サブロケーターの結果は、新しいリソースです。 リソースのリストで対応するクラスのリソースの説明を探しています。
  10. リソースクラスが説明にない場合-404;
  11. httpメソッドが見つかるまで、URLの先頭から一致する行を削除し、手順4に戻ります。


アルゴリズムを単純化するために、アドレス/ resource /および/resource/index.html(html形式の場合)は同等であると考えています。さらに、アルゴリズムの反復の最大数を強制します。



パフォーマンスを向上させるには、まずリソースの説明をキャッシュし、次に、メインアプリケーションドメインのURLサブディレクトリまたはサブドメインにリンクすることにより、大きなアプリケーションを個別の小さなアプリケーションに分割します。



結果



提案されたスキームを実装することで何が得られますか?



一般に、提案されたスキームに非常に満足しています。私たちの意見では、実際に開発者に制限を課すことはなく、実装が非常に簡単です。 興味深い予想外の結果は、プリミティブなORMレイヤーとの非常に単純な統合です。 2つの非常に単純なリソースクラスのみを実装したため、ORMレイヤーへの命令でHTTPリクエストの透過的なマッピングを受け取りました。たとえば、



GET /api/news/stories/most_popular

$db->news->stories->most_popular->select()



POST /api/news/stories/

$db->news->stories->insert($story)



PUT /api/news/stories/15

$db->news->stories->update($db->news->stories[15]);







などなど。



さて、私たちの内部RESTセミナーの資料は突然、誰もが興味を持つようになります。



All Articles