用Yii2构建自己的API框架(五、3创建User注册账号接口)

895 阅读3分钟

提示

本教程相关代码托管在Gitee:gitee.com/toshcn/micr…

创建UserRegisterForm注册账号表单,通过表单接入前端输入,验证输入的数据,生成用户表记录。

修改Utils工具类

打开common/components/Utils.php文件,增加正则验证方法,修改内容如下:

<?php
/**
 * @link https://gitee.com/toshcn/micro-hau
 * @copyright Copyright (c) 2022/4/20 micro-hau
 * @author toshcn <toshcn@foxmail.com>
 */

namespace common\components;

use yii\base\BaseObject;

/**
 * 公共的常用工具类组件 调用方式 \Yii::$app->utils->方法名()
 * Class Utils
 * @package common\components
 */
class Utils extends BaseObject
{
    // ...
    // 增加以下内容
    /**
     * 正则检查
     * @param {String} value 要检查的值
     * @param {Object} type 正则检查类型:cn_mobile手机号
     * @return bool|false|int
     */
    public static function regExp($value, $type)
    {
        if (empty($value)) {
            return false;
        }

        $res = false;
        switch ($type) {
            case 'cn_mobile':
                /**
                 * 数据卡:14号段以前为上网卡专属号段,如中国联通的是145,中国移动的是147,中国电信的是149等等。
                 * 虚拟运营商:170[1700/1701/1702]、162(电信),1703/1705/1706、165(移动),1704/1707/1708/1709(联通)、171、167(联通)
                 * 卫星通信: 1740[0-5] (电信),1349(移动)
                 * 物联网网号:10648、1440 (移动),10646、146(联通),10649、1410(电信)
                 * 国家工信部应急通信:1740[6-9],1741[0-2]
                 * 手机号码: 13[0-9], 14[5,6,7,8,9], 15[0-3, 5-9], 16[2,5,6,7], 17[0-8], 18[0-9], 19[0-3, 5-9]
                 * 移动号段: 13[4-9],147,148,15[0-2,7-9],165,170[3,5,6],172,178,18[2-4,7-8],19[5,7,8]
                 * 联通号段: 130,131,132,145,146,155,156,166,167,170[4,7,8,9],171,175,176,185,186,196
                 * 电信号段: 133,149,153,162,170[0,1,2],173,174[0-5],177,180,181,189,19[0,1,3,9]
                 * 广电号段: 192
                 *
                 * 作者:bingxuePI
                 * 链接:https://www.jianshu.com/p/5fbb85967bfd
                 */
                $res = preg_match('/^(13[0-9]|14[5-9]|15[0-3,5-9]|16[2,5,6,7]|17[0-8]|18[0-9]|19[0-3,5-9])\d{8}$/i', $value);
                break;
                
            case 'power_password':
                // 强密码 必须包含大小写字母和数字的组合,长度在8-20之间
                $res = preg_match('/^.*(?=.{8,20})(?=.*\d)(?=.*[A-Z]{1,})(?=.*[a-z]{1,})(?=.*[!@#$%^&*?\(\)]{0,}).*$/', $value);
                break;
        }

        return $res;
    }
}

修改BaseModel类

打开common/models/BaseModel.php文件,获取表单第一个校验错误字段,修改内容如下:

<?php
/**
 * @link https://gitee.com/toshcn/micro-hau
 * @copyright Copyright (c) 2022/4/21 micro-hau
 * @author toshcn <toshcn@foxmail.com>
 */

namespace common\models;

use yii\base\Model;

/**
 * 公共模型基类
 * Class BaseModel
 * @package common\models
 */
class BaseModel extends Model
{
    /**
     * 获取表单第一个校验错误信息
     * @return string
     */
    public function getFirstErrorMessage()
    {
        $msg = \Yii::t('common', 'Field Error.');
        if ($this->getErrors()) {
            foreach ($this->getErrors() as $name => $error) {
                return $msg = $error[0];
            }
        }
        return $msg;
    }

    /**
     * 获取表单第一个校验错误字段
     * @return string|boolean
     */
    public function getFirstErrorField()
    {
        if ($this->getErrors()) {
            foreach ($this->getErrors() as $name => $error) {
                return $name;
            }
        }
        return false;
    }
    
