原文首发于个人博客,欢迎点击查看得到更好的阅读体验~javascript
一直都想接触下服务端的内容,之前看过python基础,可是由于在工做中基本上使用不到,因此基础看了几遍没有实际项目操做就忘记了,忘~记~了~html
朝秦暮楚后最终选择了基于Nodejs的中间件Koa2完成了一个简单的RESTful风格的API项目。前端
项目中主要完成了如下API接口:vue
项目根据
molunerfinn
的全栈开发实战:用Vue2+Koa1开发完整的先后端项目(更新Koa2)教程搭建,在此感谢做者分享。java
.
├── package.json // npm的依赖、项目信息文件
├── README.md // 说明文件
├── upload // 上传文件存储位置
├── index.js // Koa入口文件
├── server // vue-cli 生成,用于webpack监听、构建
│ ├── config // 配置文件夹
│ ├── controllers // controller-控制器
│ ├── models // model-模型
│ ├── routes // route-路由
│ ├── schema // schema-数据库表结构
└── └── utils // 实用工具
复制代码
如下依赖的版本都是本文所写的时候的版本:node
@koa/cors
v2.2.3 (跨域)python
koa
v2.7.0mysql
koa-body
v4.1.0 (解析post以及文件上传)webpack
koa-json
v2.0.2 (Koa中间件)nginx
koa-jwt
v3.6.0 (Koa token的中间件)
koa-logger
v3.2.1 (Koa日志中间件)
koa-router
v7.4.0 (Koa路由中间件)
mysql2
v1.6.5 (nodejs的mysql驱动)
sequelize
v5.12.1 (操做数据库的ORM)
为何要使用
mysql2
呢?由于使用mysql
时启动项目sequelize
会报Error: Please install mysql2 package manually
,提示安装mysql2
。
首先咱们得新建一个项目文件夹koa-demo
,而后用命令行进入该文件夹,执行npm init
建立项目描述文件package.json
E:\Project\WebStorm\Node\koa-demo> npm init
复制代码
命令行里会以交互的形式让你填一些项目的介绍信息,依次介绍以下:(不知道怎么填的直接回车、回车...)
而后就能够打开项目文件夹,能够看到自动生成的package.json
文件
接下来咱们先加入入口文件index.js
,写入基本内容:
// index.js
import Koa from 'koa'
import koaRouter from 'koa-router'
import json from 'koa-json'
import logger from 'koa-logger'
import koaBody from 'koa-body'
const app = new Koa()
const router = koaRouter()
app.use(koaBody())
app.use(json())
app.use(logger())
app.use(async (ctx,next) => {
await next()
})
app.on('error',(err,ctx) => {
console.log('server error', err)
})
app.listen(3000,()=>{
console.log('服务启动成功,端口:3000,地址:http://localhost:3000')
})
export default app
复制代码
而后在控制台输入node index.js
,发现报错,由于咱们使用了es6的import/export
为了支持import/export
咱们须要引入babel
转码器
npm install @babel/core @babel/node @babel/preset-env @babel/register --save-dev
复制代码
而后在根目录添加.babelrc
文件,写入
{
"presets": [
["@babel/preset-env", {
"targets": {
"node": "current"
}
}]
],
"plugins": []
}
复制代码
而后在package.json
中的scripts
写入
"scripts": {
"server": "npx babel-node index.js" //使用 npx 省去了输入 babel-node 完整路径的麻烦。
// or "server": "node_modules/.bin/babel-node index.js"
}
复制代码
而后运行npm run server
就能够了,能看到输出服务启动成功,端口:3000,地址:http://localhost:3000
,则说明咱们的Koa
已经启动成功了,并在3000端口监听。
用过vue-cli的都知道前端代码修改事后,会进行热启动,就免去了手动重启项目的麻烦,那么koa2
中如何进行热启动呢?nodemon
能够帮助咱们~
npm install nodemon
复制代码
而后在package.json
中的scripts
加入
"scripts": {
"server": "npx babel-node index.js",
"start": "nodemon index.js --exec babel-node"
}
复制代码
最后运行npm run start
就能够了
在IDE中断点调试须要配置一下
首先在打开菜单栏的Run
->Run Configurations
而后点击绿色+
号,选择Node.js
在右侧的Configuration
下面填入对应的参数
Node interpreter
:选择node的执行程序
Node parameters
: 填写参数,参数以下
E:\Project\WebStorm\Node\koa-demo\node_modules\nodemon\bin\nodemon --exec E:\Project\WebStorm\Node\koa-demo\node_modules\.bin\babel-node
能够理解为将
package.json
中的scripts
下的start
命令加入了完整路径
Working directory
:填写项目目录
JavaScript file
:入口文件
为了方便理解项目结构,我作一张图,咱们就按照这个顺序进行环境搭建。
去MySql
官网下载一个对应系统的安装程序,安装过程仍是比较简单的,这里就不详细描述了
对于初次接触MySql
的我,使用命令操做实在有此难为我了,还好以前装了个可视化的工具Navicat
(破解版),固然也有一些免费版的,例如:Windows上HediSQL,macOS上Sequel Pro。
好了,如今咱们先建立一个链接koa
koa
localhost
3306
root
123456
而后新建一个数据库demo
demo
utf8_general_ci
最后咱们建立两个表user
与resource
user表:
字段 | 类型 | 长度 | 主键 | 说明 |
---|---|---|---|---|
id | int(自增) | 255 | 1 | 用户ID |
nickname | varchar | 50 | 昵称 | |
username | varchar | 50 | 用户名 | |
password | varchar | 128 | 密码 | |
creationTime | datetime | 0 | 建立时间 | |
updateTime | datetime | 0 | 更新时间 |
resource表:
字段 | 类型 | 长度 | 主键 | 说明 |
---|---|---|---|---|
id | int(自增) | 125 | 1 | 资源ID |
name | varchar | 255 | 资源名称 | |
size | double | 0 | 资源大小 | |
measure | varchar | 255 | 分辨率 | |
thumbnail | varchar | 255 | 资源地址 | |
operator | varchar | 255 | 操做者 | |
time | datetime | 0 | 建立时间 |
咱们须要把数据库的表结构用sequelize-auto
导出来。
由此,咱们首先全局安装sequelize-auto
npm install -g sequelize-auto
复制代码
进入server
的目录,执行以下语句
sequelize-auto -o "./schema" -d demo -h localhost -u root -p 3306 -x 123456 -e mysql
复制代码
-o
参数后面的是输出的文件夹目录-d
参数后面的是数据库名-h
参数后面是数据库地址-u
参数后面是数据库用户名-p
参数后面是端口号-x
参数后面是数据库密码,这个要根据本身的数据库密码来!-e
参数后面指定数据库为mysql而后就会在schema
文件夹下自动生成两个文件:
// user.js
/* jshint indent: 2 */
module.exports = function(sequelize, DataTypes) {
return sequelize.define('user', {
id: {
type: DataTypes.INTEGER(255),
allowNull: false,
primaryKey: true,
autoIncrement: true
},
nickname: {
type: DataTypes.STRING(50),
allowNull: true
},
username: {
type: DataTypes.STRING(50),
allowNull: true
},
password: {
type: DataTypes.STRING(128),
allowNull: true
},
creationTime: {
type: DataTypes.DATE,
allowNull: true
},
updateTime: {
type: DataTypes.DATE,
allowNull: true
}
}, {
tableName: 'user'
});
};
复制代码
// resource.js
/* jshint indent: 2 */
module.exports = function(sequelize, DataTypes) {
return sequelize.define('resource', {
id: {
type: DataTypes.INTEGER(125),
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING(255),
allowNull: true
},
size: {
type: "DOUBLE",
allowNull: true
},
measure: {
type: DataTypes.STRING(255),
allowNull: true
},
thumbnailProxy: {
type: DataTypes.STRING(255),
allowNull: true
},
operator: {
type: DataTypes.STRING(255),
allowNull: true
},
time: {
type: DataTypes.DATE,
allowNull: true
}
}, {
tableName: 'resource'
});
};
复制代码
跟数据库打交道的时候咱们都须要一个好的操做数据库的工具,可以让咱们用比较简单的方法来对数据库进行增删改查。
我选用的是
Sequelize
,它支持多种关系型数据库(Sqlite
、MySQL
、Postgres
等),它的操做基本都能返回一个Promise
对象,这样在Koa里面咱们可以很方便地进行”同步”操做。更多关于
Sequelize
的用法,能够参考官方文档,以及这几篇文章——Sequelize中文API文档、Sequelize和MySQL对照、Sequelize快速入门
安装Sequelize
依赖
npm install --save sequelize
复制代码
而后在server
目录下的config
目录下咱们新建一个db.js
,用于初始化Sequelize
和数据库的链接。
// config/db.js
import Sequelize from 'sequelize' // 引入sequelize
// 使用url链接的形式进行链接,注意将root: 后面的XXXX改为本身数据库的密码
const demo = new Sequelize('mysql://root:123456@127.0.0.1/demo',{
define:{
// 取消Sequelzie自动给数据表加入时间戳(createdAt以及updatedAt)
timestamps: false
},
timezone: '+08:00' // 时差区,国内须要加入否则存储的时间会有时差
})
export default demo // 将demo暴露出接口方便Model调用
复制代码
接着咱们去models
文件夹里将数据库和表结构文件链接起来。在这个文件夹下新建一个user.js
的文件。
所谓增、删、改、查,那么咱们就先来写一个新增用户的操做。
// models/user.js
import demoDB from '../config/db'
// 引入user的表结构
const userModel = '../schema/user.js'
// 用sequelize的import方法引入表结构,实例化了User。
const User = demoDB.import(userModel)
// async 异步操做
const addUser = async (userInfo) => {
await User.create(userInfo)
}
module.exports = {
addUser
}
复制代码
如今咱们就须要写一写接收参数后的一些操做以及返回信息。
// controllers/user.js
import user from '../models/login'
const postUserInfo = async ctx => {
const data = ctx.request.body
const userAuth = await login.getUserByName(data.username)
if(userAuth === null){
let userInfo = {
username: data.username,
password: data.password,
nickname: data.nickname,
creationTime: dataTime(),
updateTime: dataTime()
}
user.addUser(userInfo)
ctx.body = {
code: '0000',
info: '新建成功!'
}
}else {
ctx.body = {
code: '9999',
info: '用户已存在!'
}
}
}
export default {
postUserInfo
}
复制代码
写完这个还不能直接请求,由于咱们尚未定义路由,请求通过Koa
找不到这个路径是没有反应的。
在routes
文件夹下写一个api.js
的文件。
// routes/api.js
import user from '../controllers/user'
import koaRouter from 'koa-router'
const router = koaRouter()
router.post('/user',user.postUserInfo)
export default router
复制代码
至此咱们已经接近完成咱们的第一个API了,还缺最后一步,将这个路由规则“挂载”到Koa上去。
为了节约篇幅,下面省略了一些代码,只写了上下文做为位置标记
// index.js
// ...
import logger from 'koa-logger'
import koaRouter from 'koa-router'
const router = koaRouter()
import api from './server/routes/api.js'
// ...
app.on('error',(err,ctx) => {
console.log('server error', err)
})
// 挂载到koa-router上,同时会让全部的auth的请求路径前面加上'/auth'的请求路径
router.use('/api', api.routes())
// 将路由规则挂载到Koa上。
app.use(router.routes())
app.listen(3000,()=>{
console.log('服务启动成功,端口:3000,地址:http://localhost:3000')
})
// ...
复制代码
打开你的控制台,输入node app.js
,一切运行正常没有报错的话,大功告成,咱们的第一个API已经构建完成!
接口在跟前端对接以前,咱们应该先进行一遍测试,防止出现问题。
在测试接口的工具上我想postman
的大名应该众所周知了,官网下载安装好后即可使用。
刚才实现的不过是一个简单的用户新增接口,可是咱们要实现一个完整的系统demo,还须要作一些工做。
剩下的API添加,基本上只须要在model
和controllers
写好方法,定好接口便可~
下面主要列举一下上传接口
以及分页查询接口
的一些知识点。
在项目根目录下咱们建立的有一个upload
文件夹,上传成功后的图片就存储到这里,那么这里的图片怎么经过连接访问呢?这就须要咱们搭建一个简易的静态资源服务器了。
这里提供两种方法:
koa-static中间件
npm install koa-static
复制代码
// index.js
//...
import statics from 'koa-static'
import path from 'path'
app.use(logger())
// 静态服务
app.use(statics(path.join(__dirname, './upload/')))
复制代码
咱们在upload目录下新建一个image
文件夹,用来放图片文件,而后挂载好后启动服务就可使用http://localhost:3000/image/test.jpg
查看图片了。
**注意:**将
upload
目录配置为静态资源,那么访问的时候不须要输入upload
,而是直接访问下级目录
Nginx搭建静态资源
这里使用Nginx
进行搭建,在官网下载稳定版本,解压后打开conf
文件夹下的nginx.conf
进行修改配置
server {
listen 3001;
server_name localhost;
location /upload/ {
root E:/Project/WebStorm/Node/koa-demo/;
autoindex on;
}
}
复制代码
而后运行nginx.exe
便可,而后咱们在upload
中添加一张图片,打开浏览器输入http://localhost:3001/upload/test.jpg
即可看见图片
在models
下建立一个resource.js
// models/resource.js
import demoDB from '../config/db'
const resModel = '../schema/resource'
const Res = demoDB.import(resModel)
const postResImage = async data => {
await Res.create(data)
}
export default {
postResImage
}
复制代码
主要是用来存储图片的一些数据到数据库
下面咱们在controllers
下建立一个resource.js
多文件须要遍历
ctx.request.files
,与文件一块儿传过来的参数在ctx.request.body
中获取
// controllers/resource.js
import fs from 'fs'
import path from 'path'
import res from '../models/resource'
import formatTime from '../utils/formatTime'
import _res from '../utils/response'
import probe from 'probe-image-size'
const imageUrl = 'http://localhost:3000/image/'
const imagePath = path.join(__dirname,'../../upload/image')
const videoUrl = 'http://localhost:3000/video/'
const videoPath = path.join(__dirname,'../../upload/video')
const uploadImage = async ctx => {
const file = ctx.request.files.file;
const reader = fs.createReadStream(file.path); // 建立可读流
// 获取图片流的尺寸,注意,这里不能直接使用reader,否则会致使图片损坏。
let measure = await probe(fs.createReadStream(file.path))
const upStream = fs.createWriteStream(`${imagePath}\\${file.name}`); // 建立可写流
const data = {
name : file.name,
size : (file.size / 1024 / 1024).toFixed(2),
measure : `${measure.width}*${measure.height}`,
thumbnailProxy : `${imageUrl}${file.name}`,
operator : 'admin',
time : formatTime()
}
await res.postRes(data)
if(!fs.existsSync(imagePath)){
fs.mkdir(imagePath,err => {
if(err) throw err
reader.pipe(upStream) // 可读流经过管道写入可写流
return ctx.body = _res.success('上传成功')
})
}else {
reader.pipe(upStream) // 可读流经过管道写入可写流
return ctx.body = _res.success('上传成功')
}
}
export default {
uploadImage
}
复制代码
接收到图片后,再想获取图片的尺寸须要
npm install probe-image-size
,刚开始的时候一直在image-size
上折腾,真是搞了很久,一直觉得是本身哪里用法不对。后来才发现是对流形式的不支持,就换到了probe-image-size
.
最后在routes
添加接口
// router/api.js
import koaRouter from 'koa-router'
const router = koaRouter()
import resource from '../controllers/resource'
router.post('/upload/image',resource.uploadImage)
export default router
复制代码
上传视频后在咱们通常都须要获取视频的缩略图,用来在前端列表中展现。
咱们使用FFmpeg
,一个领先的多媒体框架。
首先在官网下载对应平台的包。我这里使用的是windows,下载完成后将FFmpeg
解压到D:\ffmpeg
下。
并配置好系统的环境变量,添加D:\ffmpeg\bin
到系统变量。详细点击查看
若是设置环境变量无效的话,还能够手动设置ffpemg的位置。
FFMpeg.setFfmpegPath('D:/ffmpeg/bin/ffmpeg.exe')
而后在项目目录安装node的中间件fluent-ffmpeg
npm install fluent-ffmpeg
复制代码
而后就可使用了。
import FFMpeg from 'fluent-ffmpeg'
FFMpeg.setFfmpegPath('D:/ffmpeg/bin/ffmpeg.exe')
const screenshots = function(fileName){
FFMpeg('upload/video/'+ fileName)
.screenshots({
timemarks: ['0.5'],
filename: 'thumbnail-%b.png',
count: 1,
folder: 'upload/video'
})
}
export default {
screenshots
}
复制代码
其它操做请参考官方文档
这里的查询主要是查询上传的图片的信息,返回给前端进行列表展现。
因此接口与上传接口同在resource.js
文件中。
// models/resource.js
// ...省略
const getResImage = async (data) => {
const { pageNo, pageSize } = data
return await Res.findAndCountAll({
limit: parseInt(pageSize),
offset: (parseInt(pageNo)-1) * parseInt(pageSize),
}).then(result=>{
return result
})
}
export default {
getResImage,
postResImage
}
复制代码
// controllers/resource.js
// ...省略
const getResImageList = async ctx => {
const data = ctx.query
const list = await res.getResImage(data)
const _resData = {
pages:{
total: list.count
},
sources: list.rows
}
ctx.body = _res.success('成功',_resData)
}
export default {
uploadFile,
getResImageList
}
复制代码
// router/api.js
router.get('/resource/image',resource.getResImageList)
复制代码
至此,KOA2中实现RESTFul 风格的API就算完成了。
一对多的多表分页查询时会在子查询里中分页,可以使用
subQuery:true
。详见项目中节目查询的代码。
最后将本文的项目代码放至了Github,若是这个项目对你有帮助,但愿你们能够fork,给我提建议,若是再有时间,能够点个Star那就更好啦~