在Yii中默认附带RESTful风格api模块,由controller+路由规则就可以操作module层进行数据库操作。不考虑请求数量的情况下,跟随官方文档方法实现一个带权限验证的RESTful API demo

前期准备条件

安装框架

本次不考虑高级框架中的widget部件,所以选用使用基础版

在工作目录下使用composer运行

composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic

安装完成后,按照官方文档说明配置Apache/Nginx服务器Nginx更适合做静态资源服务器,推荐使用apache

配置URL-rewrite(apache)

设置文档根目录为 "basic/web"

DocumentRoot "path/to/basic/web"
<Directory "path/to/basic/web">
    # 开启 mod_rewrite 用于美化 URL 功能的支持(译注:对应 pretty URL 选项)
    RewriteEngine on
    # 如果请求的是真实存在的文件或目录,直接访问
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    # 如果请求的不是真实文件或目录,分发请求至 index.php
    RewriteRule . index.php

    # if $showScriptName is false in UrlManager, do not allow accessing URLs with script name
    RewriteRule ^index.php/ - [L,R=404]

    # ...其它设置...
</Directory>

通常情况下,此时已经开启url-rewrite功能了,但是很有可能在你的服务器下有多个网站,此时就要针对每一个网站的web目录配置单独的 .htaccess文件

