一、express中间件匹配
中间件(Middleware),特指业务流程的中间处理环节,表现为回调函数的形式
中间件使用方式:
1.app/router.use( [path] , (req,res,next)=>{} )
- 匹配路径
2.app/router.methods( [path] , (req,res,next)=>{} )
-
匹配路径
-
匹配methods方法:get/post/delete/putt等
匹配流程:
- 从上往下在所有中间中依次进行匹配,当匹配到第一个符合要求的中间件时, 那么就会执行这个中间件
- 如果当前中间件功能没有结束请求-响应周期(res.end),则必须调用next()将控制权传递给下一个中间件功能,否则,请求将被挂起
const express = require('express')
const app = express()
// 匹配任意路径和任意方法
// next跳转到下一个中间件
app.use((req, res, next) => {
console.log(1);
next()
})
app.use((req, res, next) => {
console.log(2);
next()
})
// 匹配/api路径和请求方法get
// res.json、res.end、res.sendFile 响应内容并且结束中间件的传递
app.get("/api1", (req, res, next) => {
console.log(3);
res.json({
arr: [1, 2, 3]
})
})
app.get("/api2", (req, res, next) => {
console.log(4);
res.end("api2 end!!!")
})
app.listen(81, () => {
console.log('http://127.0.0.1:81');
})
一个方法可以传入多个回调函数
app.get(
"/getUserInfo",
(req, res, next) => {
console.log("match /home get middleware01");
next();
},
(req, res, next) => {
console.log("match /home get middleware02");
next();
},
(req, res, next) => {
console.log("match /home get middleware03");
next();
},
(req, res, next) => {
console.log("match /home get middleware04");
res.end("------结束了--------");
}
);
二、express常用中间件
2.1日志
记录所有的请求日志
//1.下载cors
npm i morgan
const morgan = require("morgan");
const fs = require("fs");
const app = express();
// 记录日志
const writeStream = fs.createWriteStream("./logs/expressServer.log");
app.use(morgan("combined", { stream: writeStream }));
2.2跨域
cors(Cross-Origin Resource Sharing)跨域资源贡献
方式一:服务器端使用cors插件
//1.下载cors
npm i cors
//2.导入中间件
require('cors')
//3.全局中间件
app.use(cors())
方式二:服务器端设置cors
- 开发环境跨域 webpack-server设置
- 生产环境跨域 在服务器端设置响应请求头
const express = require('express')
const app = express()
// 跨域
// 设置响应头
app.use((req, res, next) => {
// res.setHeader 一次设置一个
// res.header === res.set
res.header({
// 允许跨域的域名
'Access-Control-Allow-Origin': '*',
// 允许跨域的方法
'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE',
// 允许跨域的请求内容的类型
'Access-Control-Allow-Headers': 'Content-Type'
})
next()
})
// ... 其余中间件
app.listen(81, () => {
console.log('http://127.0.0.1:81');
})
2.3解析不同的请求类型
使用不同的中间件解析不同的参数类型(请求头中Content-Type):
2.3.1 query、params、json
2.3.2 json解析过程
1.将解析逻辑都写到同一个中间件中
2.将解析body抽取成中间件
const express = require("express");
const app = express();
// 解析body参数
app.use((req, res, next) => {
if (req.headers["content-type"] === "application/json") {
let str = "";
req.on("data", (chunk) => {
req.body = JSON.parse(chunk.toString());
});
req.on("end", () => {
next();
});
} else {
next();
}
});
app.post("/login", (req, res, next) => {
if (req.body.name === "ccc" && req.body.age === 999) {
res.end("登录成功");
} else {
res.end("账号或密码错误");
}
});
app.listen(81, () => {
console.log("http://127.0.0.1:81");
});
3.使用内置中间件
const express = require("express");
const app = express();
// 解析body参数
app.use(express.json());
app.post("/login", (req, res, next) => {
if (req.body.name === "ccc" && req.body.age === 999) {
res.end("登录成功");
} else {
res.end("账号或密码错误");
}
});
app.listen(81, () => {
console.log("http://127.0.0.1:81");
});
2.3.3 x-www-form-urlencoded
// 使用中间件
app.use(express.urlencoded({ extended: true }));
2.3.4 formdata
2.3.5 文件
单文件长传
const express = require("express");
const multer = require("multer");
const app = express();
// 文件上传
const uploader = multer({
storage: multer.diskStorage({
destination(req, file, callback) {
callback(null, "./uploads");
},
filename(req, file, callback) {
callback(null, Date.now() + "_" + file.originalname);
},
}),
});
app.post("/avatar", uploader.single("pic"), (req, res, next) => {
// 文件对象
console.log(req.file);
res.end("上传成功");
});
app.listen(81, () => {
console.log("http://127.0.0.1:81");
});
多文件上传
const express = require("express");
const multer = require("multer");
const app = express();
// 文件上传
const uploader = multer({
storage: multer.diskStorage({
destination(req, file, callback) {
callback(null, "./uploads");
},
filename(req, file, callback) {
callback(null, Date.now() + "_" + file.originalname);
},
}),
});
app.post("/avatar", uploader.array("photos"), (req, res, next) => {
// 文件数组对象
console.log(req.files);
res.end("上传多个成功");
});
app.listen(81, () => {
console.log("http://127.0.0.1:81");
});
2.3.6 总结
express.urlencoded({ extended: true })
- application/x-www-form-urlencoded form表单提交
express.json()
- application/json json字符串格式
express.raw()
- application/octet-stream 流类型
express.text()
- text/plain 纯文本类型
multer().any()
- multipart/form-data formdata表单
2.4 部署静态资源
node服务器上部署静态资源
const express = require('express')
const path = require('path')
const app = express()
// 静态资源目录
app.use(express.static(path.resolve(__dirname, './static')))
app.get("/", (req, res, next) => {
// res.sendFile 响应为文件类型
res.sendFile(path.resolve(__dirname, './static/test.html'))
})
app.listen(81, () => {
console.log('http://127.0.0.1:81');
})
- 有多个资源时,可多次使用static中间件
客户端访问资源
三、express路由(文件上传为例)
路由,有利于区分出模块
3.1 服务端:expressServer.js
- express的入口文件,负责处理全局中间件
- 错误和响应的统一处理
const express = require('express')
const userRouter = require('./userRouter')
const app = express()
// 跨域
// 设置响应头
app.use((req, res, next) => {
res.header({
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE',
'Access-Control-Allow-Headers': 'Content-Type'
})
next()
})
// user模块
app.use('/user', userRouter)
app.listen(81, () => {
console.log('http://127.0.0.1:81');
})
3.2 服务端:userRouter.js 模块
- 在user模块中写user相关的接口
const express = require('express')
const multer = require('multer')
const userRouter = express.Router()
// 1.配置multer
const uploader = multer({
storage: multer.diskStorage({
destination(req, file, callback) {
callback(null, './uploads')
},
filename(req, file, callback) {
callback(null, Date.now() + "_" + file.originalname)
}
})
})
// 2. 上传单个文件接口
// multer中间件解析 请求头类型为multipart/form-data(文件类型)
// /user/advatar
userRouter.post('/avatar', uploader.single('avatar'), (req, res) => {
console.log(req.file);
res.end('文件上传成功')
})
// 3. 上传多个文件接口
// multer中间件解析 请求头类型为multipart/form-data(文件类型)
// /user/files
userRouter.post('/files', uploader.array('files'), (req, res) => {
console.log(req.file);
res.end('文件上传成功')
})
module.exports = userRouter
3.3 客户端
<template>
<div>
<div>
<input type="file" @change="fileChange1">
<button @click="submitFile1">上传单个文件</button>
</div>
<div>
<input type="file" multiple @change="fileChange2">
<button @click="submitFile2">上传多个文件</button>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
data() {
return {
// 单个文件
file: null,
// 多个文件
fileArr: []
}
},
created() {
},
methods: {
fileChange1(e) {
// 获取文件信息
this.file = e.target.files[0]
},
submitFile1() {
// 文件放在请求体中
let _form = new FormData()
_form.append('avatar', this.file)
// 请求上传文件的接口
axios.post("http://127.0.0.1:81/user/avatar", _form).then(res => {
console.log(res);
})
},
fileChange2(e) {
// 获取文件信息
this.fileArr = [...e.target.files]
},
submitFile2() {
// 文件放在请求体中
let _form = new FormData()
this.fileArr.forEach(file => {
_form.append('files', file)
})
// 请求上传文件的接口
axios.post("http://127.0.0.1:81/user/files", _form).then(res => {
console.log(res);
})
},
}
}
</script>
<style lang="less"></style>
四、express错误和响应
const express = require("express");
const app = express();
// 跨域,设置响应头
app.use((req, res, next) => {
res.header({
// 允许跨域的域名
"Access-Control-Allow-Origin": "*",
// 允许跨域的方法
"Access-Control-Allow-Methods": "GET,POST,PUT,DELETE",
// 允许跨域的请求内容的类型
"Access-Control-Allow-Headers": "Content-Type",
});
next();
});
app.use(express.json());
app.post("/login", (req, res, next) => {
const { name, password } = req.body;
if (!name || !password) {
next(-1001);
} else if (name !== "ccc" || password !== "123") {
next(-1002);
} else {
res.json({
code: 0,
msg: "登录成功",
token: "xxxxxx",
});
}
});
// 统一处理错误的中间件
app.use((errCode, req, res, next) => {
let msg = "未知错误";
switch (errCode) {
case -1001:
msg = "用户或密码为空";
break;
case -1002:
msg = "用户或密码错误";
break;
default:
break;
}
res.json({ code: errCode, msg });
});
app.listen(81, "localhost", () => {
console.log("http://127.0.0.1:81");
});
五、express使用ejs模板引擎
npm i ejs
在express中定义相关变量
const express = require("express");
const path = require("path");
const app = express();
// 1.设置模板引擎
app.set("view engine", "ejs");
// 2.设置模板文件存放位置
app.set("views", path.resolve(__dirname, "./views"));
app.get("/home", (req, res) => {
let title = "ejs使用";
let arr = [1, 2, 3];
let flag = true;
res.render("homeView", {
title,
arr,
flag,
});
});
app.listen(81, "localhost", () => {
console.log("http://127.0.0.1:81");
});
在homeView.ejs模板文件中渲染出来
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><%= title %></title>
</head>
<body>
<h1><%= title %></h1>
<% if(flag) { %>
<ul>
<% for( let i = 0; i < arr.length; i++ ) { %>
<li><%= arr[i] %></li>
<% } %>
</ul>
<% } else{ %>
<span>123</span>
<% } %>
</body>
</html>
六、express-generator
npm install -g express-generator
创建项目
# -e ejs模板引擎
express -e [项目名称]
七、express执行同步、异步代码
线性模型
- 中间件从上往下依次执行
- 匹配到的中间件若没有调用next(),则一直等待响应结果
- 执行到res.end() 立即返回响应结果,结束中间件
同步代码
const express = require("express");
const app = express();
let msg = "";
app.use((req, res, next) => {
console.log("middleware1");
msg += "aaa";
next();
res.end(msg);
});
app.use((req, res, next) => {
console.log("middleware2");
msg += "bbb";
next();
});
app.use((req, res, next) => {
console.log("middleware3");
msg += "ccc";
});
app.listen(81, "localhost", () => {
console.log("http://127.0.0.1:81");
});
终端中打印结果:
http://127.0.0.1:81
middleware1
middleware2
middleware3
客户端中响应结果:
aaabbbccc
异步代码
const express = require("express");
const app = express();
function asyncFoo() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("bbb");
}, 1000);
});
}
let msg = "";
app.use((req, res, next) => {
console.log("middleware1");
msg += "aaa";
next();
res.end(msg);
});
app.use(async (req, res, next) => {
console.log("middleware2");
// 异步代码
const data = await asyncFoo();
msg += data;
next();
});
app.use((req, res, next) => {
console.log("middleware3");
msg += "ccc";
});
app.listen(81, "localhost", () => {
console.log("http://127.0.0.1:81");
});
终端中打印结果:
http://127.0.0.1:81
middleware1
middleware2
middleware3
客户端中响应结果:
aaa