    /**
     * 获取表单第一个校验错误的第一个错误信息
     * @return array|boolean
     */
    public function getFirstOneError()
    {
        if ($this->getErrors()) {
            foreach ($this->getErrors() as $name => $error) {
                return [$name => $error[0]];
            }
        }
        return false;
    }
}

修改Api控制器类,增加检测POST请求方法

打开api/controllers/ApiController.php文件,增加内容如下:

<?php
/**
 * @link https://gitee.com/toshcn/micro-hau
 * @copyright Copyright (c) 2022/4/20 micro-hau
 * @author toshcn <toshcn@foxmail.com>
 */

namespace api\controllers;

use yii\rest\Controller;
use yii\filters\Cors;
use yii\helpers\ArrayHelper;

/**
 * API 项目基类控制器 api项目所有接口控制器都要继承此类
 * Class ApiController
 * @package api\controllers
 */
class ApiController extends Controller
{
    const ERROR_CODE_NOT_POST = 4405; // 接口错误码 不是POST请求
    const ERROR_CODE_FORM_FIELD_ERROR = 4001; // 接口错误码 表单字段错误
    const ERROR_CODE_FORM_ERROR = 5000; // 接口错误码 服务器出错 表单保存出错

    // ...

    /**
     * 检测post请求
     * 如果非post请求,发送4405代码
     * @return mixed
     */
    public function validateIsPost()
    {
        if (!\Yii::$app->getRequest()->getIsPost()) {
            $response = \Yii::$app->getResponse();
            $response->data = \Yii::$app->api->error(static::ERROR_CODE_NOT_POST, \Yii::t('common', 'The request is not in a post way.'));
            $response->send();
        }
        return true;
    }
}
    

修改User模型类,增加密码生成和验证方法

打开common/models/User.php文件,修改内容如下:

<?php
/**
 * @link https://gitee.com/toshcn/micro-hau
 * @copyright Copyright (c) 2022/4/16 micro-hau
 * @author toshcn <toshcn@foxmail.com>
 */

namespace common\models;

use Yii;
use yii\db\ActiveRecord; // 活动记录模型
use yii\filters\RateLimitInterface; // 速率限制接口
use yii\web\IdentityInterface; // 用户身份接口
// ...
class User extends ActiveRecord implements IdentityInterface, RateLimitInterface
{
    // ...
    // 增加如下方法
    /**
     * 验证密码
     * @param string $password 用户密码
     * @return bool
     */
    public function validatePassword($password)
    {
        $pwd = sha1(md5($password . $this->password_salt));
        return Yii::$app->getSecurity()->validatePassword($pwd, $this->password_hash);
    }

    /**
     * 生成账号密码
     * @param string $password
     * @throws \yii\base\Exception
     */
    public function setPassword($password)
    {
        $passwordHashTimes = 10;//密码时间强度 数值越大 运行速度越慢
        // 随机生成20位字符 作为密码盐值
        $this->password_salt = Yii::$app->getSecurity()->generateRandomString(20);
        $pwd = sha1(md5($password . $this->password_salt)); // 密码生成算法
        // 生成密码哈希值
        $this->password_hash = Yii::$app->getSecurity()->generatePasswordHash($pwd, $passwordHashTimes);
    }

    /**
     * 生成Api登录认证token
     * Generates access token
     * @throws \yii\base\Exception
     */
    public function generateAccessToken()
    {
        // 生成唯一的token
        $this->access_token = md5($this->country_code . $this->mobile . '_' . Yii::$app->getSecurity()->generateRandomString(20));
        // token过期时间
        $this->access_token_expire = time() + 7 * 86400;
    }
}

创建UserRegisterForm账号注册表单类

在项目api/modules/v1/forms目录下创建UserRegisterForm.php文件,文件内容如下:

<?php
/**
 * @link https://gitee.com/toshcn/micro-hau
 * @copyright Copyright (c) 2022/4/21 micro-hau
 * @author toshcn <toshcn@foxmail.com>
 */

namespace api\modules\v1\forms;

use common\models\User;
use Yii;
use common\models\BaseModel;

/**
 * User模型注册表单
 * Class UserForm
 * @package api\modules\v1\forms
 */
class UserRegisterForm extends BaseModel
{
    public $mobile;                 // 用户账号手机号
    public $countryCode = 86;       // 用户账号手机号国家代码: 86(中国)
    public $nickname;               // 用户账号昵称
    public $password;               // 用户账号密码

    private $_user = null;          // 用户对象