app/web目录下新建<code>.htaccess`文件,写入以下内容

RewriteEngine on
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d

对于开启rest服务后,GET可以请求到数据,POST显示400错误。是因rewrite规则未生效,应该在Apache中htpp.conf中开启AllowOverride all,使单独配置生效

使用数据库进行用户登录验证

连接数据库

app/config/db中配置Mysql用户名和密码。

<?php

return [
    'class' => 'yii\db\Connection',
    'dsn' => 'mysql:host=localhost;dbname=你的数据库名',
    'username' => '你的用户名',
    'password' => '你的密码',
    'charset' => 'utf8',
];

创建数据表

CREATE TABLE `user` (
  `id` int(11) NOT NULL,
  `pid` int(11) NOT NULL DEFAULT '0' COMMENT '父id',
  `username` char(70) NOT NULL COMMENT '用户名',
  `password` char(70) NOT NULL COMMENT '密码',
  `type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '用户类型(0:会员,1:管理员)',
  `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间',
  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态:0禁止;1正常',
  `update_password` int(10) NOT NULL DEFAULT '0' COMMENT '修改密码次数',
  `token` varchar(32) DEFAULT NULL COMMENT '32位hash',
  `accessToken` varchar(32) DEFAULT NULL COMMENT 'access_token32位MD5Hash',
  `authKey` varchar(32) DEFAULT NULL COMMENT 'authKey',
  `created_at` int(11) DEFAULT NULL COMMENT 'created_at-yii自带字段',
  `updated_at` int(11) DEFAULT NULL COMMENT 'updated_at_yii自带字段'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='登录管理表';

ALTER TABLE `user`
  ADD PRIMARY KEY (`id`),
  ADD KEY `pid` (`pid`),
  ADD KEY `username` (`username`),
  ADD KEY `type` (`type`),
  ADD KEY `status` (`status`);

ALTER TABLE `user`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=6;

在Mysql中随意插入两条数据即可。

>
其中created_at updated_at两字段为Yii框架自带字段,长度为11的timestemp,这两个字段的缺失不会对查询用户影响,即不影响登录功能;但是在RESTful的UPDATE操作中缺失将引发500错误,此处需要打开,进行用户注册部分的验证。

使用数据库进行用户登录

在Yii中默认拥有用户鉴权IdentityInterface接口类,在活动记录中继承ActiveRecord并implements IdentityInterface即可在module中实现安全的用户鉴权查询。Yii框架在安装后已经给我们一个登录框架的最小实现
包括cookie管理、form表单的_csrf认证等,但是并没有连接数据库,而是在登录用户modelsapp/models/User.php 中以数组代替数据库查询。
为了连接数据库进行用户登录,应满足活动记录Active Record规则。
- 重命名原app/models/User.phpapp/models/User.php.bac
- 使用Gii工具生成models/User.php
- 实现User.php中关于用户鉴权活动记录的构造方法: findIdentity()findIdentityByAccessToken()findByUsername()getId()getAuthKey()validateAuthKey()validatePassword()

<?php
/*
path:app/models/User.php
*/
namespace app\models;

use Yii;

use yii\db\ActiveRecord;
use yii\web\IdentityInterface;
use yii\behaviors\TimestampBehavior;
use yii\base\NotSupportedException;


class User extends ActiveRecord implements IdentityInterface
{
    public static function tableName()
    {
        return 'user';
    }


    public function behaviors()
    {
        return [
            TimestampBehavior::className(),
        ];
    }
    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            [['pid', 'type', 'status', 'update_password'], 'integer'],
            [['username', 'password'], 'required'],
            [['created_time'], 'safe'],
            [['username', 'password'], 'string', 'max' => 70],
            [['token', 'accessToken', 'authKey'], 'string', 'max' => 32],
        ];
    }


    public function attributeLabels()
    {
        return [
            'id' => 'ID',
            'pid' => 'Pid',
            'username' => 'Username',
            'password' => 'Password',
            'type' => 'Type',
            'created_time' => 'Created Time',
            'status' => 'Status',
            'update_password' => 'Update Password',
            'token' => 'Token',
            'accessToken' => 'Access Token',
            'authKey' => 'Auth Key',
        ];
    }



    /**
     * {@inheritdoc}
     * 以下为UserQuery活动查询生成器ActiveQuery的配置项,当需要多表联查的时候,配置项在ActiveQuery中配置。若gii生成代码时未选择ActiveQuery此项不生成。
     * @return UserQuery the active query used by this AR class.
     */
    public static function find()
    {
        return new UserQuery(get_called_class());
    }
    /**
     * 根据给到的ID查询身份。
     *
     * @param string|integer $id 被查询的ID
     * @return IdentityInterface|null 通过ID匹配到的身份对象
     */
    public static function findIdentity($id)
    {
        return static::findOne($id);
    }

    /**
     * 根据 token 查询身份。
     *
     * @param string $token 被查询的 token
     * @return IdentityInterface|null 通过 token 得到的身份对象
     */
    public static function findIdentityByAccessToken($token, $type = null)
    {
        return static::findOne(['accessToken' => $token]);
    }

    /**
     * @return int|string 当前用户ID
     */
    public function getId()
    {
        return $this->id;
    }


    public static function findByUsername($username)
    {
        return static::findOne(['username' => $username]) || null;
        //也可以写成如下,最终目的是在LoginForm中调用findByUsername()时,返回一个数组,如果是个空数组则表示未登录,若登录成功则返回一个包含用户登录信息的数组
    //  $user = User::find()
    //      ->where(['username'=>$username])
    //                ->asArray()
    //                ->one();
    //        if ($user){
    //            return new static($user);
    //        }
    //        return null;
    }


    /**
     * {@inheritdoc}
     */
    public function getAuthKey()
    {
        return $this->authKey;
    }

    /**
     * {@inheritdoc}
     */
    public function validateAuthKey($authKey)
    {
        return $this->authKey === $authKey;
    }

    /**
     * Validates password
     *
     * @param string $password password to validate
     * @return bool if password provided is valid for current user
     */
    public function validatePassword($password)
    {
        return $this->password === $password;
    }

}

重写 findeByUsername()方法

其中,User::findeByUsername()方法在原自带User.php中是使用遍历数组的方式进行查询的,此处应修改为查询数据库操作,在RESTful风格api中,所有用户的登录均为无状态的,所以实现findeByUsername()方法是必须的,Yii会根据用户的access-token查询username并返回$user已登录用户的实例方便进行下一步权限控制等操作。

<?php
/* 
path:app/models/User.php
*/
namespace app\models;

class User extends ActiveRecord implements IdentityInterface
{

+    public static function findByUsername($username)
+    {
+
+        $user = User::find()
+                ->where(['username'=>$username])
+                ->asArray()
+                ->one();
+        if ($user){
+            return new static($user);
+        }
+        return null;
+    }

}

>
重写findByUsername()方法在用户进行鉴权的时候,在LoginForm中触发findByUsername()方法,并返回一个$user实例与LoginForm中数据进行对比,对比通过即为登录成功

此时我们已经完成修改登录用户查询数据表的工作。
打开 localhost验证登录完成。

>
RESTFulAPI的特点是无状态,使用token验证用户登录:下面我们先实现一个无验证的最简单RESTful-api,之后再添加登录验证

配置urlManager/json允许

在访问时url,我们的url依旧是常规的参数形式http://localhost/index.php?r=site¶m=xxx的形式,我们希望使用rest风格的路由格式,则需要配置urlManager。我们将app/config/web.php中的urlManager配置项单独成一个配置文件,方便后续修改。
- 打开app/config/web.php中的urlManager部分的注释
- 在app/config/目录下新建urlManager.php文件

<?php

return [
    'enablePrettyUrl' => true,  //美化url==ture
    'enableStrictParsing' => false,  //不启用严格解析 
    'showScriptName' => false,   //隐藏index.php
    'rules' =>[
        /**
        * 下一步将在此处针对各个controller配置各自的url重写规则
        */
+       ['class' => 'yii\rest\UrlRule', 'controller' => 'user'],
        //以上为关于Usercontroller的rule配置项。
   ],
];
  • app/config/web.php中引入urlManager

app/config/web.php $urlManager = require __DIR__.'/urlManager.php'; $config = [ 'components' => [ 'urlManager' => $urlManager, ], ];

此时访问http://localhost/gii即可进入gii工具则表示url生效

配置json响应(json request allow)

在启用urlManager后,应该启用json请求允许,方便框架接收json请求。对于框架接收json请求,需要单独配置。

// app/config/web.php
'request' => [
    'parsers' => [
        'application/json' => 'yii\web\JsonParser',
        'text/json' => 'yii\web\JsonParser',// 在post请求服务端的时候, 根据content-type寻找正确的json解释器,不配置有可能报400错误
    ]
]

yii默认启用包括json xml等格式在内的所有响应格式,随意对于response项无需配置。在http头中指定A ccept:applcation/json即可通知Yii返回json格式响应。

UserController

Yii是一个MVC框架,对于RESTful而言,不需要考虑View层的事。只需要完成Model层的数据CURD及Controller层的路由管理即可。

完成UserContrller

/app/controller/目录下新建UserControler控制器。
对于RESTful控制器,Yii实现了yii\rest\ActiveController基类,UserController需要继承此类。并在其中$modelClass指定活动记录表ActiveRecord

namespace app\controllers;

use yii\rest\ActiveController;

class UserController extends ActiveController
{
    public $modelClass = 'app\models\User';
}

GET请求测试接口完成度

使用curl测试GET请求User控制器即可

$ curl -i -H "Accept:application/json" "http://localhost/users"

若返回User数据表中数据则表示第一步实现REST已经成功。对于某些时候post请求报401错误,则查看Apache/Nginx配置文件中Rewrite规则。

发表评论

电子邮件地址不会被公开。 必填项已用*标注