質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Slim

SlimはPHPアプリケーションを開発するための軽量なマイクロフレームワークです。

Q&A

解決済

1回答

3859閲覧

SlimFramework 3のミドルウェアでリクエスト, レスポンスを書き換えたい

k725

総合スコア12

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Slim

SlimはPHPアプリケーションを開発するための軽量なマイクロフレームワークです。

0グッド

1クリップ

投稿2016/06/03 09:55

編集2016/06/03 10:17

###前提・実現したいこと
Slimにてウェブアプリを書いています。
Slimのミドルウェアにてレスポンスを書き換えたいです。

  1. リクエスト(CSV)
  2. ミドルウェア(CSV to JSON)
  3. ルータ/モデル(JSONを操作)
  4. ミドルウェア(JSON to CSV)
  5. レスポンス(CSV)

なお下記クラスはグループルータに add(new HogeMiddleware); してあります

###ソースコード

namespace app\mw; namespace app\helper; use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Message\ResponseInterface as Response; class HogeMiddleware { function __invoke(Request $request, Response $response, $next) { if (helper::isCSV($request->getBody()->getContents())) { $csv = helper::toJSON($request->getBody()->getContents()); // リクエストを書き換えたい } $response = $next($request, $response); if (helper::isJSON($response->getBody()->getContents())) { $json = helper::toCSV($response->getBody()->getContents()); // レスポンスを書き換えたい } return $response; } }

###試したこと
他の人が作成したSlimのミドルウェアを見てみましたが、リクエストやレスポンスを書き換えているコードが無く困っています。

###補足情報(言語/FW/ツール等のバージョンなど)

  • Slim 3.4.1

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

こんな感じでしょうか。
テストしてはいませんが、恐らく動くと思います。

php

1namespace app\mw; 2namespace app\helper; 3 4use Psr\Http\Message\ServerRequestInterface as Request; 5use Psr\Http\Message\ResponseInterface as Response; 6use Slim\Http\Body; // 追加 7 8class HogeMiddleware { 9 // コードの動作とは無関係だが、アクセス修飾子 public を明記 10 // 見間違えなどがあるかもしれないので、どちらかといえば書いたほうが良い 11 // ただし、(publicの場合は)省略しても動作に影響はない 12 public function __invoke(Request $request, Response $response, $next) { 13 if (helper::isCSV($request->getBody()->getContents())) { 14 $csv = helper::toJSON($request->getBody()->getContents()); 15 16 /* 17 * リクエストを書き換え 18 * 19 * @see https://github.com/slimphp/Slim/blob/3.x/Slim/Handlers/NotFound.php#L54-L59 20 * @see https://github.com/slimphp/Slim/blob/3.x/Slim/Http/RequestBody.php#L21 21 */ 22 $body = new Body(fopen('php://temp', 'w+')); 23 $body->write($csv); 24 $request = $request->withBody($body); 25 } 26 27 $response = $next($request, $response); 28 29 if (helper::isJSON($response->getBody()->getContents())) { 30 $json = helper::toCSV($response->getBody()->getContents()); 31 32 /* 33 * レスポンスを書き換え 34 * 35 * @see https://github.com/slimphp/Slim/blob/3.x/Slim/Handlers/NotFound.php#L54-L59 36 * @see https://github.com/slimphp/Slim/blob/3.x/Slim/Http/Response.php#L127 37 */ 38 $body = new Body(fopen('php://temp', 'r+')); 39 $body->write($json); 40 $response = $response->withBody($body); 41 } 42 43 return $response; 44 } 45}

リクエストやレスポンスのbodyを取得するためにはgetBodyメソッドを使いますが、書き換える場合はwithBodyメソッドを使います。

このメソッドの引数には、文字列ではなくStreamInterfaceのインスタンスを指定しなくてはなりません。
SlimではこれをSlim\Http\Bodyクラスで提供しているため、このクラスを引数に指定すればbodyを書き換えられます。
が、このSlim\Http\Bodyクラスも引数に文字列ではなくPHPのストリームを指定し、肝心のbodyはwriteメソッドで書き込む必要があるため、少々ややこしい事になっています。


SlimのSlim/Handlers/NotFound.phpでは、レスポンスの書き換えをおこなうコードを以下のように実装しています。
変数$outputに、レスポンスのbodyを上書きする文字列が格納されています。

php

1$body = new Body(fopen('php://temp', 'r+')); 2$body->write($output); 3return $response->withStatus(404) 4 ->withHeader('Content-Type', $contentType) 5 ->withBody($body);

このコードを簡単に説明すると、fopen('php://temp', 'r+')でストリームを生成してSlim\Http\Bodyクラスの引数に渡した後、
writeメソッドで変数$outputの内容をbodyとして書き込んでいます。
レスポンスのbodyの書き換えはこれをパクりました。

php

1$body = new Body(fopen('php://temp', 'r+')); 2$body->write($json); 3$response = $response->withBody($body);

リクエストの場合も、withBodyメソッドを利用する点は変わりません。
多分、レスポンスの場合と同じコードで動作するハズです。

…が、この回答では一応のため、Slimがリクエストのbodyを生成している内部処理と同じコードにしました。
レスポンスのコードと異なるのは、fopen関数の第二引数の値です。'r+'から'w+'に変更しています。

php

1$body = new Body(fopen('php://temp', 'w+')); 2$body->write($csv); 3$request = $request->withBody($body);

(ストリームとして使うなら、r+w+も変わらないと思いますが、ストリーム関係の動作にはあまり詳しくないので、確証を持って「r+w+も同じだ」と言い切ることが出来ません…)


なお、肝心なことですが、Slimが採用しているPSR-7ServerRequestInterfaceResponseInterfaceは不変オブジェクトです。
StreamInterfacegetBodyメソッドで取得できるオブジェクト)を除き、メソッドを実行しただけではオブジェクトそのものの状態(オブジェクトの値)が変化せず、そのメソッドの返り値である「状態が変化したオブジェクト」を利用しなくてはなりません。

このため、withBodyなどのメソッドが返す値のオブジェクトを次に渡す必要があります。
このコードでは以下のように変数に再代入して次に渡しています。
誤って再代入を忘れると、bodyが上書きされません

php

1// 正しい記述 2$response = $response->withBody($body); 3 4// 間違えてこうするとbodyが変わらない 5$response->withBody($body);

詳細は以下をどうぞ。

投稿2016/06/03 13:15

編集2016/06/03 13:26
sounisi5011

総合スコア697

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

k725

2016/06/03 14:02

ありがとうございます!無事解決しました
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問