Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

REST API Guide #165

Open
CyberPunkCodes opened this issue Feb 21, 2017 · 4 comments
Open

REST API Guide #165

CyberPunkCodes opened this issue Feb 21, 2017 · 4 comments

Comments

@CyberPunkCodes
Copy link

CyberPunkCodes commented Feb 21, 2017

One of the issues I have just ran into, is using Yii2 to create a REST API. The official docs lack in this area (and many others). It takes into account, assumptions, that you know where things go. While I have used Yii2 for a while, it still would be nice to get the full story, instead of bits and pieces and having to find the rest. The docs say "Implement xxx", but doesn't tell you how, so we have to fill in the gaps. My experience with Yii helps me find these answers quickly, but beginners will get stuck on these things for days.

Your cookbook is helpful, and shows great examples of use-cases and a "how-to" for specific things. Which is why I think it would be great if you could add a full walk through on setting up a REST API, without the gaps of assuming everyone knows what they are doing. Assuming that they know nothing about Yii :)


While I just got a QueryParamAuth finally working, it was a pain. After following various guides (including official Yii docs), it leaves out a lot of necessary info. I will start with what I have so far.

I copied the "frontend" app, renamed it to "api", and removed unnecessary things like the "views" folder.

Here is a tree view:

[api]
- [config]
- - bootstrap.php
- - main-local.php
- - main.php
- - params-local.php
- - params.php
- [modules]
- - [v1]
- - - [components]
- - - -ApiController.php - (extends \yii\rest\ActiveController)
- - - [controllers]
- - - - UserController.php - (extends ApiController)
- - - [models]
- - - - User.php - (sets tableName, primaryKey, rules)
- - - Module.php

api/config/main.php

<?php
$params = array_merge(
    require(__DIR__ . '/../../common/config/params.php'),
    require(__DIR__ . '/../../common/config/params-local.php'),
    require(__DIR__ . '/params.php'),
    require(__DIR__ . '/params-local.php')
);

return [
    'id' => 'app-api'
    'name' => 'My Yii2 API',
    'basePath' => dirname(__DIR__),
    'controllerNamespace' => 'api\controllers',
    'bootstrap' => ['log'],
    'modules' => [
        'v1' => [
            'basePath' => '@app/modules/v1',
            'class' => 'api\modules\v1\Module'
        ]
    ],
    'components' => [
        'user' => [
            'identityClass' => 'common\models\User',
            'enableSession' => false,
            'loginUrl' => null,
            'enableAutoLogin' => false,
        ],
        'request' => [
            'class' => '\yii\web\Request',
            'enableCookieValidation' => false,
            'parsers' => [
                'application/json' => 'yii\web\JsonParser',
            ]
        ],
        'log' => [
            'traceLevel' => YII_DEBUG ? 3 : 0,
            'targets' => [
                [
                    'class' => 'yii\log\FileTarget',
                    'levels' => ['error', 'warning'],
                ],
            ],
        ],
        'urlManager' => [
            'enablePrettyUrl' => true,
            'enableStrictParsing' => true,
            'showScriptName' => false,
            'rules' => [
                [
                    'class' => 'yii\rest\UrlRule',
                    'controller' => 'v1/user',
                ]
            ],
        ],
    ],
    'params' => $params,
];

api/modules/v1/components/ApiController.php

<?php
namespace api\modules\v1\components;

use yii\filters\auth\QueryParamAuth;
use yii\filters\Cors;

/**
 * API Base Controller
 * All controllers within API app must extend this controller!
 */
class ApiController extends \yii\rest\ActiveController
{

    public function behaviors()
    {
        $behaviors = parent::behaviors();

        // add CORS filter
        $behaviors['corsFilter'] = [
            'class' => Cors::className(),
        ];
        
        // add QueryParamAuth for authentication
        $behaviors['authenticator'] = [
            'class' => QueryParamAuth::className(),
        ];

        // avoid authentication on CORS-pre-flight requests (HTTP OPTIONS method)
        $behaviors['authenticator']['except'] = ['options'];

        return $behaviors;
    }

}

api/modules/v1/controllers/UserController.php

<?php
namespace api\modules\v1\controllers;

/**
 * User Controller
 */
class UserController extends \api\modules\v1\components\ApiController
{
    public $modelClass = 'api\modules\v1\models\User';
}

api/modules/v1/models/User.php

<?php
namespace api\modules\v1\models;

use \yii\db\ActiveRecord;

/**
 * User Model
 */
class User extends ActiveRecord
{
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return 'user';
	}

    /**
     * @inheritdoc
     */
    public static function primaryKey()
    {
        return ['id'];
    }

    /**
     * Define rules for validation
     */
    public function rules()
    {
        return [
            [['username', 'email', 'password_hash'], 'required']
        ];
    }
}

api/modules/v1/Module.php

<?php
namespace api\modules\v1;

class Module extends \yii\base\Module
{
    public $controllerNamespace = 'api\modules\v1\controllers';

    public function init()
    {
        parent::init();
    }
}

Then lastly, outside of the API app, in "common" directory, modify /common/models/User.php to handle the access_token

/**
 * Finds an identity by the given token.
 *
 * @param string $token the token to be looked for
 * @return IdentityInterface|null the identity object that matches the given token.
 */
public static function findIdentityByAccessToken($token, $type = null)
{
    return static::findOne(['access_token' => $token, 'status' => self::STATUS_ACTIVE]);
}

Now, it still doesn't work. You have to manually put in an access_token in order for it to work. I grabbed it from another guide:

  • Either manually create a new column, or create a migration

    ./yii migrate/create update_user_table

Replace with these 2 functions:

public function up()
{
    $this->addColumn('{{%user}}', 'access_token', $this->string()->unique()->after('auth_key'));
}

public function down()
{
    $this->dropColumn('{{%user}}', 'access_token');
}
  • Then manually set access_token for the user in your db to: 4p9mj82PTl1BWSya7bfpU_Nm8u07hkcB

Now you can call it using PostMan: http://api.mydomain.dev/v1/users?access-token=4p9mj82PTl1BWSya7bfpU_Nm8u07hkcB

NOTE: I have mapped api.mydomain.dev as a VHOST to /path/to/yii2site/api/web


Now what? There is no guidance on how to set the access_token for the users, or how to revoke it. I assume this would be done on login. I can do this, but others may not be able to! You can create a login process for your API, but also could set it during normal frontend login, and use the API for filling the GridView and DetailView.

What about revoking the token? If you look at the findByPasswordResetToken() function, it splits the token and checks the time stamp (second half after the underscore). The findIdentityByAccessToken() (As shown in Yii2 docs) doesn't show any verification on the token itself. You don't want the token to live forever unchecked!


I think there needs to be a full step-by-step guide on how to properly setup a REST API, and go further by showing how to login, validate the token, handle invalid token (ie: session expired), logout (removing the token). Maybe split into a few parts to handle the different types: Query, BasicAuth, Custom, etc.

@mrFleshka
Copy link

@WadeShuler
I think you can create PR with your vision about that, and samdark helps with implementing this information to cookbook.

@samdark
Copy link
Owner

samdark commented Mar 7, 2017

Yes, that would be good.

@buttflattery
Copy link

any updates on this issue when will we be having the proper and complete guide for the REST API

@samdark
Copy link
Owner

samdark commented Nov 25, 2018

When someone contributes. I'm too busy with Yii 3.0 currently.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants