Express&Koa学习文档

Express&Koa2

Express

Express的基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
const express = require("express");

// 1.创建express的服务器
const app = express();

/*
express提供给我们的中间件
在客户端发送post请求时,会将数据放到body中
客户端可以通过json的方式传递,也可以通过form表单的方式传递

- x-www-form-urlencoded
- 是一种在 HTTP 请求中用于编码表单数据的方式。它是默认的表单数据编码类型,
特别是在通过 HTML 表单提交数据时,如果表单的 enctype 属性没有被显式指定为其他值,那么就会使用 application/x-www-form-urlencoded 作为默认的编码方式

- JSON 编码 (application/json):
- 用于在请求和响应中传输 JSON 格式的数据。
请求头:Content-Type: application/json
请求体:包含 JSON 数据。

- 纯文本编码 (text/plain):
- 用于在请求和响应中传输纯文本数据。
请求头:Content-Type: text/plain
请求体:包含纯文本数据。

- XML 编码 (application/xml 或 text/xml):
- 用于在请求和响应中传输 XML 格式的数据。
请求头:Content-Type: application/xml 或 Content-Type: text/xml
请求体:包含 XML 数据。
*/
app.use(express.json()); // 解析客户端传递过来的json

// 解析传递过来的urlencoded的时候,默认使用的是node内置的querystring模块,但是这样会报警告
// 所以我们可以配置 {extended: true} 来解决这个问题,不在使用内置的querystring模块,而是使用qs第三方库
app.use(express.urlencoded({ extended: true })); // 解析客户端传递过来的urlencoded

app.post("/login", (req, res) => {
res.end("登陆成功,欢迎回来");
});

app.get("/home", (req, res) => {
res.end("home页面");
});

/*
通过use方法注册的中间件是最普通的中间件,无论是什么请求方式都可以匹配上
当匹配到第一个符合要求的中间件时,那么就会执行这个中间件
如果这个中间件中没有调用next方法,那么后面的中间件就不会再执行了
如果调用了next方法,那么就会继续向下匹配
*/
app.use((req, res, next) => {
console.log("ues中间件");
})

/*
多个中间件
*/
app.get("/user", (req, res, next) => {
console.log("中间件1");
next();
}, (req, res, next) => {
console.log("中间件2");
next();
}, (req, res, next) => {
console.log("中间件3");
}, (req, res, next) => {
console.log("中间件4");
});


// 2.启动服务器,监听端口
app.listen(9000, () => console.log("express服务器启动成功"));

中间件应用


请求日志记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const express = require("express");
const fs = require("node:fs");
const path = require("node:path");
// 引入第三方中间件
const morgan = require("morgan");

const app = express();

// 应用第三方中间件
const writeStream = fs.createWriteStream(path.resolve(__dirname, "./logs/access.log"));
app.use(morgan("combined", { stream: writeStream }));

app.post("/login", (req, res, next) => {
res.end("登陆成功");
});

app.listen(9000, () => console.log("express服务器启动成功"));

单个文件上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const express = require("express");
const path = require("node:path");
const multer = require("multer");

const app = express();

// 应用express编写的第三方中间件
const upload = multer({
// 放入指定文件夹
dest: path.resolve(__dirname, "./uploads")
});

// 上传单文件:single方法
app.post("/avatar", upload.single("avatar"), (req, res, next) => {
console.log(req.file);
res.end("文件上传成功");
});

app.listen(9000, () => console.log("express服务器启动成功"));

多个文件上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
const express = require("express");
const path = require("node:path");
const multer = require("multer");

const app = express();

// 应用express编写的第三方中间件
const upload = multer({
// dest: path.resolve(__dirname, "./uploads")
storage: multer.diskStorage({
// 文件路径
destination(req, file, cb) {
cb(null, path.resolve(__dirname, "./uploads"));
},
// 文件名字
filename(req, file, cb) {
cb(null, Date.now() + "_" + file.originalname);
}
})
});

