* @since 2.0 */ class Controller extends \yii\web\Controller { /** * The name of the header parameter representing the API version number. */ const HEADER_VERSION = 'version'; /** * @var string|array the configuration for creating the serializer that formats the response data. */ public $serializer = 'yii\rest\Serializer'; /** * @inheritdoc */ public $enableCsrfValidation = false; /** * @var string the chosen API version number * @see supportedVersions */ public $version; /** * @var array list of supported API version numbers. If the current request does not specify a version * number, the first element will be used as the chosen version number. For this reason, you should * put the latest version number at the first. */ public $supportedVersions = ['1.0']; /** * @var array list of supported response formats. The array keys are the requested content MIME types, * and the array values are the corresponding response formats. The first element will be used * as the response format if the current request does not specify a content type. */ public $supportedFormats = [ 'application/json' => Response::FORMAT_JSON, 'application/xml' => Response::FORMAT_XML, ]; /** * @inheritdoc */ public function behaviors() { return [ 'verbFilter' => [ 'class' => VerbFilter::className(), 'actions' => $this->verbs(), ], ]; } /** * @inheritdoc */ public function init() { parent::init(); $this->resolveFormatAndVersion(); } /** * @inheritdoc */ public function beforeAction($action) { if (parent::beforeAction($action)) { $this->authenticate(); return true; } else { return false; } } /** * @inheritdoc */ public function afterAction($action, $result) { $result = parent::afterAction($action, $result); return $this->serializeData($result); } /** * Resolves the response format and the API version number. * @throws BadRequestHttpException */ protected function resolveFormatAndVersion() { $this->version = reset($this->supportedVersions); Yii::$app->getResponse()->format = reset($this->supportedFormats); $types = Yii::$app->getRequest()->getAcceptableContentTypes(); if (empty($types)) { $types['*/*'] = []; } foreach ($types as $type => $params) { if (isset($this->supportedFormats[$type])) { Yii::$app->getResponse()->format = $this->supportedFormats[$type]; if (isset($params[self::HEADER_VERSION])) { if (in_array($params[self::HEADER_VERSION], $this->supportedVersions, true)) { $this->version = $params[self::HEADER_VERSION]; } else { throw new BadRequestHttpException('You are requesting an invalid version number.'); } } return; } } if (!isset($types['*/*'])) { throw new BadRequestHttpException('None of your requested content types is valid.'); } } /** * Declares the allowed HTTP verbs. * Please refer to [[VerbFilter::actions]] on how to declare the allowed verbs. * @return array the allowed HTTP verbs. */ protected function verbs() { return []; } /** * Authenticates the user. * This method implements the user authentication based on HTTP basic authentication. * @throws UnauthorizedHttpException if the user is not authenticated successfully */ protected function authenticate() { $apiKey = Yii::$app->getRequest()->getAuthUser(); if ($apiKey === null || !Yii::$app->getUser()->loginByToken($apiKey)) { Yii::$app->getResponse()->getHeaders()->set('WWW-Authenticate', 'Basic realm="api"'); throw new UnauthorizedHttpException($apiKey === null ? 'Please provide an API key.' : 'You are requesting with an invalid API key.'); } } /** * Serializes the specified data. * The default implementation will create a serializer based on the configuration given by [[serializer]]. * It then uses the serializer to serialize the given data. * @param mixed $data the data to be serialized * @return mixed the serialized data. */ protected function serializeData($data) { return Yii::createObject($this->serializer)->serialize($data); } }