php-例外時のキャプチャリクエストの状態
カスタムerrorHandlerと小さなミドルウェアスタックを備えたSlimFrameworkアプリケーションがあります。私のミドルウェアは、例外が発生した場合にエラーハンドラーからアクセスしたいRequestオブジェクトに属性を追加します。例:
$app->get('/endpoint', function($request $response, $args) {
$myAttribute = $request->getAttribute('myAttribute'); //returns 'myValue'
throw new \Exception(); //any code that throws an error
})->add(function($request, $response, $next) {
$request = $request->withAttribute('myAttribute', 'myValue');
$response = $next($request, $response);
return $response;
});
$app->getContainer['errorHandler'] = function($c) {
return function($request, $response, $exception) {
$myAttribute = $request->getAttribute('myAttribute'); //returns null
return $response;
}
};
ミドルウェアスタックを通過した後、ルート内からクローン化されたリクエストが返されないため、エラーハンドラ内のリクエストオブジェクト内に属性が存在しません。 リクエストオブジェクトとレスポンスオブジェクトが存在する場合、(その場所で)例外がスローされたときにアクセスできますか?明示的に渡すことができません(たとえば、SlimException)。 m予期しないエラーも処理しようとしています。
答え :
解決策:
例外がスローされたときに要求と応答の状態をキャプチャするための、ややハッキーな2つの解決策を作成しました。どちらも(コードを繰り返さずに)ミドルウェアスタックの中央にできるだけ近い場所にtry / catchを挿入しようとし、変更されたエンドポイントパラメータとともに元の例外を新しい例外クラスにラップします。
ミドルウェア
これは、ミドルウェアが追加される順序に注意が払われている限り機能します。残念ながら、最も内側のミドルウェアを各ルートに追加する必要があります。少なくとも、ミドルウェアが要求オブジェクトや応答オブジェクトを変更する「前」に追加する必要があります。これは、ルート内またはルート後に行われた要求/応答への変更をキャッチしません。
class WrappedException extends \Exception {
public $exception;
public $request;
public $response;
public function __construct($exception, $request, $response) {
$this->exception = $exception;
$this->request = $request;
$this->response = $response;
}
}
$app->get('/endpoint', function($request $response, $args) {
throw new \Exception(); //any code that throws an error
})->add(function($request, $response, $next) {
try {
$response = $next($request, $response);
} catch (\Exception $exception) {
throw new WrappedException($exception, $request, $response);
}
return $response;
});
$app->add(function($request, $response, $next) {
$request = $request->withAttribute('myAttribute', 'myValue');
$response = $next($request, $response);
return $response;
});
$app->getContainer['errorHandler'] = function($c) {
return function($request, $response, $exception) {
if ($exception instanceof WrappedException) {
//returns 'myValue'
$myAttribute = $exception->request->getAttribute('myAttribute');
}
return $response;
}
};
ルートクラス
これにより、すべてのルートがtryブロックにラップされ、ルートコードが少しわかりやすくなりますが、RouteBaseクラスからすべてのルートを拡張する必要があります。
class WrappedException extends \Exception {
public $exception;
public $request;
public $response;
public function __construct($exception, $request = null, $response = null) {
$this->exception = $exception;
$this->request = $request;
$this->response = $response;
}
}
class RouteBase {
public function __call($method, $arguments) {
if (method_exists($this, $method)) {
try {
$this::$method(...$arguments);
} catch (\Exception $e) {
throw new WrappedException($e, ...$arguments);
}
} else {
throw new \Exception('Route method not found.');
}
}
}
class RouteClass extends RouteBase {
//PROTECTED -- must be callable by parent class, but not outside class
protected function get(Request $request, Response $response, $args) {
throw new \Exception('A new exception!');
$response->write('hey');
return $response;
}
}
$app->get('/endpoint', 'RouteClass:get');
$app->add(function($request, $response, $next) {
$request = $request->withAttribute('myAttribute', 'myValue');
$response = $next($request, $response);
return $response;
});
$app->getContainer['errorHandler'] = function($c) {
return function($request, $response, $exception) {
if ($exception instanceof WrappedException) {
//returns 'myValue'
$myAttribute = $exception->request->getAttribute('myAttribute');
}
return $response;
}
};
答え :
解決策:
Slim 4にアップグレードします(可能な場合)
Slim4でこの問題が修正されたことは注目に値します。個人的には、特に依存性注入コンテナで多くの変更が行われたため、アップグレードするのはちょっと面倒でした(私はSlim v3.12
=> 4.8
)。
Slim 4では、ルーティングとエラー処理の両方を(個別の)ミドルウェアに移動しました。
同様の質問
私たちのウェブサイトで同様の質問で答えを見つけてください。