// 上传单文件:single方法
app.post("/avatar", upload.single("avatar"), (req, res, next) => {
console.log(req.file);
res.end("文件上传成功");
});

// 上传多文件:array方法
app.post("/photos", upload.array("photos"), (req, res, next) => {
console.log(req.files);
res.end("多文件上传成功");
});

// 如果使用form-data的格式来传递数据,不推荐,form-data格式是用来传递文件的
// 如果使用form-data格式来传递数据,那么我们可以使用any方法
// const formdata = multer();

// app.post("/login",formdata.any(), (req, res, next) => {
// console.log(req.body);
// res.end("登陆成功");
// });

// // 解析params参数
// app.get("/user/:id", (req, res, next) => {
// const id = req.params.id;
// res.end("params参数:" + id);
// });

// // 解析queryString参数
// app.get("/home/list", (req, res, next) => {
// const query = req.query;
// res.end("query参数:" + JSON.stringify(query));
// });

app.listen(9000, () => console.log("express服务器启动成功"));

/*
客户端传递到服务器参数的方法常见的是5种:
方式一:通过get请求中的URL的params;
方式二:通过get请求中的URL的query;
方式三:通过post请求中的body的json格式(中间件中已经使用过);
方式四:通过post请求中的body的x-www-form-urlencoded格式(中间件使用过);
方式五:通过post请求中的form-data格式(中间件中使用过);
*/

服务器返回客户端数据方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const express = require("express");

const app = express();

app.post("/login", (req, res, next) => {
// 1.res.end 方法(比较少)
// res.end("登陆成功");

// 2.res.json方法(最多)
res.json({
code: 0,
message: "登陆成功"
});

// 3.res.status方法
// res.status(201);
});

app.listen(9000, () => console.log("express服务器启动成功"));

express中错误处理的方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
const express = require("express");

const app = express();

app.use(express.json());

app.post("/login", (req, res, next) => {
const { username, password } = req.body;

if (!username, !password) {
next(-1001);
} else if (username !== "ear" || password !== "123123") {
next(-1002);
} else {
res.json({
code: 0,
message: "欢迎回来",
token: "1231dsdfawer234"
})
}
});

// 错误处理的中间件
app.use((errCode, req, res, next) => {
const code = errCode;
let message = "未知错误";

switch (code) {
case -1001:
message = "没有输入用户名和密码";
break;
case -1002:
message = "输入用户名或密码错误";
break;
}

res.json({ code, message });
});

app.listen(9000, () => console.log("express服务器启动成功"));

express静态资源

1
2
3
4
5
6
7
const express = require("express");

const app = express();

app.use(express.static("./public"));

app.listen(9000, () => console.log("express服务器启动成功"));

Koa2

Koa的基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
const Koa = require("koa");

// 创建app对象
const app = new Koa();

// 注册中间件(middleware)
// koa的中间件有两个参数:ctx/next
app.use((ctx, next) => {
ctx.body = "哈哈哈";
})

// 启动服务器
app.listen(8000, () => console.log("koa服务器启动成功"));

Koa中ctx参数的解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const Koa = require("koa");

const app = new Koa();

app.use((ctx, next) => {
// 1.请求对象
console.log(ctx.request); // 请求对象:Koa封装的请求对象
console.log(ctx.req); // 请求对象:Node封装的请求对象

// 2.响应对象
console.log(ctx.response); // 响应对象:Koa封装的响应对象
console.log(ctx.res); // 响应对象:Node封装的响应对象

// 3.其他属性
console.log(ctx.query);
// console.log(ctx.params);
});

app.listen(8000, () => console.log("koa服务器启动成功"));

Koa中路由的使用过程

  • index.js
1
2
3
4
5
6
7
8
9
10
11
const Koa = require("koa");
const userRouter = require("./router/userRouter");

const app = new Koa();

// 让路由中的中间件生效
app.use(userRouter.routes());
// 提示访问路由错误,比如我现在代码中只有get和post请求,如果我访问put请求,如果加上下面这句话,就会提示错误,不加就not found(不好判断)
app.use(userRouter.allowedMethods());

