Реализуем API для клиента, авторизация по токену, то есть параметром в GET (или в заголовках) приходит параметр token, который используется для идентификации пользователя и предоставления данных к ресурсам платформы.
Есть общий контроллер, от которого наследуются все остальные (AuthController, ProductController, OrdersController и прочие). Хотелось бы в этом контроллере описать логику для проверки наличия токена (в идеале свой метод описать, может быть где-то указать этот метод коллбэком, например) и при наличии оного - пускать запрос на дальнейшее выполнение, в ином случае - возвращать JSON объект с необходимыми мне данными. Данные действия должны проводиться при каждом запросе, то есть как в случае с Middleware, например, в Laravel.
Метод beforeAction в контроллере решает задачу наполовину - я могу вернуть false и тогда выполнение просто прерывается, мне же необходимо выдать хоть какую-то информацию клиенту, например сообщение, в формате JSON, об отсутствии токена и, возможно, еще некоторыми данными.
Реализация Component объекта и подключение оного в bootstrap раздел конфига приложения - аналогично beforeAction.
Если верну что-то кроме false или исключения, то ответ принимается как true и выполнение продолжается.
dev400: Этим я уже занимался, не подходит. Если я возвращаю что-либо кроме false - приложение продолжает работу, иначе просто прекращается выполнение скрипта. Мне же нужно при определённых условиях возвращать данные в виде JSON объекта (строка). Если я пытаюсь вернуть из beforeAction JSON объект через return $jsonObj, то скрипт продолжает работу, так как по сути в методе возвращается "истина". В описании вопроса я уже описал это ведь.
Используйте поведения (behaviors). Например в Yii из коробки есть такая штука:
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => HttpBearerAuth::className(),
];
return $behaviors;
}
Если хотите свою логику для проверки токена, то создайте класс который будет имлпементировать интерфейс yii\filters\auth\AuthInterface у указывайте его в поведении. В случае ошибки авторизации бросайте UnauthorizedHttpException
В подобной реализации нельзя будет вернуть в случае ошибки JSON, только если выводить ответ через echo/print и прерывать выполнение скрипта с помощью return false.
Вполне себе можно. Унаследуйте ваш базовый контроллер ( в котором подключите поведение) от yii\rest\ActiveController. Все эксепшены выброшенные в этом контроллере и наследниках будут автоматически сериализованы в JSON или XML-формат (в зависимости от content-type запроса). Вот так выглядит исключение в клиенте:
{
"name":"Unauthorized",
"message":"You are requesting with an invalid credential.",
"code":0,
"status":401,
"type":"yii\\web\\UnauthorizedHttpException"
}
DieZz: Наследование от ActiveController и автоматическая сериализация в JSON/XML - это всё есть и само собой разумеется.
Но мне необходимо возвращать JSON определённой схемы.
Вот типовая модель данных:
$data = [
'status' => false, // при ошибке false, при корректности - true
'errors' => [], // ошибки валидации полей формы, например
'logic_errors' => [], // ошибки, записываемые по ходу выполнения в скрипте
'data' => [], // разные данные или пустой массив при отсутствии необходимости, по ситуации
];
То есть при ошибке проверки токена мне нужно вернуть этот же массив, но с status => false и с описаниями ошибок в logic_errors, например.
Дропать эксепшен - это уже пройденный вариант, мне не подходит, так как формат не соответствует принятой в приложении модели данных.
Тут придется немного подпилить ваш базовый контроллер. У yii\rest\Controller есть метод serializeData($data) который вызывается в методе afterAction($action, $result) этого же контроллера. Вам надо его переопределить и задать формат ответа для всех ответов вашего API.
Сейчас посмотрел исходники и в рест-контроллере есть паблик свойство public $serializer, которе содержит имя класса для сериализации ответа. Вам нужно создать свой Serializer в котором по сути определить один метод serialize($data)
class ApiBaseController extends yii\rest\Controller
{
public $serializer = MySerializer::class;
}
class MySerializer extends yii\rest\Serializer
{
/**
* Переопределяем метод сериализации.
*
* @param mixed $data
*
* @return array
*/
public function serialize($data)
{
//Например условно делаем так
return [
'status' => $this->getStatus(),
'errors' => $this->getErrors(),
'logic_errors' => $this->getLogicalErrors(),
'data' => parent::serialize($data),
];
}
}
как и написал DieZz ниже, для этого лучше всего использовать behaviors. Определяете нужное поведение в базовом контроллере, а остальные наследуете от него.