Koa学习
1、技术分析
- RESTFul理论
- Koa2
- Postman
- MongoDB
- jwt认证原理
- 数据库设计
- 线上部署
2、RESTFul Api
符合REST架构风格的Api
RESTFul
长什么样子
- 基本的URL,如:
https://api.github.com/users
- 标准的http方法,如:get、post、put、patch、delete
- 传输的媒体数据类型,如:json、xml
请求设计规范
响应设计规范
- 查询
- 分页
- 字段过滤
- 状态码
- 错误处理
安全
- HTTPS
- 鉴权
- 限流
3、Koa简
一句话简介
基于Node.js平台的下一代web开发框架
官网简介
- 由Express膜厚原班人马打造
- Web应用和Api开发领域
- 更小、更富有变现力、更健壮
- 利用async函数,丢弃回调函数
- 增强错误处理: try catch
- 没有捆绑任何中间件
4、项目搭建
初始化项目
bash
npm init
npm init
安装Koa
bash
npm i koa --save
npm i koa --save
编写Hello Word
javascript
const Koa = require('koa');
const app = new Koa();
app.use((ctx) => {
ctx.body = 'Hello .. Word!'
});
app.listen(3000);
const Koa = require('koa');
const app = new Koa();
app.use((ctx) => {
ctx.body = 'Hello .. Word!'
});
app.listen(3000);
自动重启
bash
npm i nodemon --save
npm i nodemon --save
json
{
"start": "nodemon index.js"
}
{
"start": "nodemon index.js"
}
5、中间件和洋葱模式
中间件
就是一个函数
javascript
/**
* 中间件1
*/
app.use(async (ctx, next) => {
// 有多个中间件的时候,先执行这个中间件,再去执行下个中间件,
// 此处应该执行next,next返回的是一个Promise
await next();
// 下一个中间件执行完成,这也就是所谓的洋葱模式
console.log(1);
ctx.body = 'Hello .. Word!'
});
/**
* 中间件1
*/
app.use(async (ctx, next) => {
// 有多个中间件的时候,先执行这个中间件,再去执行下个中间件,
// 此处应该执行next,next返回的是一个Promise
await next();
// 下一个中间件执行完成,这也就是所谓的洋葱模式
console.log(1);
ctx.body = 'Hello .. Word!'
});
洋葱模式
Reguest
Response
RegistryManager
StatusCodeRedirect
ErrorHandler
CacheMiddleware
SessionMiddleware
RoutesMiddleware
PylonsApp
6、路由
koa-router
javascript
const Router = require('koa-router');
// 实例化路由
const router = new Router({
// 加路由前缀
refix: '/users'
})
// 注册路由
app.use(router.routes());
const Router = require('koa-router');
// 实例化路由
const router = new Router({
// 加路由前缀
refix: '/users'
})
// 注册路由
app.use(router.routes());
路由前缀
javascript
// 添加路由前缀
router.prefix('/api/photo');
// 添加路由前缀
router.prefix('/api/photo');
多中间件
javascript
router.get('/users/:id',()=>{}, ()=>{}, ()=> {}, (ctx) => {
ctx.body = `用户id: ${ctx.params.id}`;
});
router.get('/users/:id',()=>{}, ()=>{}, ()=> {}, (ctx) => {
ctx.body = `用户id: ${ctx.params.id}`;
});
options请求方法
预检请求
- 检测服务器支持哪些请求方法
- 支持cors的预检请求
allowedMethods
响应options请求
javascript
// 注册路由
app.use(router.routes());
app.use(router.allowedMethods()); // 响应options请求
// 注册路由
app.use(router.routes());
app.use(router.allowedMethods()); // 响应options请求
7、控制器
类+类方法的方式组织控制器
断点调试
- 调试
- 监控变量
获取HTTP请求参数
Query String,如:name=name(可选)
ctx.query.name
Router Params,如:/users/:name(必选)
ctx.params.name
Body,如
ctx.request.body.name
Header,如Accept、Cookie
ctx.header.token
发送HTTP响应
发送Status,如:200
javascript
ctx.status = 200;
ctx.status = 200;
发送Body,如:
javascript
ctx.body = {
id: ctx.params.id,
name: '马化腾',
};
ctx.body = {
id: ctx.params.id,
name: '马化腾',
};
发送Header,如:Allow、Content-Type
javascript
ctx.set('Allow', 'GET, POST');
ctx.set('Allow', 'GET, POST');
处理业务逻辑
控制器处理业务逻辑
路由引入对应的操作
8、错误处理
处理软件或者程序出现的异常状况
运行时错误
500
逻辑错误
404,423,422
为什么错误处理?
- 防止程序挂掉
- 高速用户错误信息
- 便于开发者调试
koa错误处理
- 抛出错误
javascript
row(412, '先决条件失败');
row(412, '先决条件失败');
- 错误的处理
错误处理中间件
安装koa-json-error
bash
npm i koa-json-error --save-dev
npm i koa-json-error --save-dev
错误处理配置(环境区分)
生产环境禁用打印堆栈信息
javascript
// 错误处理
app.use(koaJsonError({
postFormat: (e, {
stack,
...rest
}) => {
return process.env. NODE_ENV === 'production' ? rest : {
stack,
...rest
}
}
}));
// 错误处理
app.use(koaJsonError({
postFormat: (e, {
stack,
...rest
}) => {
return process.env. NODE_ENV === 'production' ? rest : {
stack,
...rest
}
}
}));
统一环境变量的库
bash
"start": "cross-env NODE_ENV=production node index.js"
"start": "cross-env NODE_ENV=production node index.js"
koa参数校验
- 使用
javascript
ctx.verifyParams({
userName: {
type: 'string',
required: true,
},
userPwd: {
type: 'string',
required: true,
},
});
ctx.verifyParams({
userName: {
type: 'string',
required: true,
},
userPwd: {
type: 'string',
required: true,
},
});
目录结构
app
├── api # api 层
│ ├── cms # 关于 cms 的 api
│ │ ├── admin.js
│ │ ├── log.js
│ │ ├── test.js
│ │ └── user.js
│ └── v1 # 普通api
│ └── book.js
├── config # 配置文件目录
│ ├── code-message.js # 返回成功码错误码和返回信息配置
│ ├── log.js # 日志配置文件
│ ├── secure.js # 安全性配置文件
│ └── setting.js # 普通配置文件
├── dao # 数据库操作
│ ├── admin.js
│ ├── book.js
│ ├── log.js
│ └── user.js
├── extension # 扩展目录
├── lib # 其它类库
│ ├── db.js # Sequelize 实例
│ ├── exception.js # 异常类库
│ ├── type.js # 枚举
│ └── util.js # 助手函数
├── middleware # 中间件目录
│ ├── jwt.js
│ └── logger.js
├── model # 模型层
│ ├── book.js
│ ├── file.js
│ ├── group-permission.js
│ ├── group.js
│ ├── log.js
│ ├── permission.js
│ ├── user-group.js
│ └── user.js
├── plugin # 插件目录
├── validator # 校验层
│ ├── admin.js # 校验器模块
│ ├── book.js
│ ├── common.js
│ ├── log.js
│ └── user.js
├── app.js # 创建koa实例及应用扩展
└── starter.js # 程序的启动文件
app
├── api # api 层
│ ├── cms # 关于 cms 的 api
│ │ ├── admin.js
│ │ ├── log.js
│ │ ├── test.js
│ │ └── user.js
│ └── v1 # 普通api
│ └── book.js
├── config # 配置文件目录
│ ├── code-message.js # 返回成功码错误码和返回信息配置
│ ├── log.js # 日志配置文件
│ ├── secure.js # 安全性配置文件
│ └── setting.js # 普通配置文件
├── dao # 数据库操作
│ ├── admin.js
│ ├── book.js
│ ├── log.js
│ └── user.js
├── extension # 扩展目录
├── lib # 其它类库
│ ├── db.js # Sequelize 实例
│ ├── exception.js # 异常类库
│ ├── type.js # 枚举
│ └── util.js # 助手函数
├── middleware # 中间件目录
│ ├── jwt.js
│ └── logger.js
├── model # 模型层
│ ├── book.js
│ ├── file.js
│ ├── group-permission.js
│ ├── group.js
│ ├── log.js
│ ├── permission.js
│ ├── user-group.js
│ └── user.js
├── plugin # 插件目录
├── validator # 校验层
│ ├── admin.js # 校验器模块
│ ├── book.js
│ ├── common.js
│ ├── log.js
│ └── user.js
├── app.js # 创建koa实例及应用扩展
└── starter.js # 程序的启动文件
9、字段过滤
请求
http://127.0.0.1:3000/cms/users/id?fields=user_pwd;gender;headline;
http://127.0.0.1:3000/cms/users/id?fields=user_pwd;gender;headline;
接口过滤
js
// 查询特定
async function findOne(ctx) {
const {
fields = ''
} = ctx.query;
// 过滤字段的参数
let selectFields = fields.split(';').filter(field => field).map(field => ' +' + field).join('');
let res = await User.findOne({
_id: ctx.params.id
}).select(selectFields); // 字段过滤
if (!res) {
ctx.throw(404, '该用户不存在')
} else {
ctx.body = res;
}
}
// 查询特定
async function findOne(ctx) {
const {
fields = ''
} = ctx.query;
// 过滤字段的参数
let selectFields = fields.split(';').filter(field => field).map(field => ' +' + field).join('');
let res = await User.findOne({
_id: ctx.params.id
}).select(selectFields); // 字段过滤
if (!res) {
ctx.throw(404, '该用户不存在')
} else {
ctx.body = res;
}
}
10、上传图片的实现
功能点
- 基础功能
上传图片、生成图片链接
- 附加功能
限制上传图片的大小与类型、生成高中低三种分辨率的图片链接、生成CDN
技术方案
- 阿里云OSS等云服务(推荐生产使用)
- 直接上传到服务器(不推荐生产使用,适合学习使用)
操作步骤
安装koa-body,替换 koa-bodyparser
bash
npm i koa-bodyparser --save
npm i koa-bodyparser --save
设置图片上传目录
javascript
// 请求体解析
app.use(koaBody({
multipart: true, // 启动文件
formidable: {
uploadDir: path.join(__dirname, '/public/uploads'), // 文件存放目录
keepExtensions: true, // 保留扩展名
}
}));
const koaBody = require('koa-body');
// 请求体解析
app.use(koaBody({
multipart: true, // 启动文件
formidable: {
uploadDir: path.join(__dirname, '/public/uploads'), // 文件存放目录
keepExtensions: true, // 保留扩展名
}
}));
const koaBody = require('koa-body');
获取上传图片
upload (ctx) {
const file = ctx.request.files.file;
ctx.body = {
path: file.path
}
}
upload (ctx) {
const file = ctx.request.files.file;
ctx.body = {
path: file.path
}
}
生成上传图片链接
js
function upload (ctx) {
// 上传的文件
const file = ctx.request.files.file;
// 获取文件名
const basename = path.basename(file.path);
// 生成上传后的图片链接
const url = `${ctx.origin}/uploads/${basename}`;
ctx.body = {
url
}
}
function upload (ctx) {
// 上传的文件
const file = ctx.request.files.file;
// 获取文件名
const basename = path.basename(file.path);
// 生成上传后的图片链接
const url = `${ctx.origin}/uploads/${basename}`;
ctx.body = {
url
}
}
11、二级嵌套路由的设计
a、前缀
javascript
const router = new Router({ prefix: '/questions/:questionId/answers' });
const router = new Router({ prefix: '/questions/:questionId/answers' });
b、获取参数
javascript
ctx.params.questionId
ctx.params.questionId
12、koa应用的部署
登录服务器:
bash
ssh -p 22 root@106.12.182.39
ssh -p 22 root@106.12.182.39
安装Git,并下载项目
使用Git并clone代码至服务器,并安装Node
可以npm run start
测试下项目能不能跑起来
bash
npm install
npm install
但是,当我们退出服务器后,node的进程也就关了,服务也就没了,
所以我们需要 pm2 来守护进程
pm2使用
守护进程,后台运行
安装
npm install pm2 -g
npm install pm2 -g
安装完成后云服务切换到你项目所在路径
启动并监听服务:
pm2 start ./bin/www --watch
## --watch参数,koa2应用代码发生变化时,pm2会帮你重启服务。
pm2 start ./bin/www --watch
## --watch参数,koa2应用代码发生变化时,pm2会帮你重启服务。
启动之后,显示如下:说明启动成功!
pm2更多用法
pm2 reload <appname>
./bin/www // 开启
pm2 start <appname>
./bin/www // 开启
pm2 stop <appname>
./bin/www // 关闭
pm2 list //查看所用已启动项目:
pm2列表查看,pm2 list
输入 pm2 show 0
对应上图中的id = 0
关闭进程
www 对应list的name
bash
pm2 stop www
pm2 stop www
pm2杀死进程,pm2 kill
拓展
- 使用企业级Node.js框架 ——Egg.js
- 掌握多进程编程知识
- 学习使用日志和性能监控