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')
})
// 匹配 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) => {})
// 匹配任何路径中含有 a 的路径
app.get('/a/', (req, res) => {})
// 匹配以 fly 结尾的路径
app.get('/.*fly$/', (req, res) => {})
第一种写法
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')
})
应用级中间件绑定到 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, () => {})
跟应用级中间件一样,只是绑定的对象为 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
// 放在路由注册最后,所有路径匹配不到时
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')
})
express.static 是 Express 唯一内置的中间件。基于 serve-static,负责在 Express 应用中提托管静态资源,每个应用可由多个静态目录
app.use(express.static('public'))
npm i cookie-parser
const CookieParser = require('cookie-parser')
app.use(CookieParser)
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')
})
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 格式

// 将 public 文件夹当作静态资源目录,访问的时候可以省略 /public
app.use(express.static('public'))
npm i ejs
<% %>,流程控制标签(写的是 if else,for)<%= %>,输出标签(原文输出HTML标签)<%- %>,输出标签(HTML标签会被浏览器解析)<%# %>,注释标签<%- include('header', { isShow: false }) %>,注释标签需要在应用中进行如下设置才能让 Express 渲染模板文件:
app.set('views', './views')app.set('view engine', 'ejs')// 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>
// 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>
说明文档
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 将具有文本域数据,如果存在的话
})
通过应用生成器工具 express-generator 可以快速创建一个应用的骨架。
npx express-generator
npm install -g express-generator
express
创建 ejs 模板项目
express --view=ejs myapp
cd myapp
npm install
npm start
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)
1)更轻量
2)Context 对象
koa 增加了一个 context 对象作为这次请求的上下文对象(在 koa2 中作为中间件的第一个参数传入)。同时 Context 上也挂载了 request 和 response 两个对象
3)异步流程控制
express 使用 callback 来处理异步,koa v1 使用 generator,koa v2 使用 async/await
npm i koa-router
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)
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
npm i koa-static
const static = require('koa-static')
// 将 public 文件夹设为静态资源目录
app.use(static(
path.join(__dirname, 'public')
))
// 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'
})
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)
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)
koa 提供了从上下文直接读取、写入 cookie 的方法
ctx.cookies.get(name, [options]) 读取上下文请求中的 cookiectx.cookies.set(name, value, [options]) 在上下文中写入 cookie1)下载 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')
}
})
说明文档
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` 文件数组的信息
})