第4节 为api项目搭建数据库
什么是数据库三大范式
- 每一列都是不可分割的原子数据项
- 实体的属性完全依赖于主关键字
- 任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖)
数据库中设计中的常见问题
- 字段混在了一起
- 数据表混在了一起
- 不会处理表关系
一对一 (学生姓名, 学号)
一对多 (老师, 学生)
多对多 (学生, 课程)
设计数据库的小技巧
- 一个对象, 一张表
- 一张表, 一个主键
- 表名中有
数据库名做前缀 - 字段名中有
表名做前缀 - 前缀后加
缩写 - 数据表关系处理
范式越高越好?
第5节 使用markdown书写接口文档
基本语法
- 六级标题
- 目录索引
- 加粗
- 斜体
- 删除线
- 引用
- 代码段
- 表格
- 行内代码
- 无序列表
- 有序列表
- 图片
- 超链接
第6节(判断数据库中是否有此用户)
用户登录举例
postwww.test.com/apiapi.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
配置主域名和二级域名
- 打开phpstudy
-
其他选项菜单==>站点域名管理
网站域名:
www.tp5.com
网站域名:G:\phpStudy\WWW\tp5\public
第二域名:api.tp5.com
网站端口:80
- 配置hosts(域名重定向)
127.0.0.1 www.tp5.com 127.0.0.1 api.tp5.com
使用tp5路由进行URL解析
- 为sublime安装新插件(方便操作侧边栏)
package control: install package
SideBarEnhancements
- 修改config.php(开启路由功能)
路径:
G:\phpStudy\WWW\tp5\application\config.php// 是否开启路由 \'url_route_on\' => true, // 域名部署 \'url_domain_deploy\' => true,
设置路由文件
- 方法一:路由里不写参数
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); } }访问下面地址都可以打开:
http://api.tp5.com/user/index?id=1&name=lisihttp://api.tp5.com/user?id=1&name=lisihttp://api.tp5.com/user/id/1/name/lisihttp://www.tp5.com/api/user/index?id=1&name=lisihttp://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
- 方法二:路由里写参数
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); } }下面这些访问拿不到参数
http://api.tp5.com/user/1/lisihttp://api.tp5.com/user?id=1&name=lisihttp://www.tp5.com/api/user/index?id=1&name=lisihttp://www.tp5.com/api/user?id=1&name=lisihttp://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节 接口安全
常见的安全问题以及解决方案
- 接口被大规模调用消耗系统资源,影响系统的正常访问,甚至系统瘫痪
解决方案: 获取 timestamp (时间戳), 设置接口失效时间
- 接口数据被黑客篡改(伪造请求)
解决方案: 对参数加密, 生成 token , 判断 token 是否正确
- 数据被黑客截取
解决方案: 使用 https , 用证书对数据进行加密, 即使数据被截取, 对黑客也没有意义
黑客可以获取数据, 但是无法获取数据的加密方法
我们api项目的安全设计
- time
时间戳, 用于判断请求是否超时, 设置为30秒
- token
其他参数加密而来, 保证数据不被篡改
- 敏感信息加密传输
接收加密过的用户密码, 用户密码永不返回
最好使用 https, 所有信息都会被加密
第9节 接口开发前的准备工作(参数过滤)
配置路由
- 开启路由功能
G:\phpStudy\WWW\tp5\application\config.php// 是否开启路由 \'url_route_on\' => true, // 域名部署 \'url_domain_deploy\' => true,
- 配置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;
}
验证timeG:\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, \'请求超时!\');
}
}
验证tokenG:\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;
验证码原理
- 生成及发送
- 点击获取手机码
- 发送手机号到后台
- 后台生成手机码
- 用session保存手机码及手机号
- 用短信发送平台的接口发送出去
- 验证
- 获取用户输入的手机码及手机号
- 取出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.phpnamespace phpmailer; use phpmailer\smtp; class PHPMailer ...... class phpmailerException extends \Exception...... // 需要加\
G:\phpStudy\WWW\tp5\extend\phpmailer\smtp.phpnamespace 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(赛迪云通信)
- 通过调用接口发送短信
- 开启
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.phppublic 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; }
- 使用sdk发送验证码
- 下载sdk, 把需要的文件加入thinkphp5
G:\phpStudy\WWW\tp5\extend\submail\message.phpnamespace submail; class message {......
G:\phpStudy\WWW\tp5\extend\submail\messagexsend.phpnamespace 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.phppublic 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, \'邮箱绑定失败!\');
}
}
两个接口合成一个
- 配置路由
G:\phpStudy\WWW\tp5\application\route.php// 用户绑定邮箱/手机 Route::post(\'user/bind_username\',\'user/bind_username\');
- 验证数据
G:\phpStudy\WWW\tp5\application\api\controller\Common.php\'bind_username\' => array( \'user_id\' => \'require|number\', \'user_name\' => \'require\', \'code\' => \'require|number|length:6\', ),
- 书写bind_username函数
G:\phpStudy\WWW\tp5\application\api\controller\User.phppublic 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,\'删除文章失败!\');
}
}
}