Koa 是 基于Node.js平台的下一代Web开发框架(上一代 就是 Express)。
import Koa from 'koa';
import { PORT, HOST } from './config/system.mjs';
const app = new Koa();
app.use((ctx) => {
ctx.body = 'Hello, Koa.';
});
app.listen(PORT, HOST, () => {
console.log(`服务已启动,URL地址为:${'http://' + HOST + ':' + PORT}`);
});
引入 Koa 类后,通过 new Koa()
创建app实例。然后在调用对应listen
方法去监听一个端口号,如果没有报错,即服务启动成功。
然后 app.use()
方法是给Koa应用程序添加中间件。作用是给所有请求响应数据的。
本质 就是 JavaScript函数。会接受一个参数
ctx
,即上下文对象(整合了Node.js中的request和response两个对象)以及 第二个参数next
,即为栈中的下一个中间件函数。
- 可以在整个请求/响应循环周期内 做任何事情。
- 还可以修改ctx对象,比如添加路由间共享的数据等
- 结束请求/响应周期(结束响应)
- 调用下一个中间件
Koa Context 将 node 的
request
和response
对象封装到单个对象中,为编写 Web 应用程序和 API 提供了许多有用的方法。注意:Koa实例app的context属性 是 Context对象的原型。
ctx.query
获取请求URL地址上的查询参数,返回一个Object对象。ctx.body=
设置响应正文,同时会结束请求/响应周期ctx.status=
设置HTTP状态码,比如: ctx.status = 200;
ctx.message=
设置HTTP状态信息ctx.length=
设置 Content-Length 响应头ctx.type=
设置 Content-Type 响应头Koa非常轻量,并没有内置路由的功能。这样就需要我们自己下载路由功能的中间件--
@koa/router
npm install @koa/router
new Router()
创建router实例app.use(router.routes())
将路由功能注入到应用程序中app.use(router.allowedMethods())
router实例的方法
url:对应 客户端的请求地址
middlewares:对应 对此次请求处理的中间价(可以多个)
注意:所有的定义路由的方法 都会返回router实例。在实际开发时,可以选择router实例的连调方式去定义多个路由。
router.get(url, middlewares)
定义一个get请求方法的路由router.post(url, middlewares)
定义一个post请求方法的路由router.put(url, middlewares)
定义一个put请求方法的路由router.delete(url, middlewares)
定义一个delete请求方法的路由
import Router from '@koa/router';
const router = new Router();
// 定义一个 get的请求方法 路由
// 第一个参数: 路由的路径
// 第二个参数:路由处理的中间件
router
.get('/', (ctx) => {
ctx.body = '主页';
})
.get('/about', (ctx) => {
ctx.body = '关于-get';
})
.post('/about', (ctx) => {
ctx.body = '关于-post';
});
export default router;
在Koa框架中,会自动对所有请求地址中的查询字符串进行转译操作,也就是说 自动将查询字符串 转换成 普通Object对象形式。处理完成后,会存储在
ctx.query
上。
比如,我们需要实现分页获取数据时就会像这样:
import Router from '@koa/router';
const router = new Router();
// 定义一些静态数据
const users = [
{
id: 1,
name: '果果',
age: 18,
},
{
id: 2,
name: '静静',
age: 38,
},
{
id: 3,
name: '欣欣',
age: 18,
},
{
id: 4,
name: '诗诗',
age: 28,
},
];
router
// 获取所有用户数据
.get('/', (ctx) => {
//! 如果响应数据类型 赋值为 一个对象,那么koa会自动设置响应头content-type = application/json;chartset=utf-8
ctx.body = users;
})
// 分页获取数据
// 思考:1 是get还是post;get 2 需要给服务端 传递数据吗? 当前页码current and 每页条数count
.get('/page', (ctx) => {
// ? 如何获取用户传递过来的参数current以及count?
//! koa框架 会 自动处理所有请求的查询参数 -- 在处理路由时,可以直接通过ctx.query来获取所有的查询参数
// ctx.query 获取所有转译后查询参数对象
// console.log(ctx.query);
const { current, count } = ctx.query;
// 从总数据的数组中正确的截取出我们想要的那些数据
// 1 用数组的slice方法 进行 数据截取
// 1: [0,1) 2: [1, 2) 3: [2, 3)... m: [(m-1)*count, count * m )
ctx.body = users.slice((current - 1) * count, current * count);
});
export default router;
在处理Post请求时,如果客户端传递的数据 是 通过请求体body实现的,那么默认Koa是无法处理的转换工作的。
如果想要在所有路由处理响应之前,能够将Post请求体转换成普通对象就需要安装第三方的中间件。
npm install koa-body
接下来,需要在入口文件index.js
写下如下代码:
import Koa from 'koa';
import serve from 'koa-static';
import config from './app.config.js';
import router from './router/index.mjs';
import { koaBody } from 'koa-body';
const app = new Koa();
// 托管静态文件
app.use(serve(config.public));
// 一定要在路由之前先处理请求体的转换
// 默认对 json and xml and urlencoded 进行转换
app.use(koaBody());
// 添加路由
app.use(router.routes());
// 在options请求下响应支持的请求方法
app.use(router.allowedMethods());
app.listen(config.port, config.host, () => {
console.info(`服务已启动,地址为:'http://${config.host}:${config.port}'.`);
});
上述代码中:app.use(koaBody())
就实现了对json以及urlencoded数据类型的body转换了,之后就可以通过ctx.request.body
来获取转换后的请求体数据(类型为 普通JS对象)
先这样,
import Router from '@koa/router';
const router = new Router();
// 定义一些静态数据
const users = [
{
id: 1,
name: '果果',
age: 18,
},
{
id: 2,
name: '静静',
age: 38,
},
{
id: 3,
name: '欣欣',
age: 18,
},
{
id: 4,
name: '诗诗',
age: 28,
},
];
let uid = 4; // 用来储存当前最新添加用户的id值
router
// 获取所有用户数据
.get('/', (ctx) => {
//! 如果响应数据类型 赋值为 一个对象,那么koa会自动设置响应头content-type = application/json;chartset=utf-8
ctx.body = users;
})
// 添加新用户
.post('/insert', (ctx) => {
// console.log(ctx.request.body);
users.push({ ...ctx.request.body, id: ++uid });
ctx.body = '添加成功';
});
export default router;
koa-body
内置支持文件上传。
但是默认是没有开启请求体multipart/form-data
类型的解析,在对应上传文件的路由上需要单独配置。像这样,
// 上传文件
router
.put(
'/upload',
koaBody({
multipart: true,
formidable: {
uploadDir: './public', // 文件上传后位置
keepExtensions: true, // 保留原文件的后缀名
},
}),
(ctx) => {
// 获取待上传的文件
// 通过ctx.request.files 获取上传后文件的相关信息,比如newFilename 为 上传文件后的新名称
console.log(ctx.request.files);
ctx.body = `/${ctx.request.files.file.newFilename}`;
}
);
实际开发时,在上传文件时会指定文件最终存储的目录下,此时除了在请求体中通过file指定要上传的文件之外,在添加一个参数folder 用以指定上传后文件的存储位置。
import Router from '@koa/router';
import { koaBody } from 'koa-body';
import { copyFile, rm, access, mkdir } from 'node:fs/promises';
import { UPLOAD_DIR, STATIC } from '../app.config.mjs';
const router = new Router();
// 通过router实例的一些方法 比如 get、post等去定义 对应请求方法的路由
router
.put(
'/upload',
koaBody({
multipart: true,
formidable: {
keepExtensions: true,
uploadDir: UPLOAD_DIR, // 设置上传文件的最终位置
},
onError(err, ctx) {
ctx.body = {
code: 1,
msg: err,
};
},
}),
async (ctx) => {
let filename = ctx.request.files.file.newFilename;
let srcFile = `./${UPLOAD_DIR}/${filename}`;
let destFile = srcFile;
// 获取 请求体 中 除了file文件之外的其他数据
const { folder } = ctx.request.body;
// 如果用户在上传文件时 指定了目标文件夹
if (folder) {
// 1 将默认上传位置的文件 拷贝到 目标的文件夹下
destFile = `./${UPLOAD_DIR}/${folder}/${filename}`;
try {
// 先确保destFile的文件夹都真实存在
let isExist = await access(`./${UPLOAD_DIR}/${folder}`).catch(
() => false
);
// isExist 就是 undefined 或者 false
if (isExist == false) {
// 如果不存在 就创建
await mkdir(`./${UPLOAD_DIR}/${folder}`, { recursive: true });
}
await copyFile(srcFile, destFile);
// 2 成功后 删除默认位置的文件
await rm(srcFile);
} catch (error) {
console.error(error);
}
}
// 给客户端响应数据
ctx.body = {
code: 0,
data: {
path: `${UPLOAD_DIR.replace(STATIC, '')}/${folder}/${filename}`,
filename,
},
msg: '上传成功',
};
}
);
export default router;