我有一个带有自定义errorHandler的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。 是否可以在引发异常的时候(在该位置)访问Request和Response对象,因为它们存在,我无法显式传递它们(例如SlimException),因为我也试图处理意外错误。
答案 0 :(得分:0)
我创建了两个稍微有点怪异的解决方案,以在引发异常时捕获请求和响应状态。两者都尝试将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;
}
};
答案 1 :(得分:0)
现在值得注意的是,Slim 4 解决了这个问题。就我个人而言,由于发生了很多变化,升级有点痛苦,尤其是对于依赖注入容器(我来自 Slim v3.12
=> 4.8
)。
在 Slim 4 中,他们将路由和错误处理转移到(单独的)中间件中。