捕获异常时的请求状态
Capture Request state at the time of an exception
我有一个带有自定义错误处理程序和一个小型中间件堆栈的 Slim Framework 应用程序。我的中间件将属性添加到 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;
}
};
该属性在错误处理程序中的 Request 对象中不存在,因为从路由内部克隆的 Request 在遍历中间件堆栈后尚未返回。 是否可以在抛出异常时(在该位置)访问存在的请求和响应对象?我无法显式传递它们(例如,SlimException)因为我也在尝试处理意外错误。
我已经创建了两个有点老套的解决方案来在抛出异常时捕获请求和响应状态。两者都尝试将 try/catch 插入尽可能靠近中间件堆栈的中心(不重复代码),并涉及将原始异常与修改后的端点参数一起包装在新异常 class 中。
中间件
只要注意中间件的添加顺序就可以了。不幸的是,它确实需要将最里面的中间件添加到每个路由,或者至少,"before" 中间件修改 Request and/or Response 对象。这不会捕捉到对 Request/Response 内部所做的任何更改,也不会捕获路由之后的任何更改。
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;
}
};
路线Class
这将所有路由包装在一个 try 块中,并使路由代码更清晰一些,但您必须确保从 RouteBase class.
扩展所有路由
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(如果可以)
现在值得注意的是 Slim 4 修复了这个问题。就个人而言,升级有点痛苦,因为发生了很多变化,尤其是依赖注入容器(我从 Slim v3.12
=> 4.8
)。
在 Slim 4 中,他们将路由和错误处理都移到了(单独的)中间件中。
我有一个带有自定义错误处理程序和一个小型中间件堆栈的 Slim Framework 应用程序。我的中间件将属性添加到 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;
}
};
该属性在错误处理程序中的 Request 对象中不存在,因为从路由内部克隆的 Request 在遍历中间件堆栈后尚未返回。 是否可以在抛出异常时(在该位置)访问存在的请求和响应对象?我无法显式传递它们(例如,SlimException)因为我也在尝试处理意外错误。
我已经创建了两个有点老套的解决方案来在抛出异常时捕获请求和响应状态。两者都尝试将 try/catch 插入尽可能靠近中间件堆栈的中心(不重复代码),并涉及将原始异常与修改后的端点参数一起包装在新异常 class 中。
中间件
只要注意中间件的添加顺序就可以了。不幸的是,它确实需要将最里面的中间件添加到每个路由,或者至少,"before" 中间件修改 Request and/or Response 对象。这不会捕捉到对 Request/Response 内部所做的任何更改,也不会捕获路由之后的任何更改。
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;
}
};
路线Class
这将所有路由包装在一个 try 块中,并使路由代码更清晰一些,但您必须确保从 RouteBase class.
扩展所有路由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(如果可以)
现在值得注意的是 Slim 4 修复了这个问题。就个人而言,升级有点痛苦,因为发生了很多变化,尤其是依赖注入容器(我从 Slim v3.12
=> 4.8
)。
在 Slim 4 中,他们将路由和错误处理都移到了(单独的)中间件中。