koa基本使用
npm i koa
const koa = require('koa')
const app = new koa()
// 中间件1
app.use((ctx,next)=>{
ctx.body = 'hi1'
next()
})
// 中间件2
app.use((ctx,next)=>{
ctx.body = 'hi2'
next()
})
// 中间件3
app.use((ctx,next)=>{
ctx.body = 'hi3'
next()
})
app.listen(8080, () => {
console.log('http://localhost:8080');
})
ctx上下文对象
- ctx.request和ctx.response 是Koa 封装的对象
- ctx.req和ctx.res 是Node.js 封装的对象,有HTTP请求对象的所有属性和方法
- next函数返回Promise对象,故可以使用同步的方式接收异步代码
跨域
const cors = require('koa2-cors');
app.use(cors())
路由
npm i @koa/router
const koa = require('koa')
const KoaRouter = require('@koa/router')
const app = new koa()
// 1.创建路由对象
const userRouter = new KoaRouter({ prefix: '/users' })
// 2.在路由中注册中间件
userRouter.get('/', (ctx, next) => {
ctx.body = "获取get"
})
// 3.使路由生效
app.use(userRouter.routes())
app.use(userRouter.allowedMethods())
app.listen(8080, () => {
console.log('http://localhost:8080');
})
allowedMethods的作用:用于判断某一个method是否支持
(1). 如果我们请求 get,那么是正常的请求,因为我们有实现get;
(2). 如果我们请求 put、delete、patch,那么就自动报错:Method Not Allowed,状态码:405;
(3). 如果我们请求 link、copy、lock,那么久自动报错:NotImplemented,状态码:501;
参数解析
query、params参数
const koa = require('koa')
const KoaRouter = require('@koa/router')
const app = new koa()
const userRouter = new KoaRouter({ prefix: '/users' })
// params参数
// http://localhost:8080/users/1
userRouter.get('/:id', (ctx, next) => {
ctx.body = ctx.params.id
})
// query参数
// http://localhost:8080/users?id=1&name=cjc
userRouter.get('/', (ctx, next) => {
ctx.body = ctx.query
})
app.use(userRouter.routes())
app.use(userRouter.allowedMethods())
app.listen(8080, () => {
console.log('http://localhost:8080');
})
json、x-www-form-urlencoded
npm i koa-bodyparser
const koa = require('koa')
const KoaRouter = require('@koa/router')
const bodyParser = require('koa-bodyparser')
const app = new koa()
const userRouter = new KoaRouter({ prefix: '/users' })
// 解析json、x-www-form-urlencoded的中间件
app.use(bodyParser())
// json、x-www-form-urlencoded参数
userRouter.post('/', (ctx, next) => {
ctx.body = ctx.request.body
})
// 使用路由
app.use(userRouter.routes())
app.use(userRouter.allowedMethods())
app.listen(8080, () => {
console.log('http://localhost:8080');
})
form-data
npm i @koa/multer multer
const koa = require('koa')
const KoaRouter = require('@koa/router')
const multer = require('@koa/multer')
const app = new koa()
const userRouter = new KoaRouter({ prefix: '/users' })
// multer() -> 返回multer实例
// formDataParse.any() -> 返回中间件
const formDataParse = multer()
// form-data参数
userRouter.post('/', formDataParse.any(), (ctx, next) => {
ctx.body = ctx.request.body
})
app.use(userRouter.routes())
app.use(userRouter.allowedMethods())
app.listen(8080, () => {
console.log('http://localhost:8080');
})
文件上传
npm i @koa/multer multer
const koa = require('koa')
const KoaRouter = require('@koa/router')
const multer = require('@koa/multer')
const app = new koa()
/**
* 文件上传
*/
const uploadRouter = new KoaRouter({ prefix: "/upload" });
const upload = multer({
storage: multer.diskStorage({
destination(req, file, cb) {
cb(null, "./imgs"); //指定存储文件夹为 imgs
},
filename(req, file, cb) {
// 解决中文名乱码的问题
file.originalname = Buffer.from(file.originalname, "latin1").toString(
"utf8"
);
cb(null, Date.now() + "_" + file.originalname);
},
}),
});
// 单文件上传
uploadRouter.post("/singleFile", upload.single("photo"), (ctx, next) => {
ctx.body = ctx.request.file;
});
// 多文件上传
uploadRouter.post("/multiFiles", upload.array("photos"), (ctx, next) => {
ctx.body = ctx.request.files;
});
app.use(uploadRouter.routes());
app.use(uploadRouter.allowedMethods());
app.listen(8080, () => {
console.log('http://localhost:8080');
})
总结
const koa = require("koa");
const KoaRouter = require("@koa/router");
const bodyParser = require("koa-bodyparser");
const multer = require("@koa/multer");
const static = require("koa-static");
const app = new koa();
/**
* 全局中间件
*/
// 1.跨域
// 2.普通全局中间件
app.use(bodyParser());
/**
* 路由中间件,参数解析
*/
const userRouter = new KoaRouter({ prefix: "/users" });
// query、params
userRouter.get("/:id", (ctx, next) => {
const { params, query } = ctx;
ctx.body = {
params: params,
query: query,
};
});
// json、x-www-form-urlencoded
userRouter.post("/addUser", (ctx, next) => {
ctx.body = ctx.request.body;
console.log(ctx.req.body, ctx.request.body);
});
// form-data
const formDataParse = multer();
userRouter.post("/addUserByForm", formDataParse.any(), (ctx, next) => {
ctx.body = ctx.request.body;
});
app.use(userRouter.routes());
app.use(userRouter.allowedMethods());
/**
* 文件上传
*/
const uploadRouter = new KoaRouter({ prefix: "/upload" });
const upload = multer({
storage: multer.diskStorage({
destination(req, file, cb) {
cb(null, "./imgs"); //指定存储文件夹为 imgs
},
filename(req, file, cb) {
// 解决中文名乱码的问题
file.originalname = Buffer.from(file.originalname, "latin1").toString(
"utf8"
);
cb(null, Date.now() + "_" + file.originalname);
},
}),
});
// 单文件上传
uploadRouter.post("/singleFile", upload.single("photo"), (ctx, next) => {
ctx.body = ctx.request.file;
});
// 多文件上传
uploadRouter.post("/multiFiles", upload.array("photos"), (ctx, next) => {
ctx.body = ctx.request.files;
});
app.use(uploadRouter.routes());
app.use(uploadRouter.allowedMethods());
// 静态服务器
app.use(static("./imgs"));
app.listen(8080, () => {
console.log("http://localhost:8080");
});
静态资源
npm i koa-static
const koa = require('koa')
const static = require('koa-static')
const app = new koa()
// 静态服务器
// http://localhost:8080/[imgs文件夹下的文件名] 访问静态资源
// http://localhost:8080/1691313647439_labi.jpg
app.use(static('./imgs'))
app.listen(8080, () => {
console.log('http://localhost:8080');
})
错误处理和数据响应
数据响应
响应体ctx.body的类型:
- string
- Buffer
- Stream
- Object || Array
- null 不响应任何内容
ctx.status未设置任何内容,Koa自动将状态设置为200或204(no content)
const koa = require("koa");
const KoaRouter = require("@koa/router");
const fs = require("fs");
const app = new koa();
const userRouter = new KoaRouter({ prefix: "/users" });
userRouter.get("/", (ctx, next) => {
// // 1.string
// ctx.body = "ok";
// // 2.Object || Array
// ctx.body = {
// name: "ccc",
// age: 999,
// };
// // 3.Buffer
// ctx.body = Buffer.from("Buffer数据");
// 4.Stream 流数据
const readStream = fs.createReadStream("./imgs/1691313647439_labi.jpg");
ctx.type = "image/jpeg";
ctx.body = readStream;
// 5.null
// ctx.body = null;
});
app.use(userRouter.routes());
app.use(userRouter.allowedMethods());
app.listen(8080, () => {
console.log("http://localhost:8080");
});
错误处理
- 抛出自定义错误事件 ctx.app.emit(eventName,…args), 第一个参数为监听器的名称,第二个为不定参数
- app.on(“eventName”, (errCode, ctx) => {}) ,回调函数中的参数,均为emit中传递过来的
const koa = require("koa");
const KoaRouter = require("@koa/router");
const bodyParser = require("koa-bodyparser");
const app = new koa();
const userRouter = new KoaRouter({ prefix: "/users" });
app.use(bodyParser());
userRouter.post("/login", (ctx, next) => {
const { name, pwd } = ctx.request.body;
if (!name || !pwd) {
// 1.抛出自定义错误事件error
ctx.app.emit("error", -1001, ctx);
} else if (name !== "ccc" || pwd !== 123) {
ctx.app.emit("error", -1002, ctx);
} else {
ctx.body = {
code: 0,
msg: "登录成功",
token: "xxx",
};
}
});
app.use(userRouter.routes());
app.use(userRouter.allowedMethods());
// 2.监听事件,统一错误处理
app.on("error", (code, ctx) => {
let msg = "未知错误";
switch (code) {
case -1001:
msg = "账号或密码为空";
break;
case -1002:
msg = "账号或密码错误";
break;
default:
break;
}
ctx.body = {
code,
msg,
};
});
app.listen(8080, () => {
console.log("http://localhost:8080");
});
Koa执行同步、异步代码
洋葱模型(U型)
Koa的中间件机制:
- 遇到next或者 await next就中断本中间件的代码执行,跳转到对应的下一个中间件执行期内的代码,一直到最后一个中间件
- 然后逆序回退到倒数第二个中间件await next 或者next下部分的代码执行,完成后继续回退,一直回退到第一个中间件await next或者next下部分的代码执行完成,中间件全部执行结束。
- Koa 是在所有中间件中使用 ctx.body 设置响应数据,但是并不会立即响应,而是在所有中间件执行结束后,再调用 res.end(ctx.body) 进行响应
洋葱模型:请求与响应都在最外层,中间件处理是一层层进行。
同步代码
const koa = require("koa");
const app = new koa();
// Koa执行同步代码
app.use((ctx, next) => {
console.log("middleware1");
ctx.msg = "aaa";
next();
ctx.body = ctx.msg;
});
app.use((ctx, next) => {
console.log("middleware2");
ctx.msg += "bbb";
next();
});
app.use((ctx, next) => {
console.log("middleware3");
ctx.msg += "ccc";
});
app.listen(8080, () => {
console.log("http://localhost:8080");
});
异步代码
-
next前不加await
如果执行的下一个中间件是一个异步函数, 那么next默认不会等到中间件的结果, 就会执行下一步操作 -
next前加await
koa中的next()返回Promise对象,故可以等待异步代码执行结果
express中的next()返回void
const koa = require("koa");
const app = new koa();
function asyncFoo() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("bbb");
}, 1000);
});
}
// Koa执行异步代码
app.use(async (ctx, next) => {
console.log("middleware1");
ctx.msg = "aaa";
await next();
ctx.body = ctx.msg;
});
app.use(async (ctx, next) => {
console.log("middleware2");
const res = await asyncFoo();
ctx.msg += res;
await next();
});
app.use((ctx, next) => {
console.log("middleware3");
ctx.msg += "ccc";
});
app.listen(8080, () => {
console.log("http://localhost:8080");
});