1. 简介
Express是一个路由和中间件的Web框架,它本身的功能非常少,本质上是一系列中间件函数的调用。
(官网:
https://www.expressjs.com.cn/
)
2. 快速搭建
(1). 方案1-使用脚手架 【不推荐】
A. 安装脚手架:npm install -g express-generator
B. 创建项目:express express-demo
C. 安装依赖:npm install
D. 启动项目:node bin/www
(2). 方案2-全部收搭建
A. 初始化项目
【npm init -y】
B. 安装express
【npm install express】
3. 什么是中间件?
(1). 中间件的本质是传递给express的一个回调函数; 这个回调函数接受三个参数:
请求对象(request对象)、 响应对象(response对象)、next函数(在express中定义的用于执行下一个中间件的函数)
(2). 中间件可以执行以下任务:
A. 执行任何代码
B. 更改请求(request)和响应(response)对象;
C. 结束请求-响应周期(返回数据);
D. 调用栈中的下一个中间件;
注:如果当前中间件功能没有结束请求-响应周期(res.end),则必须调用next()将控制权传递给下一个中间件功能,否则,请求将被挂起.
const express = require("express");
const app = express();
// 第一个中间件
// 给app传入的的回调函数就是一个中间件
app.post("/checkLogin", (request, response, next) => {
console.log("我是匹配到的第一个中间件哦");
// 1. 可以执行相关业务
// 获取请求参数
// 执行DB业务
// 直接返回给客户端
// 2. 执行下一个中间件
next();
// 下面也是一个中间件
app.use((request, response, next) => {
console.log("我是第二个中间件");
response.end("登录成功了");
app.listen(9090, () => {
console.log("------------express服务器启动成功了------------");
二. 中间件详解
1. 中间件提供两种使用方式
(1). app.use() 可以匹配任何请求
(2). app.method 不同的方法匹配不同的请求 。 比如:app.post()、app.get()
2. 中间件的匹配规则如下
(1).当express接收到客户端发送的网络请求时, 从上往下在所有中间中依次进行匹配,当匹配到第一个符合要求的中间件时, 那么就会执行这个中间件
(2).如果当前中间件功能没有结束请求-响应周期(res.end),则必须调用next()将控制权传递给下一个中间件功能,否则,请求将被挂起
3. 普通的中间件
使用app.use,默认可以匹配任何路径和任何请求。(ps:可以再回调中通过 request.method 进一步区分请求类型,但通常不这么做,代码臃肿)
如下案例1: 两个中间件都会被请求到,且按顺序执行
const express = require("express");
const app = express();
app.listen(9090, () => {
console.log("------------express服务器启动成功了------------");
// 案例1. 普通中间件
app.use((request, response, next) => {
console.log("first middleware");
next();
app.use((request, response, next) => {
console.log("second middleware");
response.end("请求成功了");
4. 路径匹配
app.use中的第一个参数可以传入路径,这样就限制了该路径下的任何请求。
如下案例2:只要是路径 http://localhost:9090/checkLogin, 任何请求请求都可以访问
app.use("/checkLogin", (request, response, next) => {
console.log("checkLogin middleware");
response.end("登录成功");
5. 方法匹配+路径匹配
app.post 和 app.get ,第一个参数传入路径,则分别匹配改路径下的post请求和get请求
如下案例3:
Post请求 + 路径/checkLogin:可以匹配到第一个中间件
Get请求 + 路径/getUserInfo:可以匹配到第二个中间件
// 第一个中间件
app.post("/checkLogin", (request, response, next) => {
console.log("checkLogin middleware");
response.end("登录成功");
// 第二个中间件
app.get("/getUserInfo", (request, response, next) => {
console.log("getUserInfo middleware");
response.end("获取用户信息成功");
6. 多个中间件
可以在一个方法中传入多个回调函数,配合next方法,组成多个中间件
如下案例4
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("------结束了--------");
三. 参数解析
1. 通过post请求发送json格式
【对应postman中的 raw--json】 如图1
通过 app.use(express.json()) 中间件解析客户端发送过来的json请求,在回调函数中通过 res.body 直接可以获取这个对象。
const express = require("express");
const app = express();
app.listen(9090, () => {
console.log("------------express服务器启动成功了------------");
// 1. 通过post请求发送json格式
// 使用中间件
app.use(express.json());
app.use("/checkLogin", (req, res, next) => {
// 直接可以获取提交过来的json格式
console.log(req.body); //{ userAccount: 'admin', userPwd: '123456' }
// 返回结果
res.end(`请求成功`);
2. 通过post请求发送x-www-form-urlencoded格式
【对应postman中的x-www-form-urlencoded】 如图2
通过 app.use(express.urlencoded({ extended: true }))解析客户端传递来的urlencoded格式,默认使用的node内置querystring模块, { extended: true }: 不再使用内置的querystring, 而是使用qs第三方库
// 使用中间件
app.use(express.urlencoded({ extended: true }));
app.use("/checkLogin", (req, res, next) => {
// 直接可以获取提交过来的json格式
console.log(req.body); //{ userAccount: 'admin', userPwd: '123456' }
// 返回结果
res.end(`请求成功`);
3. 通过post请求发送form-data格式
【对应postman中的form-data】
(1). 通过使用multer中间件,需要先【npm install multer】
(2). 然后导入并使用中间件,就可以接受到客户端传递过来的form-data中的数据了。
// 编写中间件
const multer = require("multer");
const formdata = multer();
// 使用中间件
app.use("/checkLogin", formdata.any(), (req, res, next) => {
// 直接可以获取提交过来的json格式
console.log(req.body); //{ userAccount: 'admin', userPwd: '123456' }
// 返回结果
res.end(`请求成功`);
4. 通过get请求中的URL的query
形如:http://localhost:9090/checkLogin?userAccount=admin&userPwd=123456
在node中通过 request.query 直接就可以获取这个对象
app.get("/checkLogin", (request, response, next) => {
// 获取请求参数
console.log(request.query); //{ userAccount: 'admin', userPwd: '123456' }
// 返回结果
response.end(`请求成功`);
5. 通过get请求中的URL的params
形如:http://localhost:9090/checkLogin/admin/123456
在node中的匹配路径为:"/checkLogin/:userAccount/:userPwd",最终通过request.params获取
app.get("/checkLogin/:userAccount/:userPwd", (request, response, next) => {
// 获取请求参数
console.log(request.params); //{ userAccount: 'admin', userPwd: '123456' }
// 返回结果
response.end(`请求成功`);
四. 响应返回
1. end方法
类似于http中的response.end方法,用法是一致的
2. json方法 --(最常用)
json方法中可以传入很多的类型:object、array、string、boolean、number、null等,它们会被转换成json格式返回;
3. status方法
用于设置状态码;可以和json方法同时使用
注:这里是一个函数,而不是属性赋值;
const express = require("express");
const app = express();
app.listen(9090, () => {
console.log("------------express服务器启动成功了------------");
app.post("/checkLogin", (request, response, next) => {
// 1. end方法
// response.end("登录成功");
// 2. json方法
// response.json({
// status: "ok",
// msg: "登录成功",
// data: {
// userName: "ypf",
// useAge: 18,
// },
// });
// 3. status方法 (配合json同时使用)
response.status(401);
response.json({
status: "ok",
msg: "登录成功",
data: {
userName: "ypf",
useAge: 18,
五. 日志记录
1. 通过指令安装包 【npm install morgan】
2. 通过下面代码使用中间件
注:这里的日志路径文件夹必须事先存在,否则启动的时候报错
const express = require("express");
const fs = require("fs");
const app = express();
app.listen(9090, () => {
console.log("------------express服务器启动成功了------------");
// 使用中间件
const morgan = require("morgan");
const writeStream = fs.createWriteStream("./logs/myLog.txt");
app.use(morgan("combined", { stream: writeStream }));
app.post("/checkLogin", (request, response, next) => {
response.end("请求成功");
日志截图:
六. 文件上传
1. 说明
(1). 需要通过指令【npm install multer】 安装中间件multer
(2). 在multer中可以配置文件存放路径 和 文件命名规则
2. 单文件上传
通过 myUpload.single("myFile1")方法来配置,这里的myFile1为参数名
const express = require("express");
const app = express();
app.listen(9090, () => {
console.log("------------express服务器启动成功了------------");
// 引入multer中间件
const multer = require("multer");
let myUpload = multer({
// dest: "./uploads", //这种写法没有后缀名
storage: multer.diskStorage({
// 文件存放路径
destination(req, file, callback) {
callback(null, "./uploads");
// 文件命名规则
filename(req, file, callback) {
// 时间戳_文件名.后缀名 (file.originalname: 文件名.后缀名)
callback(null, Date.now() + "_" + file.originalname);
app.post("/singleUpload", myUpload.single("myFile1"), (req, res, next) => {
console.log(req.file);
res.end("文件上传成功~");
3. 多文件上传
通过 myUpload.array("myManyFile1")方法来配置,这里的myManyFile1为参数名
const express = require("express");
const app = express();
app.listen(9090, () => {
console.log("------------express服务器启动成功了------------");
// 引入multer中间件
const multer = require("multer");
// 2. 多文件上传
let myUpload = multer({
// dest: "./uploads", //这种写法没有后缀名
storage: multer.diskStorage({
destination(req, file, callback) {
callback(null, "./uploads");
filename(req, file, callback) {
// 时间戳_文件名.后缀名 (file.originalname: 文件名.后缀名)
callback(null, Date.now() + "_" + file.originalname);
app.post("/manyUpload", myUpload.array("myManyFile1"), (req, res, next) => {
console.log(req.files);
res.end("文件上传成功~");
七. 路由的使用
如果我们将所有的代码逻辑都写在app中,那么app会变得越来越复杂:
(1). 一方面完整的Web服务器包含非常多的处理逻辑;
(2). 另一方面有些处理逻辑其实是一个整体,我们应该将它们放在一起:比如对users相关的处理
获取用户列表;获取某一个用户信息; 创建一个新的用户;删除一个用户; 更新一个用户
const express = require("express");
const app = express();
app.listen(9090, () => {
console.log("------------express服务器启动成功了------------");
// 1. 原始写法--都写在这个app中
app.get("/users", (req, res, next) => {
res.json("用户列表数据");
app.get("/users/:id", (req, res, next) => {
const id = req.params.id;
res.json("某一个用户的数据:" + id);
app.post("/users", (req, res, next) => {
res.json("创建用户成功");
app.delete("/users/:id", (req, res, next) => {
const id = req.params.id;
res.json("删除某一个用户的数据:" + id);
app.patch("/users/:id", (req, res, next) => {
const id = req.params.id;
res.json("修改某一个用户的数据:" + id);
2. 什么是路由?
使用 express.Router来创建一个路由处理程序: 一个Router实例拥有完整的中间件和路由系统; 因此,它也被称为 迷你应用程序(mini-app)
3. 如何使用
(1). 创建路由 const router = express.Router();
(2). 使用路由构建各种请求匹配器 get post put等等
(3). 使用中间件 app.use("/users", router);
注:上述的每个请求前面都会拼上 /users
使用路由的写法(不抽文件)
// 2. 使用路由写法
// 2.1 创建路由
const router = express.Router();
// 2.2 各种请求
router.get("/", (req, res, next) => {
res.json("用户列表数据");
router.get("/:id", (req, res, next) => {
const id = req.params.id;
res.json("某一个用户的数据:" + id);
router.post("/", (req, res, next) => {
res.json("创建用户成功");
router.delete("/:id", (req, res, next) => {
const id = req.params.id;
res.json("删除某一个用户的数据:" + id);
router.patch("/:id", (req, res, next) => {
const id = req.params.id;
res.json("修改某一个用户的数据:" + id);
// 2.3 使用路由 (上述的每个请求前面都会拼上 /users)
app.use("/users", router);
通常将路由相关的单独抽离到一个文件中
const myRouter = require("./router/myRouter");
// 使用路由 (上述的每个请求前面都会拼上 /users)
app.use("/users", myRouter);
const express = require("express");
// 2.1 创建路由
const router = express.Router();
// 2.2 各种请求
router.get("/", (req, res, next) => {
res.json("用户列表数据");
router.get("/:id", (req, res, next) => {
const id = req.params.id;
res.json("某一个用户的数据:" + id);
router.post("/", (req, res, next) => {
res.json("创建用户成功");
router.delete("/:id", (req, res, next) => {
const id = req.params.id;
res.json("删除某一个用户的数据:" + id);
router.patch("/:id", (req, res, next) => {
const id = req.params.id;
res.json("修改某一个用户的数据:" + id);
// 3.将路由导出
module.exports = router;
八. 静态资源服务器
Node也可以作为静态资源服务器,并且express给我们提供了方便部署静态资源的方法;
比如可以直接部署一个静态项目;或者把一些图片放到静态资源里,便于访问
使用 express.static() 这个内置中间件来执行。
如下案例:
1. 把picture文件夹作为静态资源 如: http://localhost:9090/test.jpg
2. 在builder文件夹里部署一个静态资源项目 http://localhost:9090/index.html
特别注意:上面两个案例都是相当于直接根目录了,所以不要拼接 /picture 和 /build
const express = require("express");
const app = express();
app.listen(9090, () => {
console.log("------------express服务器启动成功了------------");
// 1. 直接将一个文件夹作为静态资源(才可以通过浏览器地址来访问这个资源)
app.use(express.static("./picture"));
// 2. 可以将一个完整的静态项目部署在这里
app.use(express.static("./build"));
// 编写中间件
app.post("/checkLogin", (req, res, next) => {
res.end("登录成功");
九. 异常处理方案
通俗的来说,就是单独抽离出来一个中间,通过next方法进入,统一来出来各种异常,自己定义异常码。
const express = require("express");
const app = express();
app.listen(9090, () => {
console.log("------------express服务器启动成功了------------");
app.use(express.json());
app.post("/checkLogin", (request, response, next) => {
let { userAccount, userPwd } = request.body;
if (!userAccount || !userPwd) {
next("error1");
} else if (userAccount !== "admin" || userPwd !== "123456") {
next("error2");
} else {
response.json({ status: "ok", msg: "登录成功" });
// 统一处理异常中间件
// 第一个参数为接收next传递过来的异常码
app.use((errCode, request, response, next) => {
let msg = "";
switch (errCode) {
case "error1":
msg = "用户名或密码为空";
break;
case "error2":
msg = "用户名或密码不正确";
break;
default:
msg = "登录失败";
break;
response.json({ status: "error", msg });
作 者 : Yaopengfei(姚鹏飞)
博客地址 : http://www.cnblogs.com/yaopengfei/
声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。