app.listen(8000, () => console.log("koa服务器启动成功"));
  • userRouter.js
1
2
3
4
5
6
7
8
9
10
11
// pnpm add @koa/router
const KoaRouter = require("@koa/router");

// 1.创建路由对象 prefix:前缀
const userRouter = new KoaRouter({ prefix: "/user" });

// 2.在路由中注册中间件
userRouter.get("/", (ctx, next) => ctx.body = "users list data");
userRouter.post("/", (ctx, next) => ctx.body = "创建用户成功");

module.exports = userRouter;

Koa中参数解析方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
const Koa = require("koa");
const KoaRouter = require("@koa/router");
// pnpm add koa-bodyparser
const bodyParser = require("koa-bodyparser");
// pnpm add @koa/multer multer
const multer = require("@koa/multer");

const app = new Koa();

// 使用第三方中间件解析body数据,也可解析urlencoded
app.use(bodyParser());
// formdata解析
const formParser = multer();

const userRouter = new KoaRouter({ prefix: "/users" });

/*
1.get:params方式,例子:/:id
2.get:query方式,例子:?name=why&age=18
3.post:json方式,例子:{"name":"ear","age":18}
4.post:x-www-form-urlencoded
4.post:form-data
*/
// 1.get/param
userRouter.get("/:id", (ctx, next) => {
const id = ctx.params.id;
ctx.body = "user list data" + id;
});

// 2.get/query
userRouter.get("/", (ctx, next) => {
const query = ctx.query;
ctx.body = "user list data" + JSON.stringify(query);
});

// 3.post/json(最多)
userRouter.post("/", (ctx, next) => {
// 注意:不能从ctx.body中获取数据,这个第三方库是将解析后的body数据放到ctx.request.body中,没有放到ctx.req.body中
console.log(ctx.request.body);

// ctx.body用于向客户端返回数据
ctx.body = "用户json数据";
});

// 4.post/urlencoded
userRouter.post("/urlencode", (ctx, next) => {
// koa-bodyparser 既能够解析json数据,也能够解析urlencode数据
console.log(ctx.request.body);
ctx.body = "用户urlencode数据";
});

// 5.post/form-data
userRouter.post("/formdata", formParser.any(), (ctx, next) => {
console.log(ctx.request.body);
ctx.body = "用户formdata数据";
});

app.use(userRouter.routes());
app.use(userRouter.allowedMethods());

app.listen(7000, () => console.log("koa服务器启动成功"));

Koa中文件上传的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
const Koa = require("koa");
const KoaRouter = require("@koa/router");
const multer = require("@koa/multer");
const path = require("node:path");

const app = new Koa();

// const upload = multer({
// dest: path.resolve(__dirname, "./uploads")
// });

const upload = multer({
storage: multer.diskStorage({
destination(req, file, cb) {
cb(null, path.resolve(__dirname, "./uploads"));
},
filename(req, file, cb) {
cb(null, Date.now() + "_" + file.originalname);
}
})
});

const uploadRouter = new KoaRouter({ prefix: "/upload" });

uploadRouter.post("/avatar", upload.single("avatar"), (ctx, next) => {
console.log(ctx.request.file);
ctx.body = "文件上传成功";
});

// upload.array("photos")中的photos是前端传递过来的文件字段名
uploadRouter.post("/photos", upload.array("photos"), (ctx, next) => {
console.log(ctx.request.files);
ctx.body = "多文件上传成功";
});

app.use(uploadRouter.routes());
app.use(uploadRouter.allowedMethods());

app.listen(6000, () => console.log("koa服务器启动成功"));

Koa中部署静态资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const Koa = require("koa");
const KoaRouter = require("@koa/router");
const path = require("node:path");
// yarn add koa-static
const static = require("koa-static");
// const mount = require("koa-mount"); // 新增引入

const app = new Koa();

