thinkphp5开发restful-api接口学习 笔记二

第4节 为api项目搭建数据库

什么是数据库三大范式

  1. 每一列都是不可分割的原子数据项
  2. 实体的属性完全依赖于主关键字
  3. 任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖)

数据库中设计中的常见问题

  1. 字段混在了一起


  1. 数据表混在了一起

  1. 不会处理表关系

一对一 (学生姓名, 学号)
一对多 (老师, 学生)
多对多 (学生, 课程)

设计数据库的小技巧

  1. 一个对象, 一张表
  2. 一张表, 一个主键
  3. 表名中有数据库名做前缀
  4. 字段名中有表名做前缀
  5. 前缀后加缩写
  6. 数据表关系处理

范式越高越好?

第5节 使用markdown书写接口文档

基本语法

  1. 六级标题
  2. 目录索引
  3. 加粗
  4. 斜体
  5. 删除线
  6. 引用
  7. 代码段
  8. 表格
  9. 行内代码
  10. 无序列表
  11. 有序列表
  12. 图片
  13. 超链接

第6节(判断数据库中是否有此用户)

用户登录举例

post www.test.com/api api.test.com

参数 必选 类型 说明
time true int 时间戳 (用于确定接口的访问时间)
token true string 确定访问者身份 (MD5(USER_MD5(time)_USER))
username true string 只接受手机号
password true string 用户密码
   {
       "ret": 200, // 返回结果状态。200:接口正常请求并返回/40*:服务端的数据有误/500:服务器运行错误
       "data": {
           "user_id": "27", // 用户id
           "user_tag": "1" // 用户身份
       },
       "msg": "" // 401:用户名不存在!/402:手机号不存在!/403:密码不正确!
   }

第7节 为项目配置URL

需求分析

  • api.tp5.com/user/2 ===> www.tp5.com/index.php/api/user/index/id/2

配置主域名和二级域名

  1. 打开phpstudy
  2. 其他选项菜单 ==> 站点域名管理

网站域名:www.tp5.com
网站域名:G:\phpStudy\WWW\tp5\public
第二域名:api.tp5.com
网站端口: 80

  1. 配置hosts(域名重定向)
127.0.0.1 www.tp5.com
127.0.0.1 api.tp5.com

使用tp5路由进行URL解析

  1. 为sublime安装新插件(方便操作侧边栏)

package control: install package
SideBarEnhancements

  1. 修改config.php(开启路由功能)

路径:G:\phpStudy\WWW\tp5\application\config.php

// 是否开启路由
'url_route_on' => true,
// 域名部署
'url_domain_deploy' => true,

设置路由文件

  1. 方法一:路由里不写参数

D:\phpStudy\WWW\tp5\application\route.php

<?php
use think\Route;
Route::domain('api', 'api');
Route::get('user', 'user/index');

新建 api/controller/User.php

<?php
namespace app\api\controller;
class User
{
   public function index()
   {
       echo 'user/index';
       echo '<br>';
       $id = input('id');
       echo $id;
       echo '<br>';
       $name = input('name');
       print_r($name);
   }
}

访问下面地址都可以打开:

  1. http://api.tp5.com/user/index?id=1&name=lisi
  2. http://api.tp5.com/user?id=1&name=lisi
  3. http://api.tp5.com/user/id/1/name/lisi
  4. http://www.tp5.com/api/user/index?id=1&name=lisi
  5. http://www.tp5.com/api/user/index/id/1/name/lisi
    输出:
user/index
1
lisi

下面这些访问拿不到参数
http://api.tp5.com/user/index/id/1/name/lisi
输出:

user/index
  1. 方法二:路由里写参数

D:\phpStudy\WWW\tp5\application\route.php

<?php
use think\Route;
Route::domain('api', 'api');
Route::get('user/:id/:name', 'user/index');

新建 api/controller/User.php

<?php
namespace app\api\controller;
class User
{
   public function index()
   {
       echo 'user/index';
       echo '<br>';
       $id = input('id');
       echo $id;
       echo '<br>';
       $name = input('name');
       print_r($name);
   }
}

下面这些访问拿不到参数

  1. http://api.tp5.com/user/1/lisi
  2. http://api.tp5.com/user?id=1&name=lisi
  3. http://www.tp5.com/api/user/index?id=1&name=lisi
  4. http://www.tp5.com/api/user?id=1&name=lisi
  5. http://www.tp5.com/api/user/index/id/1/name/lisi
    输出:
user/index
1
lisi

*下面这种会报错 *
http://api.tp5.com/user/index?id=1&name=lisi
非法请求:api/user/index


第8节 接口安全

常见的安全问题以及解决方案

  1. 接口被大规模调用消耗系统资源,影响系统的正常访问,甚至系统瘫痪

解决方案: 获取 timestamp (时间戳), 设置接口失效时间

  1. 接口数据被黑客篡改(伪造请求)

解决方案: 对参数加密, 生成 token , 判断 token 是否正确

  1. 数据被黑客截取

解决方案: 使用 https , 用证书对数据进行加密, 即使数据被截取, 对黑客也没有意义

