一、Express
1.1 安装与简单使用
npm i express
// send 方法支持返回代码块、数据(自动转为JSON)、文本
// 注意:send 只能调用一次,后面再调也不会执行
const espress = require('express')
const app = espress()
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.get('/login', (req, res) => {
res.send({
code: 0,
data: 'ok'
})
})
app.listen(3000, () => {
console.log('server start')
})
1.2 路由
1.2.1 字符串模式
// 匹配 acd 和 abcd
app.get('/ab?cd', (req, res) => {})
// 匹配 abe 和 abcde
app.get('/ab(cd)?e', (req, res) => {})
// 匹配 ab/***
app.get('/ab/:id', (req, res) => {})
// 匹配 abcd、abbcd、abbbcd等
app.get('/ab+cd', (req, res) => {})
// 匹配 abcd、abxcd、abiahiucd等
app.get('/ab*cd', (req, res) => {})
1.2.2 正则模式
// 匹配任何路径中含有 a 的路径
app.get('/a/', (req, res) => {})
// 匹配以 fly 结尾的路径
app.get('/.*fly$/', (req, res) => {})
1.2.3 路由守卫
第一种写法
const espress = require('express')
const app = espress()
// 路由守卫
app.get('/', (req, res, next) => {
// 登录鉴权,验证 token
const isLogin = Math.random() > 0.5
if (isLogin) {
next()
} else {
res.send('401 error')
}
}, (req, res) => {
res.send('欢迎来到 Home 页面')
})
app.listen(3000, () => {
console.log('server start')
})
第二种写法
const espress = require('express')
const app = espress()
function func1(req, res, next) {
// 登录鉴权,验证 token
const isLogin = Math.random() > 0.5
if (isLogin) {
next()
} else {
res.send('401 error')
}
}
function func2(req, res) {
res.send('欢迎来到 Home 页面')
}
app.get('/', [func1, func2])
app.listen(3000, () => {
console.log('server start')
})
1.3 中间件
1.3.1 应用级中间件
应用级中间件绑定到 app 对象
const espress = require('express')
const app = espress()
// 访问任何路径都会先走 func1 的逻辑
app.use(func1)
function func1(req, res, next) {
// 登录鉴权,验证 token
const isLogin = Math.random() > 0.5
if (isLogin) {
next()
} else {
res.send('401 error')
}
}
app.get('/home', (req, res) => {})
app.get('/login', (req, res) => {})
app.listen(3000, () => {})
1.3.2 路由级中间件
跟应用级中间件一样,只是绑定的对象为 express.Router()
// index.js
const espress = require('express')
const app = espress()
const HomeRouter = require('./HomeRouter')
// 以 /home 开头的路径匹配
app.use('/home', HomeRouter)
// HomeRouter.js
const espress = require('express')
const router = espress.Router()
// 访问 /home 走这里逻辑
router.get('/', (req, res) => {
console.log('/home')
res.send('home')
})
module.exports = router
1.3.3 错误中间件
// 放在路由注册最后,所有路径匹配不到时
const espress = require('express')
const app = espress()
app.get('/login', (req, res) => {
res.send({
code: 0,
data: 'ok'
})
})
// 上面的路径都匹配不到走这里
app.use((req, res) => {
// 抛出 404
res.status(404).send('404 Not Found')
})
app.listen(3000, () => {
console.log('server start')
})
1.3.4 内置中间件
express.static 是 Express 唯一内置的中间件。基于 serve-static,负责在 Express 应用中提托管静态资源,每个应用可由多个静态目录
app.use(express.static('public'))
1.3.5 第三方中间件
npm i cookie-parser
const CookieParser = require('cookie-parser')
app.use(CookieParser)
1.4 获取请求参数
1.4.1 get 请求
const express = require('express')
const app = express()
// 获取get请求参数
app.get('/', (req, res) => {
// http://localhost:3000/?username=joe
console.log(req.query) // { username: 'joe' }
res.send({
code: 0,
data: 'get success'
})
})
app.listen(3000, () => {
console.log('server start')
})
1.4.2 post 请求
const express = require('express')
const app = express()
// 使用内置中间件解析post参数 --- Content-Type: application/x-www-form-urlencoded 格式
app.use(express.urlencoded({ extended: true }))
// 使用内置中间件解析post参数 --- Content-Type: application/json 格式
app.use(express.json())
// 获取post请求参数
app.post('/login', (req, res) => {
console.log(req.body) // { username: 'joe', password: '123' }
res.send({
code: 0,
data: 'post success'
})
})
app.listen(3000, () => {
console.log('server start')
})
Content-Type: application/x-www-form-urlencoded 格式
Content-Type: application/json 格式
1.5 静态资源
// 将 public 文件夹当作静态资源目录,访问的时候可以省略 /public
app.use(express.static('public'))
1.6 ejs 模板
npm i ejs
<% %>
,流程控制标签(写的是 if else,for)<%= %>
,输出标签(原文输出HTML标签)<%- %>
,输出标签(HTML标签会被浏览器解析)<%# %>
,注释标签<%- include('header', { isShow: false }) %>
,导入公共模板
需要在应用中进行如下设置才能让 Express 渲染模板文件:
- views,放模板文件的目录,比如:
app.set('views', './views')
- view engine,模板引擎,比如:
app.set('view engine', 'ejs')
1.6.1 简单使用
// index.js
const express = require('express')
const app = express()
app.set('views', './views')
app.set('view engine', 'ejs')
app.get('/', (req, res) => {
// 这里会找 ./views 下的 login.ejs 渲染
res.render('login', { error: '' })
})
app.listen(3000, () => {
console.log('server start')
})
// views/login.ejs
<div>login 页面, <%= error %></div>
1.6.2 for 循环遍历数据
// index.js
app.get('/home', (req, res) => {
res.render('home', { list: [1, 2, 3] })
})
// views/home.ejs
<ul>
<% for(let i = 0; i < list.length; i++) { %>
<li><%= list[i] %> </li>
<% } %>
</ul>
1.7 文件上传
说明文档
Multer 是一个 node.js 中间件,用于处理 multipart/form-data 类型的表单数据,它主要用于上传文件
npm i multer
const express = require('express')
const multer = require('multer')
// 文件存放在 uploads 文件夹下
const upload = multer({ dest: 'uploads/' })
const app = express()
app.post('/profile', upload.single('avatar'), function (req, res, next) {
// req.file 是 `avatar` 文件的信息(前端传文件的字段)
const filename = `/uploads/${req.file.filename}` // 获取文件之后存数据库
// req.body 将具有文本域数据,如果存在的话
})
app.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {
// req.files 是 `photos` 文件数组的信息
// req.body 将具有文本域数据,如果存在的话
})
1.8 应用程序生成器
通过应用生成器工具 express-generator 可以快速创建一个应用的骨架。
npx express-generator
npm install -g express-generator
express
创建 ejs 模板项目
express --view=ejs myapp
cd myapp
npm install
npm start
1.9 Cookie 与 Session
1)下载 express-session
npm i express-session
2)引入 express-session
// 入口文件 app.js
var express = require('express')
var expressSession = require('express-session')
var MongoStore = require('connect-mongo')
var indexRouter = require('./routes/index')
var app = express()
// 注册 session 中间件,放在注册路由前面
app.use(expressSession({
name: 'user', // 下发给客户端的 cookie 名
secret: 'session', // 服务器生成的 session 签名,也就是密钥
resave: true, // 设置为 true 表示重新设置 session 对象后会重新计算过期时间
saveUninitialized: true, // 设置为 true 表示刚开始就会下发一个 cookie,但是需要与服务器通信后才有效
cookie: {
maxAge: 1000 * 60 * 60, // cookie 过期时间,这里为一小时
secure: false, // 为 true 时只有 https 协议才能通过 document.cookie 访问
},
// rolling: true, // 为 true 时表示超时前刷新,cookie 会重新计时;false 表示超时前刷新多少次,都按第一次刷新开始计时
store: MongoStore.create({
mongoUrl: 'mongodb://127.0.0.1:27017/express_session', // 新建一个数据库存放 session
ttl: 1000 * 60 * 60, // cookie 过期时间,跟 maxAge 一致
})
}))
app.use('/', indexRouter)
3)登录成功设置 session
// 登录接口,req.session 是自动匹配 cookie 生成的对象,不同浏览器生成 session 的对象不同
router.post('/login', function(req, res) {
const { name, password } = req.body
// 成功,req.session 添加一个字段记录已登录 例如添加 user 字段
req.session.user = true
})
4)设置中间件,session 过期校验
app.use((req, res, next) => {
// login 相关路由
if (req.url.includes('/login')) {
next()
return
}
// 校验是否有 cookie
if (req.session.user) {
// 重新设置 session 对象,刷新过期时间
req.session.user = Date.now()
next()
} else {
// 如果是接口就返回错误码,如果是路由就重定向 login 页面
req.url.includes('/add') ? res.status(401).send({ ok: -1 }) : res.redirect('/login')
}
})
1.10 Json Web Token(JWT)
npm i jsonwebtoken
const jwt = require('jsonwebtoken')
// 加密。第一个参数是加密数据 第二个参数是密钥 第三个参数设置过期时间
const token = jwt.sign({
name: 'admin'
// expiresIn 支持 1h 1d 写法
}, 'anydata', { expiresIn: '10s' })
// 解密。第一个参数是加密数据 第二个参数是密钥(需跟加密密钥一致)
const decoded = jwt.verify(token, 'anydata')
二、Koa
2.1 简单使用
npm i koa
const Koa = require('koa')
const app = new Koa()
// ctx 上下文
/*
ctx.req - Node 的 request 对象
ctx.res - Node 的 response 对象
ctx.request - koa 的 request 对象
ctx.response - koa 的 response 对象
*/
app.use((ctx, next) => {
// response.body 是往外输出的
// ctx.body - ctx.response.body 简写方式
ctx.response.body = 'hello world'
})
app.listen(3000)
2.2 Koa 对比 Express
1)更轻量
- koa 不提供内置的中间件
- koa 不提供路由,而是把路由这个库分离出来了(koa/router)
2)Context 对象
koa 增加了一个 context 对象作为这次请求的上下文对象(在 koa2 中作为中间件的第一个参数传入)。同时 Context 上也挂载了 request 和 response 两个对象
3)异步流程控制
express 使用 callback 来处理异步,koa v1 使用 generator,koa v2 使用 async/await
2.3 Koa 路由
npm i koa-router
2.3.1 注册路由
const Koa = require('koa')
const KoaRouter = require('koa-router')
const app = new Koa()
const router = new KoaRouter()
router.get('/', (ctx, next) => {
ctx.body = 'hello world!'
})
// allowedMethods 的作用是当请求方式出错时,响应头 Allow 会返回正确的请求方式
app.use(router.routes()).use(router.allowedMethods())
app.listen(3000)
2.3.2 分模块注册路由
1)入口文件
// 入口文件 app.js
const Koa = require('koa')
const router = require('./router/index')
const app = new Koa()
app.use(router.routes()).use(router.allowedMethods())
app.listen(3000)
2)路由入口文件
// router/index.js
const KoaRouter = require('koa-router')
const userRouter = require('./user')
const listRouter = require('./list')
const router = new KoaRouter()
// 统一加前缀
// router.prefix('/api')
// 注册路由级组件
router.use('/user', userRouter.routes(), userRouter.allowedMethods())
router.use('/list', listRouter.routes(), listRouter.allowedMethods())
// 重定向
router.redirect('/', '/user')
module.exports = router
3)路由模块文件
// router/user.js
const Koa = require('koa')
const KoaRouter = require('koa-router')
const app = new Koa()
const router = new KoaRouter()
router.get('/', (ctx, next) => {
ctx.body = 'user page'
})
app.use(router.routes()).use(router.allowedMethods())
module.exports = router
2.4 静态资源
npm i koa-static
const static = require('koa-static')
// 将 public 文件夹设为静态资源目录
app.use(static(
path.join(__dirname, 'public')
))
2.5 获取请求参数
2.5.1 get 请求
// http://localhost:3000/list?page=0&size=10
router.get('/', (ctx, next) => {
console.log(ctx.query) // { page: '0', size: '10' }
ctx.body = 'list page'
})
// http://localhost:3000/list/123
router.get('/:id', (ctx, next) => {
console.log(ctx.params) // { id: '123' }
ctx.body = 'list page'
})
2.5.2 post 请求
const Koa = require('koa')
const KoaRouter = require('koa-router')
const KoaBodyparser = require('koa-bodyparser')
const app = new Koa()
const router = new KoaRouter()
app.use(KoaBodyparser())
router.post('/list', (ctx, next) => {
console.log(ctx.request.body)
ctx.body = 'list'
})
app.use(router.routes()).use(router.allowedMethods())
app.listen(3000)
2.6 ejs 模板
npm i ejs koa-views
// 入口文件
const Koa = require('koa')
const path = require('path')
const KoaRouter = require('koa-router')
const KoaViews = require('koa-views')
const app = new Koa()
const router = new KoaRouter()
app.use(KoaBodyparser())
// 配置模板引擎
app.use(KoaViews(path.join(__dirname, 'views'), { extension: 'ejs' }))
// 异步读取文件,必须使用 async await
router.get('/', async (ctx, next) => {
// 渲染 views 文件夹下的 index.ejs
await ctx.render('index')
})
app.use(router.routes()).use(router.allowedMethods())
app.listen(3000)
2.7 cookie 与 session
2.7.1 cookie
koa 提供了从上下文直接读取、写入 cookie 的方法
ctx.cookies.get(name, [options])
读取上下文请求中的 cookiectx.cookies.set(name, value, [options])
在上下文中写入 cookie
2.7.2 session
1)下载 koa-session-minimal
npm i koa-session-minimal
2)引入 koa-session-minimal
const session = require('koa-session-minimal')
app.use(session({
key: 'sessionId',
// cookie 属性配置
cookie: {
maxAge: 1000 * 60
}
}))
3)登录成功设置 session
// 登录接口,ctx.session 是自动匹配 cookie 生成的对象,不同浏览器生成 session 的对象不同
router.post('/login', function(ctx, next) {
const { name, password } = ctx.request.body
// 成功,ctx.session 添加一个字段记录已登录 例如添加 user 字段
ctx.session.user = true
})
4)设置路由中间件,session 过期校验
app.use(async (ctx, next) => {
// login 相关路由
if (ctx.url.includes('/login')) {
await next()
return
}
// 校验是否有 cookie
if (ctx.session.user) {
// 重新设置 session 对象,刷新过期时间
ctx.session.user = Date.now()
await next()
} else {
// 重定向 login 页面
ctx.redirect('/login')
}
})
2.8 文件上传
说明文档
npm i @koa/multer multer
const Koa = require('koa')
const KoaRouter = require('koa-router')
const multer = require('@koa/multer')
// 文件存放在 uploads 文件夹下
const upload = multer({ dest: 'uploads/' })
const app = new Koa()
const router = new KoaRouter()
router.post('/profile', upload.single('avatar'), function (ctx, next) {
// ctx.file 是 `avatar` 文件的信息(前端传文件的字段)
const filename = `/uploads/${ctx.file.filename}` // 获取文件之后存数据库
})
router.post('/photos/upload', upload.array('photos', 12), function (ctx, next) {
// ctx.files 是 `photos` 文件数组的信息
})