// 静态资源 是挂在到根目录下的 http://localhost:7000
app.use(static(path.resolve(__dirname, "./uploads")));
// 将静态资源目录挂载到 /aaa 路径下,http://localhost:7000/aaa
// app.use(mount("/aaa", static(path.resolve(__dirname, "./uploads"))));

const userRouter = new KoaRouter({ prefix: "/users" });

userRouter.get("/", (ctx, next) => {
ctx.body = "user list data";
});

app.use(userRouter.routes());
app.use(userRouter.allowedMethods());

app.listen(7000, () => console.log("koa服务器启动成功"));

Koa响应结果的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
const Koa = require("koa");
const KoaRouter = require("@koa/router");
const fs = require("node:fs");
const path = require("node:path");

const app = new Koa();

const userRouter = new KoaRouter({ prefix: "/users" });

userRouter.get("/", (ctx, next) => {
// 1.body的类型是string
// ctx.body = "user list data";

// 2.body的类型是Buffer
// ctx.body = Buffer.from("hello");

// 3.body的类型是Stream
// const readStream = fs.createReadStream(path.resolve(__dirname, "./uploads/1701924027354_潜水员戴夫.png"));
// // ctx.type 的作用是设置 HTTP 响应头中的 Content-Type,用于明确告知客户端返回数据的媒体类型,从而让浏览器或客户端正确解析响应内容
// ctx.type = "image/jpeg";
// ctx.body = readStream;

// 4.body的类型是数据(array/object)
// ctx.body = {
// code: 0,
// data: [
// { id: 111, name: "iphone", price: 1000 },
// { id: 112, name: "iphone", price: 1000 }
// ]
// }

// 5.body的值是null, 状态码会自动为204
// ctx.body = null;
});

app.use(userRouter.routes());
app.use(userRouter.allowedMethods());

app.listen(7000, () => console.log("koa服务器启动成功"));

Koa的错误处理方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
const Koa = require("koa");
const KoaRouter = require("@koa/router");

const app = new Koa();

const userRouter = new KoaRouter({ prefix: "/users" });

userRouter.get("/", (ctx, next) => {
const isAuth = false;
if (isAuth) {
ctx.body = "user list data";
} else {
// EventEmitter
ctx.app.emit("error", -1003, ctx);
}
});

app.on("error", (code, ctx) => {
const errCode = code;
let message = ""
switch (errCode) {
case -1001:
message = "账号或密码错误";
break;
case -1002:
message = "参数错误";
break;
case -1003:
message = "token错误";
break;
}

const body = {
code: errCode,
message
}

ctx.body = body;
});

app.use(userRouter.routes());
app.use(userRouter.allowedMethods());

app.listen(8000, () => console.log("koa服务器启动成功"));

Koa中间件


同步执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const Koa = require("koa");

const app = new Koa();

app.use((ctx, next) => {
console.log("1");
ctx.msg = "aaa";
next();
ctx.body = ctx.msg;
});

app.use((ctx, next) => {
console.log("2");
ctx.msg += "bbb";
next();
});

app.use((ctx, next) => {
console.log("3");
ctx.msg += "ccc";
});

app.listen(8000, () => console.log("koa服务器启动成功"));

异步执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const Koa = require("koa");
const axios = require("axios");

const app = new Koa();

app.use(async (ctx, next) => {
console.log("1");
ctx.msg = "aaa";
await next();
ctx.body = ctx.msg;
});

app.use(async (ctx, next) => {
console.log("2");
ctx.msg += "bbb";

// 如果执行的下一个中间件是一个异步函数,那么next默认不会等到中间件的结果,就会执行下一步操作
// 如果我们希望等待下一个异步函数的执行结果,那么需要在next函数前面加await
await next();
});

app.use(async (ctx, next) => {
console.log("3");

// 网络请求
const res = await axios.get("http://123.207.32.32:8000/home/multidata");

ctx.msg += res.data.data.banner.list[0].title;
});

app.listen(7000, () => console.log("koa服务器启动成功"));