黑客可以获取数据, 但是无法获取数据的加密方法

我们api项目的安全设计

  1. time

时间戳, 用于判断请求是否超时, 设置为30秒

  1. token

其他参数加密而来, 保证数据不被篡改

  1. 敏感信息加密传输

接收加密过的用户密码, 用户密码永不返回
最好使用 https, 所有信息都会被加密

第9节 接口开发前的准备工作(参数过滤)

配置路由

  1. 开启路由功能

G:\phpStudy\WWW\tp5\application\config.php

// 是否开启路由
'url_route_on' => true,
// 域名部署
'url_domain_deploy' => true,
  1. 配置route.php

G:\phpStudy\WWW\tp5\application\route.php

<?php
use think\Route;

// api.tp5.com ===> www.tp5.com/index.php/api
Route::domain('api','api');
// post api.tp5.com/user  --->  user.php login()
Route::post('user','user/login');

使用common.php统一处理参数过滤
G:\phpStudy\WWW\tp5\application\api\controller\Common.php

<?php
namespace app\api\controller;
use think\Controller;
use think\Request;
use think\Validate;
class Common extends Controller {
    protected $request; // 用来处理参数
    protected $validater; // 用来验证数据/参数
    protected $params; // 过滤后符合要求的参数
    protected $rules = array(
            'User'=>array(......);
    protected function _initialize() {
        parent::_initialize();
        $this->request = Request::instance();
        $this->check_time($this->request->only(['time']));
        $this->check_token($this->request->param());
        $this->params = $this->check_params($this->request->except(['time','token']));
    }

自定义返回信息函数
G:\phpStudy\WWW\tp5\application\api\controller\Common.php

 
/**
* api 数据返回
 
 * @param  [int] $code [结果码 200:正常/4**数据问题/5**服务器问题]
 * @param  [string] $msg  [接口要返回的提示信息]
 * @param  [array]  $data [接口要返回的数据]
 * @return [string]       [最终的json数据]
*/
public function return_msg($code, $msg = '', $data = []) {
 
    /*********** 组合数据  ***********/
    $return_data['code'] = $code;
    $return_data['msg']  = $msg;
    $return_data['data'] = $data;
 
    /*********** 返回信息并终止脚本  ***********/
    echo json_encode($return_data);die;
}

验证time
G:\phpStudy\WWW\tp5\application\api\controller\Common.php

 
/**
* 验证请求是否超时
 * @param  [array] $arr [包含时间戳的参数数组]
 * @return [json]      [检测结果]
*/
public function check_time($arr) {
    //intval() string 11返回11    11hello返回11 hello返回0  array 空array返回0 非空array返回1
    //intval($arr['time']) <= 1        等于1就是非空array(不是我们想要的)  
    //小于1即等于0就是string或者空array或者是空(也不是我们想要的) 
    //我想要想要的是一串数字,所以 intval($arr['time']) <= 1就报错
    if (!isset($arr['time']) || intval($arr['time']) <= 1) {
        $this->return_msg(400, '时间戳不正确!');
    }
    if (time() - intval($arr['time']) > 60) {
        $this->return_msg(400, '请求超时!');
    }
}

验证token
G:\phpStudy\WWW\tp5\application\api\controller\Common.php

 
/**
* 验证token(防止篡改数据)
 * @param  [array] $arr [全部请求参数]
 * @return [json]      [token验证结果]
*/
public function check_token($arr) {
 
    /*********** api传过来的token  ***********/
    if (!isset($arr['token']) || empty($arr['token'])) {
        $this->return_msg(400, 'token不能为空!');
    }
    $app_token = $arr['token']; // api传过来的token
 
    /*********** 服务器端生成token  ***********/
    unset($arr['token']);
    $service_token = '';
    foreach ($arr as $key => $value) {
        $service_token .= md5($value);
    }
    $service_token = md5('api_' . $service_token . '_api'); // 服务器端即时生成的token
 
    /*********** 对比token,返回结果  ***********/
    if ($app_token !== $service_token) {
        $this->return_msg(400, 'token值不正确!');
    }
}

为每个接口配置验证规则
G:\phpStudy\WWW\tp5\application\api\controller\Common.php

protected $rules = array(
    'User' => array(
        'login' => array(
            'user_name' => ['require', 'chsDash', 'max' => 20],
            'user_pwd'  => 'require|length:32',
        ),
    ),
);

验证参数
G:\phpStudy\WWW\tp5\application\api\controller\Common.php

 
/**
* 验证参数 参数过滤
 * @param  [array] $arr [除time和token外的所有参数]
 * @return [return]      [合格的参数数组]
*/
public function check_params($arr) {
 
    /*********** 获取参数的验证规则  ***********/
    $rule = $this->rules[$this->request->controller()][$this->request->action()];
 
    /*********** 验证参数并返回错误  ***********/
    $this->validater = new Validate($rule);
    if (!$this->validater->check($arr)) {
        $this->return_msg(400, $this->validater->getError());
    }
 
    /*********** 如果正常,通过验证  ***********/
    return $arr;
}

第9节 获取验证码

接口文档
新建api_user表

DROP TABLE IF EXISTS `api_user`;
CREATE TABLE `api_user` (
  `user_id` int(11) NOT NULL AUTO_INCREMENT,
  `user_phone` char(11) NOT NULL,
  `user_nickname` varchar(255) NOT NULL COMMENT '昵称',
  `user_email` varchar(255) NOT NULL,
  `user_rtime` int(11) NOT NULL COMMENT 'register time',
  `user_pwd` char(32) NOT NULL,
  `user_icon` varchar(255) NOT NULL COMMENT '用户头像',
  PRIMARY KEY (`user_id`)
) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

验证码原理

  1. 生成及发送
  • 点击获取手机码
  • 发送手机号到后台
  • 后台生成手机码
  • 用session保存手机码及手机号
  • 用短信发送平台的接口发送出去
  1. 验证
  • 获取用户输入的手机码及手机号
  • 取出session保存的内容
  • 对比验证
  • 返回信息, 结束

配置路由

注意: get方式没有参数名, 所以要注意参数的顺序, 对号入座.

G:\phpStudy\WWW\tp5\application\route.php

// 获取验证码
Route::get('code/:time/:token/:username/:is_exist','code/get_code');

参数过滤

在common.php里简单过滤, 具体验证放在code.php里

G:\phpStudy\WWW\tp5\application\api\controller\Common.php

'Code' => array(
    'get_code' => array(
        'username' => 'require',
        'is_exist' => 'require|number|length:1',
    ),
),

检测用户名
G:\phpStudy\WWW\tp5\application\api\controller\Code.php

namespace app\api\controller;
use phpmailer\phpmailer;
use submail\messagexsend;
 
class Code extends Common {
    public function get_code() {
        $username      = $this->params['username'];
        $exist         = $this->params['is_exist'];
        $username_type = $this->check_username($username); // 检查用户名, 决定用下面哪那个函数
        switch ($username_type) {
        case 'phone':
            $this->get_code_by_username($username, 'phone', $exist); // 通过手机获取验证码
            break;
        case 'email':
            $this->get_code_by_username($username, 'email', $exist); // 通过邮箱获取验证码
            break;
        }
    }
}

G:\phpStudy\WWW\tp5\application\api\controller\Common.php

public function check_username($username) {
    /*********** 判断是否为邮箱  ***********/
    $is_email = Validate::is($username, 'email') ? 1 : 0;
    /*********** 判断是否为手机  ***********/
    $is_phone = preg_match('/^1[34578]\d{9}$/', $username) ? 4 : 2;
    /*********** 最终结果  ***********/
    $flag = $is_email + $is_phone;
    switch ($flag) {
    /*********** not phone not email  ***********/
    case 2:
        $this->return_msg(400, '邮箱或手机号不正确!');
        break;
    /*********** is email not phone  ***********/
    case 3:
        return 'email';
        break;
    /*********** is phone not email  ***********/
    case 4:
        return 'phone';
        break;
    }
}

通过用户名(手机/邮箱)获取验证码
G:\phpStudy\WWW\tp5\application\api\controller\Code.php

public function get_code_by_username($username, $type, $exist) {
    if ($type == 'phone') {
        $type_name = '手机';
    } else {
        $type_name = '邮箱';
    }
    /*********** 检测手机号/邮箱是否存在  ***********/
    $this->check_exist($username, $type, $exist);
    /*********** 检查验证码请求频率 30秒一次  ***********/
    if (session("?" . $username . '_last_send_time')) {
        if (time() - session($username . '_last_send_time') < 30) {
            $this->return_msg(400, $type_name . '验证码,每30秒只能发送一次!');
        }
    }
    /*********** 生成验证码  ***********/
    $code = $this->make_code(6);
    /*********** 使用session存储验证码, 方便比对, md5加密   ***********/
    $md5_code = md5($username . '_' . md5($code));
    session($username . '_code', $md5_code);
    /*********** 使用session存储验证码的发送时间  ***********/
    session($username . '_last_send_time', time());
    /*********** 发送验证码  ***********/
    if ($type == 'phone') {
        $this->send_code_to_phone($username, $code);
    } else {
        $this->send_code_to_email($username, $code);
    }
}

判断用户名(手机/邮箱)是否应该存在
G:\phpStudy\WWW\tp5\application\api\controller\Common.php

public function check_exist($value, $type, $exist) {
    $type_num  = $type == "phone" ? 2 : 4;
    $flag      = $type_num + $exist;
    $phone_res = db('user')->where('user_phone', $value)->find();
    $email_res = db('user')->where('user_email', $value)->find();
    switch ($flag) {
    /*********** 2+0 phone need no exist  ***********/
    case 2:
        if ($phone_res) {
            $this->return_msg(400, '此手机号已被占用!');
        }
        break;
    /*********** 2+1 phone need exist  ***********/
    case 3:
        if (!$phone_res) {
            $this->return_msg(400, '此手机号不存在!');
        }
        break;
    /*********** 4+0 email need no exist  ***********/
    case 4:
        if ($email_res) {
            $this->return_msg(400, '此邮箱已被占用!');
        }
        break;
    /*********** 4+1 email need  exist  ***********/
    case 5:
        if (!$email_res) {
            $this->return_msg(400, '此邮箱不存在!');
        }
        break;
    }
}

生成验证码
G:\phpStudy\WWW\tp5\application\api\controller\Code.php

public function make_code($num) {
    $max = pow(10, $num) - 1;
    $min = pow(10, $num - 1);
    return rand($min, $max);
}

通过邮箱发送验证码

去邮箱开启smtp
php需要开启php_openssl
现在phpmailer并把需要的文件添加进thinkphp5

  • G:\phpStudy\WWW\tp5\extend\phpmailer\phpmailer.php
namespace phpmailer;
use phpmailer\smtp;
class PHPMailer ......
class phpmailerException extends \Exception...... // 需要加\
  • G:\phpStudy\WWW\tp5\extend\phpmailer\smtp.php
namespace phpmailer;
class SMTP ......

G:\phpStudy\WWW\tp5\application\api\controller\Code.php

public function send_code_to_email($email, $code) {
    $toemail = $email;
    $mail    = new PHPMailer();
    $mail->isSMTP();
    $mail->CharSet    = 'utf8'; // 设置字符集
    $mail->Host       = 'smtp.126.com'; // smtp服务器
    $mail->SMTPAuth   = true;
    $mail->Username   = "xujunhao_api@126.com";
    $mail->Password   = "xujunhao890518"; // 自己设置的smtp密码, 与登录密码无关
    $mail->SMTPSecure = 'ssl';
    $mail->Port       = 994;
    $mail->setFrom('xujunhao_api@126.com', '接口测试');
    $mail->addAddress($toemail, 'test');
    $mail->addReplyTo('xujunhao_api@126.com', 'Reply');
    $mail->Subject = "您有新的验证码!"; // 邮件标题
    $mail->Body    = "这是一个测试邮件,您的验证码是$code,验证码的有效期为1分钟,本邮件请勿回复!"; // 邮件内容
    if (!$mail->send()) {
        $this->return_msg(400, $mail->ErrorInfo);
    } else {
        $this->return_msg(200, '验证码已经发送成功,请注意查收!');
    }
}

通过手机发送验证码 使用submail(赛迪云通信)

  1. 通过调用接口发送短信
  • 开启php_curl
  • 安装本地证书下载证书
  • G:\phpStudy\php\php-5.5.38\php.ini
[curl]
; A default value for the CURLOPT_CAINFO option. This is required to be an
; absolute path.
curl.cainfo = "G:\phpStudy\php\php-5.5.38\cacert.pem"
  • G:\phpStudy\WWW\tp5\application\api\controller\Code.php
public function send_code_to_phone($phone, $code) {
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, 'https://api.mysubmail.com/message/xsend');
    curl_setopt($curl, CURLOPT_HEADER, 0);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($curl, CURLOPT_POST, 1);
    $data = [
        'appid'   => '15180',
        'to'      => $phone,
        'project' => '9CTTG2',
        'vars'    => '{"code":' . $code . ',"time":"60"}',
        'signature'=>'76a9e82484c83345b7850395ceb818fb',
    ];
    curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
    $res = curl_exec($curl);
    curl_close($curl);
    $res = json_decode($res);
    if ($res->status != 'success') {
        $this->return_msg(400,$res->msg);
    }else{
        $this->return_msg(200,'手机验证码已发送, 每天发送5次, 请在一分钟内验证!');
    }
    dump($res->staus);die;
}
  1. 使用sdk发送验证码
  • 下载sdk, 把需要的文件加入thinkphp5
  • G:\phpStudy\WWW\tp5\extend\submail\message.php
namespace submail;
class message {......
  • G:\phpStudy\WWW\tp5\extend\submail\messagexsend.php
namespace submail;
use submail\message;
class MESSAGEXsend {

