Задача:
в проекте есть много мест, где для неавторизованных пользователей вызывается throw HttpException(403). Нужно только гостей редиректить на страницу аутентификации. Решено было таким образом:
в конфиге подключаем errorHandler
'errorHandler' => [
'errorAction' => 'site/error',
],
В SiteController такой код:
public function actionError()
{
if(($exception = Yii::$app->getErrorHandler()->exception) === null)
$exception = new HttpException(404, Yii::t('yii', 'Page not found.'));
if($exception instanceof HttpException)
$code = $exception->statusCode;
else
$code = $exception->getCode();
if($code == 403 && Yii::$app->user->isGuest)
return $this->redirect(['/user/account/login']);
...
В одной из предыдущих версий это работало нормально, но сейчас страница стала получать код статуса 403, вместо 302 и редиректа не происходит:
Если это не ошибка, то подскажите как правильно разрулить ситуацию.
Спасибо и сорри за русский.
На первый взгляд все правильно. Вы порождаете HttpException со статус-кодом 403, а потом еще довешиваете редирект.
подскажите как правильно разрулить ситуацию
Я бы исправил ваш код таким образом:
if($code == 403 && Yii::$app->user->isGuest) {
return $this->redirect(['/user/account/login'], 302);
}
В одной из предыдущих версий это работало нормально
Точного ответа дать не могу, можете попробовать найти коммит, изменивший поведение с помощью git bisect и я постараюсь это прокомментировать.
Пока задачу закрываю, но если что - высказывайте свои мысли.
Хм, что-то я фигню посоветовал :)
Если посмотреть на ErrorHandler.php:120, можно увидеть, что статус-код будет все равно установлен из исключения.
Попробуйте
if($code == 403 && Yii::$app->user->isGuest) {
$exception->statusCode = 302;
return $this->redirect(['/user/account/login']);
}
На первый взгляд все правильно. Вы порождаете HttpException со статус-кодом 403, а потом еще довешиваете редирект.
И в чем криминал-то? Кто сказал, что исключение должно обязательно всплыть на самый верх? Ведь ErrorHandler и создан, чтобы его перехватывать и обрабатывать как-то более цивилизованно, чем просто вывести "404 Page not found". Или я не прав?
Решение с $exception->statusCode = 302; не проходит, т.к. переменная $exception внутренняя для метода внутри ErrorHandler::renderException(), из SiteController::actionError() ее изменить нельзя. Потому тикет и создал.
Заменить во всем коде все вызовы HttpException на что-то типа
if(Yii::$app->user->isGuest) return $this->redirect(...); else throw HttpException(403) конечно можно, но как-то это коряво.
Я пока решил так: создал потомка ErrorHandler, подменив в нем метод renderException(), изменив в нем завершающий код:
было:
if ($exception instanceof HttpException)
$response->setStatusCode($exception->statusCode);
else
$response->setStatusCode(500);
$response->send();
стало:
if($response->statusCode != 302)
{
if($exception instanceof HttpException)
$response->setStatusCode($exception->statusCode);
else
$response->setStatusCode(500);
}
$response->send();
Для моего случая работает. Имхо такое поведение (редиректы пропускаем, иначе возвращаем статус, порожденный исключением) более логично. Может стоит внести в фреймворк?
Yii::$app->getResponse()->redirect($url)->send();
return;
Обработка ошибок — особый случай.
This note saved my life. Thank you. I can now use the below in my config
'on beforeAction' => function ($event){
if(Yii::$app->user->isGuest && Yii::$app->getRequest()->url !== Url::to(Yii::$app->getUser()->loginUrl))
{
Yii::$app->getResponse()->redirect(Url::to(\Yii::$app->getUser()->loginUrl))->send();
return;
}
},
Most helpful comment