    /**
     * 表单字段规则
     * @return array
     */
    public function rules()
    {
        return [
            [['mobile', 'nickname', 'password'], 'trim'],
            [['countryCode'], 'number', 'skipOnError' => true],
            [['mobile', 'countryCode', 'nickname', 'password'], 'required'],
            ['mobile', 'validateMobile', 'skipOnError' => true], // 行内验证器 检测手机号码格式
            // 限制手国家代码+机号码唯一
            [
                ['mobile'],
                'unique',
                'skipOnError' => true,
                'targetClass' => User::class,
                'targetAttribute' => ['mobile', 'countryCode' => 'country_code'],
                // 自定义错误消息
                'message' => Yii::t('common', 'Mobile "{value}" has already been taken.')
            ],
            // 限制昵称唯一
            [
                ['nickname'],
                'unique',
                'skipOnError' => true,
                'targetClass' => User::class,
                'targetAttribute' => ['nickname'],
                'message' => Yii::t('common', 'Nickname "{value}" has already been taken.')
            ],
        ];
    }

    /**
     * 检测手机号码格式
     * @param string $attribute 当前检测的字段名
     * @param array $params 以名-值对形式提供的额外参数
     * @param \yii\validators\InlineValidator $validator 当前行内验证器 InlineValidator实例。此参数自版本 2.0.11 起可用。
     */
    public function validateMobile($attribute, $params, $validator)
    {
        if (!$this->hasErrors()) {
            if (!Yii::$app->utils->regExp($this->$attribute, 'cn_mobile')) {
                $validator->addError($this, $attribute, Yii::t('common', 'The mobile "{value}" is invalid for {attribute}.'));
            }
        }
    }

    /**
     * 返回用户对象
     * @return null|@common\models\User
     */
    public function getUser()
    {
        return $this->_user;
    }

    /**
     * 注册账号
     * @return bool
     */
    public function register()
    {
        // 打开数据库事务
        $trans  = Yii::$app->getDb()->beginTransaction();
        try {
            // rules()规则都验证通过
            if ($this->validate()) {
                $user = new User();
                $user->mobile = $this->mobile;
                $user->nickname = $this->nickname;
                $user->country_code = $this->countryCode;
                $user->generateAccessToken(); // 生成api接口token
                $user->setPassword($this->password); // 生成密码
                $user->created_at = $user->updated_at = date('Y-m-d H:i:s');

                if ($user->save(false)) {
                    // 创建用户成功 提交事务
                    $trans->commit();
                    $this->_user = $user; // 保存用户对象
                    return true;
                }
            }
            // 创建失败 数据库回滚
            $trans->rollBack();
        } catch(\Exception $e) {
            $trans->rollBack();
        }
        return false;
    }
}

创建AccountControll账号控制器类

在项目api/modules/v1/controllers目录下创建AccountControll.php文件,文件内容如下:

<?php /** @noinspection PhpUndefinedFieldInspection */

/**
 * @link https://gitee.com/toshcn/micro-hau
 * @copyright Copyright (c) 2022/4/22 micro-hau
 * @author toshcn <toshcn@foxmail.com>
 */

namespace api\modules\v1\controllers;

use Yii;
use api\controllers\ApiController;
use api\modules\v1\forms\UserRegisterForm;

/**
 * 账号登录,注册相关控制器
 * Class AccountController
 * @package api\modules\v1\controllers
 */
class AccountController extends ApiController
{
    /**
     * 账号注册
     */
    public function actionRegister()
    {
        $this->validateIsPost();
        $form = new UserRegisterForm();
        // 从post中取出数据,赋值给注册表单对应字段
        if ($form->load(Yii::$app->getRequest()->post(), '') && $form->register()) {
            return Yii::$app->api->success();
        }
        if ($form->hasErrors()) {
            return Yii::$app->api->error(parent::ERROR_CODE_FORM_FIELD_ERROR, $form->errors);
        }
        return Yii::$app->api->error(parent::ERROR_CODE_FORM_ERROR, Yii::t('common', 'Account registration failed.'));
    }
}

测试接口

用Postman模拟请求,设置请求标头content-typeapplication/json; charset=UTF-8, 提交表单如:

{
    "mobile": "13188888888",
    "password": "Test12345678",
    "nickname": "test"
}

将会看到如下内容:

{
    "status": 1,
    "code": 1,
    "message": "success",
    "data": []
}