    protected $appid = '';

    protected $appkey = '';

    protected $sign_type = '';

    protected $To = array();

    protected $Addressbook = array();

    protected $Project = '';

    protected $Vars = array();

    function __construct() {
        $this->appid  = "15180";
        $this->appkey = "76a9e82484c83345b7850395ceb818fb";
        $this->sign_type = 'normal';
    }
  • G:\phpStudy\WWW\tp5\application\api\controller\Code.php
public function send_code_to_phone($phone, $code) {
    $submail = new MESSAGEXsend();
    $submail->SetTo($phone);
    $submail->SetProject('9CTTG2');
    $submail->AddVar('code', $code);
    $submail->AddVar('time', 60);
    $xsend = $submail->xsend();
    if ($xsend['status'] !== 'success') {
        $this->return_msg(400, $xsend['msg']);
    } else {
        $this->return_msg(200, '手机验证码已发送, 每天发送5次, 请在一分钟内验证!');
    }
}


第10节 用户注册

接口文档
配置路由
G:\phpStudy\WWW\tp5\application\route.php

// 用户注册
Route::post('user/register','user/register');

验证数据
G:\phpStudy\WWW\tp5\application\api\controller\Common.php

protected $rules = array(
    'User' => array(
        'register' => array(
            'user_name' => 'require',
            'user_pwd'  => 'require|length:32',
            'code'      => 'require|number|length:6',
        ),
    ),
);

关闭数据库字段检查
G:\phpStudy\WWW\tp5\application\database.php

// 是否严格检查字段是否存在
'fields_strict'   => false,

书写register函数
G:\phpStudy\WWW\tp5\application\api\controller\User.php

public function register() {
    /*********** 接收参数  ***********/
    $data = $this->params;
    /*********** 检查验证码  ***********/
    $this->check_code($data['user_name'], $data['code']);
    /*********** 检测用户名  ***********/
    $user_name_type = $this->check_username($data['user_name']);
    switch ($user_name_type) {
    case 'phone':
        $this->check_exist($data['user_name'], 'phone', 0);
        $data['user_phone'] = $data['user_name'];
        break;
    case 'email':
        $this->check_exist($data['user_name'], 'email', 0);
        $data['user_email'] = $data['user_name'];
        break;
    }
    /*********** 将用户信息写入数据库  ***********/
    unset($data['user_name']);
    $data['user_rtime'] = time(); // register time
    $res                = db('user')->insert($data);
    if (!$res) {
        $this->retrun_msg(400, '用户注册失败!');
    } else {
        $this->return_msg(200, '用户注册成功!');
    }
}

检查验证码
G:\phpStudy\WWW\tp5\application\api\controller\Common.php

public function check_code($user_name, $code) {
    /*********** 检测是否超时  ***********/
    $last_time = session($user_name . '_last_send_time');
    if (time() - $last_time > 60) {
        $this->return_msg(400, '验证超时,请在一分钟内验证!');
    }
    /*********** 检测验证码是否正确  ***********/
    $md5_code = md5($user_name . '_' . md5($code));
    if (session($user_name . "_code") !== $md5_code) {
        $this->return_msg(400, '验证码不正确!');
    }
    /*********** 不管正确与否,每个验证码只验证一次  ***********/
    session($user_name . '_code', null);
}

第11节 用户登录

接口文档
配置路由
G:\phpStudy\WWW\tp5\application\route.php

// 用户登录
Route::post('user/login','user/login');

验证数据
G:\phpStudy\WWW\tp5\application\api\controller\Common.php

protected $rules = array(
    'User' => array(
        'login'    => array(
            'user_name' => 'require',
            'user_pwd'  => 'require|length:32',
        ),
    ),
);

书写login函数
G:\phpStudy\WWW\tp5\application\api\controller\User.php

public function login() {
    /*********** 接收参数  ***********/
    $data = $this->params;
    /*********** 检测用户名  ***********/
    $user_name_type = $this->check_username($data['user_name']);
    switch ($user_name_type) {
    case 'phone':
        $this->check_exist($data['user_name'], 'phone', 1);
        $db_res = db('user')
            ->field('user_id,user_name,user_phone,user_email,user_rtime,user_pwd')
            ->where('user_phone', $data['user_name'])
            ->find();
        break;
    case 'email':
        $this->check_exist($data['user_name'], 'email', 1);
        $db_res = db('user')
            ->field('user_id,user_name,user_phone,user_email,user_rtime,user_pwd')
            ->where('user_email', $data['user_name'])
            ->find();
        break;
    }
    if ($db_res['user_pwd'] !== $data['user_pwd']) {
        $this->return_msg(400, '用户名或者密码不正确!');
    } else {
        unset($db_res['user_pwd']); // 密码永不返回
        $this->return_msg(200, '登录成功!', $db_res);
    }
}

第12节 用户上传头像

接口文档
配置路由
G:\phpStudy\WWW\tp5\application\route.php

// 用户上传你头像
Route::post('user/icon','user/upload_head_img');

验证数据
G:\phpStudy\WWW\tp5\application\api\controller\Common.php

protected $rules = array(
    'User' => array(
        'upload_head_img' => array(
            'user_id'   => 'require|number',
            'user_icon' => 'require|image|fileSize:2000000000|fileExt:jpg,png,bmp,jpeg',
        ),
    ),
);

修改参数过滤
G:\phpStudy\WWW\tp5\application\api\controller\Common.php

$this->params = $this->check_params($this->request->param(true));

编写upload_head_img函数
G:\phpStudy\WWW\tp5\application\api\controller\User.php

public function upload_head_img() {
    /*********** 接收参数  ***********/
    $data = $this->params;
    /*********** 上传文件,获得路径  ***********/
    $head_img_path = $this->upload_file($data['user_icon'], 'head_img');
    /*********** 存入数据库  ***********/
    $res = db('user')->where('user_id', $data['user_id'])->setField('user_icon', $head_img_path);
    if ($res) {
        $this->return_msg(200, '头像上传成功!', $head_img_path);
    } else {
        $this->return_msg(400, '上传头像失败!');
    }
}

编写upload_file函数
G:\phpStudy\WWW\tp5\application\api\controller\Common.php

public function upload_file($file, $type = '') {
    $info = $file->move(ROOT_PATH . 'public' . DS . 'uploads');
    if ($info) {
        $path = '/uploads/' . $info->getSaveName();
        /*********** 裁剪图片  ***********/
        if (!empty($type)) {
            $this->image_edit($path, $type);
        }
        return str_replace('\\', '/', $path);
    } else {
        $this->return_msg(400, $file->getError());
    }
}

编写image_edit函数
G:\phpStudy\WWW\tp5\application\api\controller\Common.php

public function image_edit($path, $type) {
    $image = Image::open(ROOT_PATH . 'public' . $path);
    switch ($type) {
    case 'head_img':
        $image->thumb(200, 200, Image::THUMB_CENTER)->save(ROOT_PATH . 'public' . $path);
        break;
    }
}

第13节 用户修改密码

接口文档
配置路由
G:\phpStudy\WWW\tp5\application\route.php

// 用户修改密码
Route::post('user/change_pwd','user/change_pwd');

验证数据
G:\phpStudy\WWW\tp5\application\api\controller\Common.php

protected $rules = array(
    'User' => array(
        'change_pwd'      => array(
            'user_name'    => 'require',
            'user_ini_pwd' => 'require|length:32',
            'user_pwd'     => 'require|length:32',
        ),
    ),
);

编写change_pwd函数
G:\phpStudy\WWW\tp5\application\api\controller\User.php

public function change_pwd() {
    /*********** 接收参数  ***********/
    $data = $this->params;
    /*********** 检查用户名并取出数据库中的密码  ***********/
    $user_name_type = $this->check_username($data['user_name']);
    switch ($user_name_type) {
    case 'phone':
        $this->check_exist($data['user_name'], 'phone', 1);
        $where['user_phone'] = $data['user_name'];
        break;
    case 'email':
        $this->check_exist($data['user_name'], 'email', 1);
        $where['user_email'] = $data['user_name'];
        break;
    }
    /*********** 判断原始密码是否正确  ***********/
    $db_ini_pwd = db('user')->where($where)->value('user_pwd');
    if ($db_ini_pwd !== $data['user_ini_pwd']) {
        $this->return_msg(400, '原密码错误!');
    }
    /*********** 把新的密码存入数据库  ***********/
    $res = db('user')->where($where)->setField('user_pwd', $data['user_pwd']);
    if ($res !== false) {
        $this->return_msg(200, '密码修改成功!');
    } else {
        $this->return_msg(400, '密码修改失败!');
    }
}

第14节 找回密码

接口文档
配置路由
G:\phpStudy\WWW\tp5\application\route.php

// 用户找回密码
Route::post('user/find_pwd','user/find_pwd');

验证数据
G:\phpStudy\WWW\tp5\application\api\controller\Common.php

protected $rules = array(
    'User' => array(
        'find_pwd'        => array(
            'user_name' => 'require',
            'user_pwd'  => 'require|length:32',
            'code'      => 'require|number|length:6',
        ),
    ),
);

书写find_pwd函数
G:\phpStudy\WWW\tp5\application\api\controller\User.php

public function find_pwd() {
    /*********** 接收参数  ***********/
    $data = $this->params;
    /*********** 检测验证码  ***********/
    $this->check_code($data['user_name'], $data['code']);
    /*********** 检测用户名  ***********/
    $user_name_type = $this->check_username($data['user_name']);
    switch ($user_name_type) {
    case 'phone':
        $this->check_exist($data['user_name'], 'phone', 1);
        $where['user_phone'] = $data['user_name'];
        break;
    case 'email':
        $this->check_exist($data['user_name'], 'email', 1);
        $where['user_email'] = $data['user_name'];
        break;
    }
    /*********** 修改数据库  ***********/
    $res = db('user')->where($where)->setField('user_pwd', $data['user_pwd']);
    if ($res !== false) {
        $this->return_msg(200, '密码修改成功!');
    } else {
        $this->return_msg(400, '密码修改失败!');
    }
}

第15节 用户手机号/邮箱绑定

接口文档
配置路由
G:\phpStudy\WWW\tp5\application\route.php

// 用户绑定手机号
Route::post('user/bind_phone','user/bind_phone');
// 用户绑定邮箱
Route::post('user/bind_email','user/bind_email');

验证数据
G:\phpStudy\WWW\tp5\application\api\controller\Common.php

'bind_phone'        => array(
    'user_id' => 'require|number',
    'phone'  => ['require','regex'=>'/^1[34578]\d{9}$/'],
    'code'      => 'require|number|length:6',
),
 
'bind_email'        => array(
    'user_id' => 'require|number',
    'email'  => 'require|email',
    'code'      => 'require|number|length:6',
),

书写bind_phone函数
G:\phpStudy\WWW\tp5\application\api\controller\User.php

public function bind_phone() {
    /*********** 接收参数  ***********/
    $data = $this->params;
    /*********** 检查验证码  ***********/
    $this->check_code($data['phone'], $data['code']);
    /*********** 修改数据库  ***********/
    $res = db('user')->where('user_id', $data['user_id'])->setField('user_phone', $data['phone']);
    if ($res !== false) {
        $this->return_msg(200, '手机号绑定成功!');
    } else {
        $this->return_msg(400, '手机号绑定失败!');
    }
}

书写bind_email函数
G:\phpStudy\WWW\tp5\application\api\controller\User.php

public function bind_email() {
    /*********** 接收参数  ***********/
    $data = $this->params;
    /*********** 检查验证码  ***********/
    $this->check_code($data['email'], $data['code']);
    /*********** 修改数据库  ***********/
    $res = db('user')->where('user_id', $data['user_id'])->setField('user_email', $data['email']);
    if ($res !== false) {
        $this->return_msg(200, '邮箱绑定成功!');
    } else {
        $this->return_msg(400, '邮箱绑定失败!');
    }
}

两个接口合成一个

