原文首發於個人博客,歡迎點擊查看得到更好的閱讀體驗~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那就更好啦~