专栏地址:https://www.nowcoder.com/tutorial/10032/index
1. 业务场景
我们以一个简化版的电商网站项目为例,其中涉及到6块功能,分别是登录、添加商品、商品列表、下订单、我的订单、订单详情,每个功能都有相应的权限限制,并且调用对应的接口来做相应的业务逻辑处理,完成后跳转到下一页面。具体页面/功能、权限、调用接口、动作及跳转的逻辑关系如下:
页面 | 权限 | 调用接口 | 动作及跳转 |
---|
1登录 | 所有用户 | login | 点击【登录】->商品页 |
2添加商品 | 管理员权限 | add_product | 无 |
3商品列表 | 所有用户 | get_product | 点击【下单】->订单 |
4订单 | 订单所属用户 | order | 点击【支付】->我的订单 |
–支付 | 订单所属用户 | mock | –点击【支付完成】->我的订单 |
5我的订单 | 订单所属用户 | user_orderid | 点击【订单区域】->订单详情 |
6订单详情 | 订单所属用户 | get_order | 无 |
项目的设计遵循前后端完全分离的设计模式,前端可以是Web、iOS、Android、小程序各种类型的客户端,但后端接口都是一致的,因此我们用代码实现上述6个接口功能,然后开发Web网站调用接口的实现电商下单购买支付的流程,并列举两个其中比较典型的安全问题,项目示例代码参见GitHub:https://github.com/8784285/order_web.git 。
2. 接口文档
1) 登录
- 接口url:http://127.0.0.1:5000/login
- 接口方法:POST
- 请求参数:
参数名 | 类型 | 是否必填 | 备注 | 示例 |
---|
username | string | 是 | 字母数字_.组合,长度2-20 | Mr.null |
password | string | 是 | MD5加密,长度32 | |
{
"username": "Mr.null",
"password": "dc483e80a7a0bd9ef71d8cf973673924"
}
参数名 | 类型 | 是否必需 | 备注 | 示例 |
---|
code | number | 是 | 4:成功;2:失败 | 4 |
msg | string | 是 | 登录成功/非法用户 | “登录成功” |
token | string | 是 | | |
{
"code": 4,
"msg": "登录成功",
"token": "eyJhbGciOiJIUzUxMiIsImlhdCI6MTU5MTgzNzQ0MSwiZXhwIjoxNTkxOTIzODQxfQ.eyJ1c2VybmFtZSI6Ik1yLm51bGwiLCJwYXNzd29yZCI6ImRjNDgzZTgwYTdhMGJkOWVmNzFkOGNmOTczNjczOTI0In0.fJVi3MZXmEz9lue0gZUO0m_lG2I-8endYsGOP4FNCGZ9wdGpKDb-yDNGj9XCrRXtoIz0AGIh-v-_oPWeOSYspQ"
}
2) 添加商品
- 接口url:http://127.0.0.1:5000/add_product
- 接口方法:POST
- 请求参数:
参数名 | 类型 | 是否必填 | 备注 | 示例 |
---|
name | string | 是 | 字母数字_.组合,长度2-20 | Mr.null |
price | number | 是 | 保留两位小数 | 25.50 |
count | count | 是 | >0的整数 | 100 |
{
"name":"Book",
"price":16.50,
"count":100
}
参数名 | 类型 | 是否必填 | 备注 | 示例 |
---|
Authorization | string | 是 | 请求授权头 | XDS 7.dVkI-SQWxjICYXD_TE54hZGIZwVud18LfXew-Ioorg0 |
参数名 | 类型 | 是否必需 | 备注 | 示例 |
---|
code | number | 是 | 2001:成功;2002:失败 | 2001 |
msg | string | 是 | 商品添加成功/商品添加失败 | “商品添加成功” |
{
"code": 2001,
"msg": "商品添加成功"
}
3)商品列表
- 接口url:http://127.0.0.1:5000/get_product
- 接口方法:GET
- 请求参数:
参数名 | 类型 | 是否必填 | 备注 | 示例 |
---|
name | string | 否 | 不传则默认返回20个商品 | Book |
http://127.0.0.1:5000/get_product?name=Book
参数名 | 类型 | 是否必填 | 备注 | 示例 |
---|
Authorization | string | 是 | 请求授权头 | XDS 7.dVkI-SQWxjICYXD_TE54hZGIZwVud18LfXew-Ioorg0 |
参数名 | 类型 | 是否必需 | 备注 | 示例 |
---|
code | number | 是 | 5:成功;6:失败 | 5 |
msg | string | 是 | 查询成功/未查询到数据 | “查询成功” |
data | object | 是 | 包含count、name、price的1到多个字典 | |
{
"code": 5,
"data": {
"count": 100,
"name": "Book",
"price": 12.5
},
"msg": "查询成功"
}
4)提交订单
- 接口url:http://127.0.0.1:5000/add_order
- 接口方法:POST
- 请求参数:
参数名 | 类型 | 是否必填 | 备注 | 示例 |
---|
sid | number | 是 | >0的整数 | 12 |
orderprice | number | 是 | 商品价格,保留两位小数 | 37.50 |
username | string | 是 | 登录用户的用户名 | Mr.null |
receiver | string | 是 | 收件人 | 张三 |
phone | string | 是 | 电话 | 13800138000 |
address | string | 是 | 收件地址 | 北京市海淀区长安街888号 |
{
"sid":11,
"orderprice":39.50,
"username":"admin",
"receiver":"张三",
"phone":"13800138000",
"address":"北京市海淀区长安街888号"
}
参数名 | 类型 | 是否必填 | 备注 | 示例 |
---|
Authorization | string | 是 | 请求授权头 | XDS 7.dVkI-SQWxjICYXD_TE54hZGIZwVud18LfXew-Ioorg0 |
参数名 | 类型 | 是否必需 | 备注 | 示例 |
---|
code | number | 是 | 2003:成功;2004:失败 | 2003 |
msg | string | 是 | 订单创建成功/订单创建失败 | “订单创建成功” |
{
"code": 2003,
"msg": "创建订单成功"
}
5)获取用户订单
- 接口url:http://127.0.0.1:5000/user_orderid
- 接口方法:GET
- 请求参数:
参数名 | 类型 | 是否必填 | 备注 | 示例 |
---|
username | string | 是 | 登录用户的用户名 | Mr.null |
http://192.168.160.94:5000/user_orderid?username=Mr.null
参数名 | 类型 | 是否必填 | 备注 | 示例 |
---|
Authorization | string | 是 | 请求授权头 | XDS 7.dVkI-SQWxjICYXD_TE54hZGIZwVud18LfXew-Ioorg0 |
参数名 | 类型 | 是否必需 | 备注 | 示例 |
---|
code | number | 是 | 2003:成功;2004:失败 | 2003 |
msg | string | 是 | 订单创建成功/订单创建失败 | “订单创建成功” |
orderid | object | 是 | 用户订单编号列表,最多20条 | |
{
"code": 5,
"msg": "查询成功",
"orderid": [
1,
2
]
}
6)订单详情
- 接口url:http://127.0.0.1:5000/get_order
- 接口方法:GET
- 请求参数:
参数名 | 类型 | 是否必填 | 备注 | 示例 |
---|
productid | number | 是 | >0的整数 | 12 |
http://192.168.160.94:5000/get_order?productid=12
参数名 | 类型 | 是否必填 | 备注 | 示例 |
---|
Authorization | string | 是 | 请求授权头 | XDS 7.dVkI-SQWxjICYXD_TE54hZGIZwVud18LfXew-Ioorg0 |
参数名 | 类型 | 是否必需 | 备注 | 示例 |
---|
code | number | 是 | 5:成功;6:失败 | 5 |
msg | string | 是 | 查询成功/未查询到结果 | “查询成功” |
orderid | object | 是 | 包含count、name、price等用户订单详情字典 | |
{
"code": 5,
"data": {
"count": 100,
"name": "Book",
"price": 12.5
},
"msg": "查询成功"
}
3. 数据库结构
电商项目数据库共包含4个表,分别是
- stock:商品信息表
- stock_order:订单信息表
- users:用户表
- userinfo:用户详情信息表
数据库表结构及初始化数据如下(sql详见GitHub:)
-- phpMyAdmin SQL Dump
-- version 4.9.3
-- https://www.phpmyadmin.net/
--
-- Host: localhost:3306
-- Generation Time: Aug 09, 2020 at 10:45 AM
-- Server version: 5.7.26
-- PHP Version: 7.2.22
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";
--
-- Database: `apitest`
--
-- --------------------------------------------------------
--
-- Table structure for table `stock`
--
CREATE TABLE `stock` (
`id` int(11) UNSIGNED NOT NULL,
`name` varchar(50) NOT NULL DEFAULT '' COMMENT '名称',
`price` float(4,2) NOT NULL DEFAULT '0.00' COMMENT '价格',
`count` int(11) NOT NULL COMMENT '库存',
`sale` int(11) NOT NULL COMMENT '已售',
`version` int(11) NOT NULL COMMENT '乐观锁,版本号'
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
--
-- Dumping data for table `stock`
--
INSERT INTO `stock` (`id`, `name`, `price`, `count`, `sale`, `version`) VALUES
(11, 'Book', 12.50, 0, 0, 1),
(12, 'Book', 12.50, 0, 0, 1),
(13, 'Book', 12.50, 100, 0, 1),
(14, 'Book', 12.50, 100, 0, 1),
(15, 'Book', 16.50, 100, 0, 1),
(16, 'Book', 16.50, 100, 0, 1),
(17, 'Book', 16.50, 100, 0, 1),
(18, 'Book', 16.50, 100, 0, 1),
(19, 'Book', 16.50, 100, 0, 1),
(20, 'Book', 16.50, 100, 0, 1),
(21, 'Book', 16.50, 98, 0, 1);
-- --------------------------------------------------------
--
-- Table structure for table `stock_order`
--
CREATE TABLE `stock_order` (
`id` int(11) UNSIGNED NOT NULL,
`sid` int(11) NOT NULL COMMENT '库存ID',
`orderprice` float(4,2) NOT NULL DEFAULT '0.00' COMMENT '订单价格',
`username` varchar(30) NOT NULL DEFAULT '' COMMENT '商品名称',
`receiver` varchar(255) DEFAULT NULL,
`phone` varchar(255) DEFAULT NULL,
`address` varchar(255) DEFAULT NULL,
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
`status` int(1) NOT NULL DEFAULT '0' COMMENT '0:创建成功,未支付;1:支付完成'
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
--
-- Dumping data for table `stock_order`
--
INSERT INTO `stock_order` (`id`, `sid`, `orderprice`, `username`, `receiver`, `phone`, `address`, `create_time`, `status`) VALUES
(146, 21, 16.50, 'Mr.null', 'Mr.null', '13800138001', '福建省厦门市思明区西山街道999号', '2020-08-02 13:04:11', 1);
-- --------------------------------------------------------
--
-- Table structure for table `userinfo`
--
CREATE TABLE `userinfo` (
`Id` int(11) NOT NULL,
`IDCard` varchar(18) NOT NULL DEFAULT '',
`Phone` varchar(15) NOT NULL DEFAULT '',
`Address` varchar(255) NOT NULL DEFAULT '',
`uid` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
--
-- Dumping data for table `userinfo`
--
INSERT INTO `userinfo` (`Id`, `IDCard`, `Phone`, `Address`, `uid`) VALUES
(1, '970827198804175919', '13800138000', '北京市海淀区北四环西路888号', 1),
(2, '720621199406056677', '13800138001', '福建省厦门市思明区西山街道999号', 2),
(3, '84152519840225088X', '13800138002', '江苏省苏州市昆山市黄浦江路898号', 3),
(4, '640403199008017641', '13800138003', '上海市浦东新区华夏路666号', 4);
-- --------------------------------------------------------
--
-- Table structure for table `users`
--
CREATE TABLE `users` (
`Id` int(11) NOT NULL,
`name` varchar(255) NOT NULL DEFAULT '',
`password` varchar(255) NOT NULL DEFAULT ''
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
--
-- Dumping data for table `users`
--
INSERT INTO `users` (`Id`, `name`, `password`) VALUES
(1, 'admin', '5690dddfa28ae085d23518a035707282'),
(2, 'Mr.null', 'dc483e80a7a0bd9ef71d8cf973673924'),
(3, 'ZhangSan', '5690dddfa28ae085d23518a035707282'),
(4, 'Lisi', 'dc483e80a7a0bd9ef71d8cf973673924'),
(5, 'a1', 'd41d8cd98f00b204e9800998ecf8427e'),
(6, 'a2', 'dc483e80a7a0bd9ef71d8cf973673924');
--
-- Indexes for dumped tables
--
--
-- Indexes for table `news`
--
ALTER TABLE `news`
ADD PRIMARY KEY (`Id`);
--
-- Indexes for table `stock`
--
ALTER TABLE `stock`
ADD PRIMARY KEY (`id`);
--
-- Indexes for table `stock_order`
--
ALTER TABLE `stock_order`
ADD PRIMARY KEY (`id`);
--
-- Indexes for table `userinfo`
--
ALTER TABLE `userinfo`
ADD PRIMARY KEY (`Id`);
--
-- Indexes for table `users`
--
ALTER TABLE `users`
ADD PRIMARY KEY (`Id`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `news`
--
ALTER TABLE `news`
MODIFY `Id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=264;
--
-- AUTO_INCREMENT for table `stock`
--
ALTER TABLE `stock`
MODIFY `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=22;
--
-- AUTO_INCREMENT for table `stock_order`
--
ALTER TABLE `stock_order`
MODIFY `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=147;
--
-- AUTO_INCREMENT for table `userinfo`
--
ALTER TABLE `userinfo`
MODIFY `Id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5;
--
-- AUTO_INCREMENT for table `users`
--
ALTER TABLE `users`
MODIFY `Id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=7;
4. 系统配置及服务启动
1)hosts配置
为了能抓到本地服务的数据包,我们先在hosts文件中增加如下内容:
127.0.0.1 order.web
127.0.0.1 order.api
2)服务启动
将项目代码clone到本地,然后执行下述操作:
打开命令窗口,命令行输入命令进入根目录下的api目录,运行如下命令,启动接口服务
python run.py
然后回到根目录,运行如下命令,启动网站
python main.py
浏览器地址栏输入"http://order.web:5001" 即可打开电商网站登录页面
我的微信如下:
关注公众号,更多精彩