koa快速入门

4/28/2021 NodeKoaWeb

# 文章目录

# 一、koa 介绍及初体验

koa 官方的介绍:

  • koa:next generation web framework for node.js;
  • koa:node.js 的下一代 web 框架;

事实上,koa 是 express 同一个团队开发的一个新的 Web 框架:

  • 目前团队的核心开发者 TJ 的主要精力也在维护 Koa,express 已经交给团队维护了;
  • Koa 旨在为 Web 应用程序和 API 提供更小、更丰富和更强大的能力;
  • 相对于 express 具有更强的异步处理能力(后续我们再对比);
  • Koa 的核心代码只有 1600+行,是一个更加轻量级的框架,我们可以根据需要安装和使用中间件;
  • 事实上学习了 express 之后,学习 koa 的过程是很简单的

# 1.1 koa 初体验

下载:npm i koa \-D

App.js

// 内部导出的是一个类,叫 Application
// 太长了我们命名为 koa,但是是一个类 所以大写 即 Koa
const Koa = require("koa");

const app = new Koa();

app.use((ctx, next) => {
  console.log("中间件被执行");

  // Accept:String,Object,Array,Buffer
  ctx.response.body = "Hello World!";
  next();
});

app.listen(8000, () => {
  console.log("server start");
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

控制台输出:

server start
中间件被执行
1
2

客户端响应:

Hello World!
1

注:当没有设置任何中间件及返回值的时候,koa 默认帮我们返回Not Found,而express中请求会被挂起,即客户端一直处于请求状态

# 二、中间件

# 2.x ctx.request 和 ctx.req 的区别

ctx.request 是 context经过封装的请求对象,用起来更直观和简单;
ctx.req 是 context 提供的 node.js 原生 HTTP 请求对象,可以得到更多的内容。
同理 ctx.response 是 context经过封装的响应对象,
ctx.res 是 context 提供的 node.js 原生 HTTP 响应对象。

# 2.1 注册中间件

koa 通过创建的 app 对象,注册中间件只能通过 use 方法:

  • Koa 并没有提供 methods 的方式来注册中间件;
  • 也没有提供 path 中间件来匹配路径;

注册示例:

app.use((ctx, next) => {
  // Do something...
});
1
2
3

手动区分methodpath

const Koa = require("koa");

const app = new Koa();

// use 注册中间件,没有提供 指定方法、指定路径 的注册方式
app.use((ctx, next) => {
  console.log("中间件被执行");
  if (ctx.request.url === "/login") {
    if (ctx.request.method === "GET") {
      ctx.response.body = "Login Success~";
    }
  } else {
    ctx.response.body = "Ohter~";
  }
  next();
});

app.listen(8000, () => {
  console.log("server start");
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 2.2 koa-router

接下来演示一下怎么在koa中使用路由

安装:npm i \-D koa-router

App.js

const Koa = require("koa");

const app = new Koa();

const userRouter = require("./routers/users");

// 把返回的函数注册为中间件
app.use(userRouter.routes());
// 捕获不支持的请求,注册为中间件 客户端接收到:405 Method Not Allowed,501 Not Implemented等等
app.use(userRouter.allowedMethods());

app.listen(8000, () => {
  console.log("路由服务器启动成功~");
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14

routers/users.js

const Router = require("koa-router");
// prefix 前缀
const router = new Router({ prefix: "/users" });

router.get("/", (ctx, next) => {
  ctx.response.body = "Default";
});

router.post("/list", (ctx, next) => {
  ctx.response.body = "list";
});

module.exports = router;
1
2
3
4
5
6
7
8
9
10
11
12
13

# 2.3 参数解析 query & params

注意:原生并没有提供相应的功能模块用于解析params,这里我们使用路由功能里的解析。

const Koa = require("koa");
const Router = require("koa-router");

const app = new Koa();
const userRouter = new Router({ prefix: "/users" });

userRouter.get("/:path", (ctx, next) => {
  console.log("ctx.request.params: ", ctx.request.params);
  console.log("ctx.request.query: ", ctx.request.query);
  ctx.response.body = "Hello World!";
});

// app.use((ctx, next) => {
//   // url包含query信息,path只是单纯的请求路径
//   console.log('ctx.url: ', ctx.url);
//   console.log('ctx.path: ', ctx.path);

//   console.log('ctx.query: ', ctx.query);
//   console.log('ctx.params: ', ctx.params); // undifined

//   ctx.response.body = 'Hello World!'
//   next()
// })

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

app.listen(8000, () => {
  console.log("参数处理");
});
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

Postman 请求路径:http://localhost:8000/users/abc?name=why&age=18

控制台输出:

ctx.request.params:  { path: 'abc' }
ctx.request.query:  [Object: null prototype] { name: 'why', age: '18' }
1
2

# 2.4 参数解析 json & urlencoded

koa 默认没有内置解析jsonurlencoded的中间件,我们可以选择一个比较好用的第三方库来解析。

这里选用koa-bodyparser
安装:npm i \-D koa-bodyparser

先来看看没有使用的情况:

Postman 请求地址:
在这里插入图片描述

const Koa = require("koa");

const app = new Koa();

app.use((ctx, next) => {
  console.log("中间件被执行");
  console.log("ctx.request.body: ", ctx.request.body);

  ctx.response.body = "Hello World!";
  next();
});

app.listen(8000, () => {
  console.log("server start");
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

控制台输出:

ctx.request.body:  undefined
1

使用第三方库之后:

const Koa = require("koa");
const bodyParser = require("koa-bodyparser");

const app = new Koa();

// 使用第三方库帮助解析 json和urlencoded
app.use(bodyParser());

app.use((ctx, next) => {
  console.log("中间件被执行");
  console.log("ctx.request.body: ", ctx.request.body);

  ctx.response.body = "Hello World!";
  next();
});

app.listen(8000, () => {
  console.log("server start");
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

控制台输出:

ctx.request.body:  { name: '华为手机', price: 8888 }
1

# 2.5 参数解析 form-data

同上也没有内置解析的方式,选一个第三方库:koa-multer
注意: 该库解析到的 body 是放到 ctx.req.body 上的。

  • ctx.req 是原生 http 的请求体对象
  • ctx.request 是 koa 封装的请求体对象

使用示例:
Postman 请求:
在这里插入图片描述

const Koa = require("koa");
const multer = require("koa-multer");

const app = new Koa();
const upload = multer();

// 解析form-data 到 req.req.body 中
// any接受一切 none 只接受文本域
app.use(upload.any()); // 不建议在全局使用 接收一切的中间件

// 推荐在需要的地方使用
// Router.post('/login', upload.any(), (ctx, next) => {})

app.use((ctx, next) => {
  // ctx.req 是原生 http的 req
  console.log("ctx.req.body: ", ctx.req.body);

  // ctx.request 是 koa封装的一个对象

  ctx.response.body = "Hello World!";
  next();
});

app.listen(8000, () => {
  console.log("server start");
});
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

控制天输出:

ctx.req.body:  [Object: null prototype] { book: '三体', author: '刘慈欣' }
1

# 2.6 koa 实现文件上传

核心是 koa-multer 的 storage 配置项

const path = require("path");

const Koa = require("koa");
const Router = require("koa-router");
const multer = require("koa-multer");

const app = new Koa();
const uploadRouter = new Router({ prefix: "/upload" });
const storage = multer.diskStorage({
  // destination 目的地
  destination: (req, file, cb) => {
    cb(null, "./uploads/");
  },
  // 重命名
  filename: (req, file, cb) => {
    cb(null, Date.now() + path.extname(file.originalname));
  },
});
const upload = multer({
  // dest: './uploads/' //这样不能指定文件名和扩展名
  storage,
});

uploadRouter.post("/avatar", upload.single("avatar"), (ctx, next) => {
  // 这里能拿到上传的文件信息
  console.log("ctx.req.file: ", ctx.req.file);
  ctx.response.body = "上传头像成功!";
});

app.use(uploadRouter.routes());

app.listen(8000, () => {
  console.log("server start");
});
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

控制台输出:

ctx.req.file:  { fieldname: 'avatar',
  originalname: 'gril.jpg',
  encoding: '7bit',
  mimetype: 'image/jpeg',
  destination: './uploads/',
  filename: '1619442067658.jpg',
  path: 'uploads\\1619442067658.jpg',
  size: 79120
}
1
2
3
4
5
6
7
8
9

# 三、响应数据及代理

输出结果:body 将响应主体设置为以下之一:

  • string :字符串数据
  • Buffer :Buffer 数据
  • Stream :流数据
  • Object|| Array:对象或者数组
  • null :不输出任何内容

如果 response.status 尚未设置,Koa 会自动将状态设置为 200 或 204。

const Koa = require("koa");

const app = new Koa();

app.use((ctx, next) => {
  console.log("中间件被执行");

  // 设置状态码
  // ctx.response.status = 200
  // ctx.response.body = {}  也可以

  // 内部做了代理,可以简写
  ctx.status = 200;
  ctx.body = {
    name: "bookbook",
    age: 18,
    Home: "https://www.bookbook.cc",
  };
  next();
});

app.listen(8000, () => {
  console.log("server start");
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

为什么可以简写呢?这是因为koa帮我们做了一层代理,将属性代理到对应的属性上去。

下图proto其实就是 ctx,他会把下述列出的属性或方法代理到res.requestres.response上。
在这里插入图片描述

# 四、部署静态资源

没有内置中间件,使用官方库:koa-static

安装:npm i \-D koa-static

部署示例:

const Koa = require("koa");
const koaStatic = require("koa-static");

const app = new Koa();

app.use(koaStatic("./dist"));

app.listen(8000, () => {
  console.log("server start");
});
1
2
3
4
5
6
7
8
9
10

# 五、错误处理

const Koa = require("koa");

const app = new Koa();

app.use((ctx, next) => {
  const isLogin = false;
  if (!isLogin) {
    // 此处的 app 就是上边的app,但是不建议越级拿,通常都是 ctx.app获取
    ctx.app.emit("error", new Error("您还没有登录~"), ctx);
  }
});

app.on("error", (err, ctx) => {
  console.log("ctx: ", ctx);
  ctx.status = 401;
  ctx.body = err.message;
});

app.listen(8000, () => {
  console.log("server start");
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
最后更新于: 2021年9月15日星期三晚上10点10分
Faster Than Light
Andreas Waldetoft / Mia Stegmar