  1. 配置路由

G:\phpStudy\WWW\tp5\application\route.php

// 用户绑定邮箱/手机
Route::post('user/bind_username','user/bind_username');
  1. 验证数据

G:\phpStudy\WWW\tp5\application\api\controller\Common.php

'bind_username'        => array(
    'user_id' => 'require|number',
    'user_name'  => 'require',
    'code'      => 'require|number|length:6',
),
  1. 书写bind_username函数

G:\phpStudy\WWW\tp5\application\api\controller\User.php

public function bind_username() {
    /*********** 接收参数  ***********/
    $data = $this->params;
    /*********** 检测验证码  ***********/
    $this->check_code($data['user_name'], $data['code']);
    /*********** 判断用户名  ***********/
    $user_name_type = $this->check_username($data['user_name']);
    switch ($user_name_type) {
    case 'phone':
        $type_text                 = '手机号';
        $update_data['user_phone'] = $data['user_name'];
        break;
    case 'email':
        $type_text                 = '邮箱';
        $update_data['user_email'] = $data['user_name'];
        break;
    }
    /*********** 修改数据库  ***********/
    $res = db('user')->where('user_id', $data['user_id'])->update($update_data);
    if ($res !== false) {
        $this->return_msg(200, $type_text . '绑定成功!');
    } else {
        $this->return_msg(400, $type_text . '绑定失败!');
    }
}

第16节 用户修改昵称

接口文档
配置路由
G:\phpStudy\WWW\tp5\application\route.php

// 用户修改昵称
Route::post('user/nickname','user/set_nickname');

验证数据
G:\phpStudy\WWW\tp5\application\api\controller\Common.php

'set_nickname'        => array(
    'user_id' => 'require|number',
    'user_nickname'  => 'require|chsDash',
),

编写set_nickname函数
G:\phpStudy\WWW\tp5\application\api\controller\User.php

public function set_nickname(){
    /*********** 接收参数  ***********/
    $data = $this->params;
    /*********** 检测昵称  ***********/
    $res = db('user')->where('user_nickname',$data['user_nickname'])->find();
    if ($res) {
        $this->return_msg(400,'该昵称已被占用!');
    }
    /*********** 写入数据库  ***********/
    $res = db('user')->where('user_id',$data['user_id'])->setField('user_nickname',$data['user_nickname']);
    if (!$res) {
        $this->return_msg(400,'修改昵称失败!');
    }else{
        $this->return_msg(200,'昵称修改成功!');
    }
}

第17节 新增文章

接口文档
新建api_article表

DROP TABLE IF EXISTS `api_article`;
CREATE TABLE `api_article` (
  `article_id` int(11) NOT NULL AUTO_INCREMENT,
  `article_title` varchar(255) NOT NULL,
  `article_uid` int(11) NOT NULL COMMENT 'user id',
  `article_content` text NOT NULL,
  `article_ctime` int(11) NOT NULL,
  PRIMARY KEY (`article_id`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

配置路由
G:\phpStudy\WWW\tp5\application\route.php

// 新增文章
Route::post('article','article/add_article');

验证数据
G:\phpStudy\WWW\tp5\application\api\controller\Common.php

'Article' => array(
    'add_article' => array(
        'article_uid' => 'require|number',
        'article_title' => 'require|chsDash',
    ),
),

编写add_article函数
G:\phpStudy\WWW\tp5\application\api\controller\Article.php

<?php
namespace app\api\controller;
class Article extends Common {
    public function add_article() {
        /*********** 接收参数  ***********/
        $data                  = $this->params;
        $data['article_ctime'] = time();
        /*********** 写入数据库  ***********/
        $res = db('article')->insertGetId($data);
        if ($res) {
            $this->return_msg(200, '新增文章成功!',$res);
        } else {
            $this->return_msg(400, '新增文章失败!');
        }
    }
}

参数安全html代码实体化

防止跨域脚本攻击

G:\phpStudy\WWW\tp5\application\config.php

// 默认全局过滤方法 用逗号分隔多个
'default_filter'         => 'htmlspecialchars',

第18节 查看文章列表

接口文档

配置路由
G:\phpStudy\WWW\tp5\application\route.php

// 查看文章列表
Route::get('articles/:time/:token/:user_id/[:num]/[:page]','article/article_list');

验证数据
G:\phpStudy\WWW\tp5\application\api\controller\Common.php

'Article' => array(
    'article_list' => array(
        'user_id' => 'require|number',
        'num' => 'number',
        'page' => 'number',
    ),
),

编写article_list函数
G:\phpStudy\WWW\tp5\application\api\controller\Article.php

<?php
namespace app\api\controller;
class Article extends Common {
    public function article_list() {
        /*********** 接收参数  ***********/
        $data = $this->params;
        if (!isset($data['num'])) {
            $data['num'] = 10;
        }
        if (!isset($data['page'])) {
            $data['page'] = 1;
        }
        /*********** 查询数据库  ***********/
        $where['article_uid'] = $data['user_id'];
        $where['article_isdel'] = 0;
        $count                = db('article')->where($where)->count();
        $page_num             = ceil($count / $data['num']);
        $field                = "article_id,article_ctime,article_title,user_nickname";
        $join                 = [['api_user u', 'u.user_id = a.article_uid']];
        $res                  = db('article')->alias('a')->field($field)->join($join)->where($where)->page($data['page'], $data['num'])->select();
        /*********** 判断并输出  ***********/
        if ($res === false) {
            $this->return_msg(400, '查询失败!');
        } elseif (empty($res)) {
            $this->return_msg(200, '暂无数据!');
        } else {
            $return_data['articles'] = $res;
            $return_data['page_num'] = $page_num;
            $this->return_msg(200, '查询成功!', $return_data);
        }
    }
}

第19节 查看单个文章

接口文档
配置路由
G:\phpStudy\WWW\tp5\application\route.php

// 获取单个文章信息
Route::get('article/:time/:token/:article_id','article/article_detail');

验证数据
G:\phpStudy\WWW\tp5\application\api\controller\Common.php

'Article' => array(
    'article_detail' => array(
        'article_id' => 'require|number',
    ),
),

编写article_detail函数
G:\phpStudy\WWW\tp5\application\api\controller\Article.php

<?php
namespace app\api\controller;
class Article extends Common {
    public function article_detail() {
        /*********** 接收参数  ***********/
        $data = $this->params;
        /*********** 查询数据库  ***********/
        $field                  = 'article_id,article_title,article_ctime,article_content,user_nickname';
        $where['article_id']    = $data['article_id'];
        $join                   = [['api_user u', 'u.user_id = a.article_uid']];
        $res                    = db('article')->alias('a')->join($join)->field($field)->where($where)->find();
        $res['article_content'] = htmlspecialchars_decode($res['article_content']);
        /*********** 判断结果并输出  ***********/
        if (!$res) {
            $this->return_msg(400, '查询失败!');
        } else {
            $this->return_msg(200, '查询成功!', $res);
        }
    }
}

第20节 修改文章

接口文档
配置路由
G:\phpStudy\WWW\tp5\application\route.php

// 修改/更新文章
Route::put('article','article/update_article');

验证数据
G:\phpStudy\WWW\tp5\application\api\controller\Common.php

'Article' => array(
    'update_article' => array(
        'article_id' => 'require|number',
        'article_title'=>'chsDash'
    ),
),

编写update_article函数
G:\phpStudy\WWW\tp5\application\api\controller\Article.php

<?php
namespace app\api\controller;
class Article extends Common {
    public function update_article() {
        /*********** 接收参数  ***********/
        $data = $this->params;
        /*********** 存入数据库  ***********/
        $res = db('article')->where('article_id', $data['article_id'])->update($data);
        if ($res !== false) {
            $this->return_msg(200, '修改文章成功!');
        } else {
            $this->return_msg(400, '修改文章失败!');
        }
    }
}

第21节 删除文章

接口文档
配置路由
G:\phpStudy\WWW\tp5\application\route.php

// 删除文章
Route::delete('article/:time/:token/:article_id','article/del_article');

验证数据
G:\phpStudy\WWW\tp5\application\api\controller\Common.php

'Article' => array(
    'del_article' => array(
        'article_id' => 'require|number',
    ),
),

为逻辑删除增加字段article_isdel

DROP TABLE IF EXISTS `api_article`;
CREATE TABLE `api_article` (
  `article_id` int(11) NOT NULL AUTO_INCREMENT,
  `article_title` varchar(255) NOT NULL,
  `article_uid` int(11) NOT NULL COMMENT 'user id',
  `article_content` text NOT NULL,
  `article_ctime` int(11) NOT NULL,
  `article_isdel` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否删除 1:yes 0:no',
  PRIMARY KEY (`article_id`)
) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

编写del_article函数
G:\phpStudy\WWW\tp5\application\api\controller\Article.php

<?php
namespace app\api\controller;
class Article extends Common {
    public function del_article(){
        /*********** 接收参数  ***********/
        $data = $this->params;
        /*********** 删除数据(逻辑删除)  ***********/
        $res = db('article')->where('article_id',$data['article_id'])->setField('article_isdel',1);
        /*********** 删除数据(物理删除)  ***********/
        // $res = db('article')->delete($data['article_id']);
        if ($res) {
            $this->return_msg(200,'删除文章成功');
        }else{
            $this->return_msg(400,'删除文章失败!');
        }
    }
}
posted @ 2018-08-25 14:35  HaimaBlog  阅读(3722)  评论(0编辑  收藏  举报