Appearance
Koa请求与响应
在Koa中,请求和响应处理是通过Context对象统一管理的。Context(ctx)对象是Koa的请求和响应对象的封装,提供了便捷的方法来处理HTTP请求和响应。
Context对象
Context对象是Koa的核心,它封装了Node.js的request和response对象:
javascript
app.use(async (ctx) => {
// ctx.request - Koa Request对象
// ctx.response - Koa Response对象
// ctx.req - Node.js原生request对象
// ctx.res - Node.js原生response对象
});
请求对象(Request)
请求基本信息
javascript
app.use(async (ctx) => {
// HTTP方法
console.log(ctx.request.method); // GET, POST, PUT, DELETE等
// URL信息
console.log(ctx.request.url); // 完整URL路径和查询字符串
console.log(ctx.request.path); // 路径部分
console.log(ctx.request.query); // 解析后的查询参数对象
console.log(ctx.request.querystring); // 原始查询字符串
// 协议和主机信息
console.log(ctx.request.origin); // 协议+主机名
console.log(ctx.request.href); // 完整URL
console.log(ctx.request.protocol); // http或https
console.log(ctx.request.host); // 主机名+端口
console.log(ctx.request.hostname); // 仅主机名
// 客户端信息
console.log(ctx.request.ip); // 客户端IP
console.log(ctx.request.ips); // 代理IP数组(如果配置了proxy)
});
请求头处理
javascript
app.use(async (ctx) => {
// 获取所有请求头
console.log(ctx.request.headers);
// 获取特定请求头
console.log(ctx.request.get('Content-Type'));
console.log(ctx.request.get('User-Agent'));
console.log(ctx.request.get('Authorization'));
// 检查内容类型
if (ctx.request.is('text/html')) {
console.log('Content-Type is text/html');
}
if (ctx.request.is('json')) {
console.log('Content-Type is JSON');
}
// 检查接受的内容类型
const acceptedType = ctx.request.accepts(['html', 'json', 'text']);
console.log('Client accepts:', acceptedType);
// 检查接受的字符集
console.log('Accepted charsets:', ctx.request.acceptsCharsets());
// 检查接受的编码
console.log('Accepted encodings:', ctx.request.acceptsEncodings());
// 检查接受的语言
console.log('Accepted languages:', ctx.request.acceptsLanguages());
});
请求体处理
javascript
const bodyParser = require('koa-bodyparser');
app.use(bodyParser());
app.use(async (ctx) => {
if (ctx.method === 'POST') {
// 解析后的请求体
console.log(ctx.request.body);
// 根据内容类型处理不同类型的数据
if (ctx.request.is('application/json')) {
// JSON数据
const jsonData = ctx.request.body;
console.log('JSON data:', jsonData);
} else if (ctx.request.is('application/x-www-form-urlencoded')) {
// 表单数据
const formData = ctx.request.body;
console.log('Form data:', formData);
} else if (ctx.request.is('multipart/form-data')) {
// 文件上传数据(需要其他中间件如koa-multer)
console.log('File upload data');
}
}
});
请求参数处理
javascript
// 使用@koa/router处理路由参数
const Router = require('@koa/router');
const router = new Router();
router.get('/users/:id/posts/:postId', async (ctx) => {
// 路由参数
console.log(ctx.params.id); // 从URL路径获取
console.log(ctx.params.postId); // 从URL路径获取
// 查询参数
console.log(ctx.query.page); // 从URL查询字符串获取
console.log(ctx.query.limit);
// 获取原始查询字符串
console.log(ctx.querystring);
ctx.body = {
userId: ctx.params.id,
postId: ctx.params.postId,
page: ctx.query.page,
limit: ctx.query.limit
};
});
响应对象(Response)
响应状态码
javascript
app.use(async (ctx) => {
// 设置响应状态码
ctx.status = 200; // 成功
ctx.status = 201; // 创建成功
ctx.status = 400; // 请求错误
ctx.status = 401; // 未授权
ctx.status = 403; // 禁止访问
ctx.status = 404; // 未找到
ctx.status = 500; // 服务器错误
// 或者通过response对象设置
ctx.response.status = 200;
// 使用状态消息
ctx.message = 'Custom message';
});
响应体设置
javascript
app.use(async (ctx) => {
// 设置不同类型的响应体
// 字符串响应
ctx.body = 'Hello World';
// JSON响应
ctx.body = {
message: 'Success',
data: [1, 2, 3],
timestamp: new Date()
};
// 数组响应
ctx.body = [1, 2, 3, 4];
// Buffer响应
ctx.body = Buffer.from('Hello World');
// Stream响应
const fs = require('fs');
ctx.body = fs.createReadStream('file.txt');
// 错误响应
ctx.body = { error: 'Something went wrong' };
// 空响应
ctx.body = '';
});
响应头设置
javascript
app.use(async (ctx) => {
// 设置单个响应头
ctx.set('X-Custom-Header', 'value');
// 设置多个响应头
ctx.set({
'X-API-Version': '1.0',
'X-Response-Time': '100ms',
'Cache-Control': 'no-cache'
});
// 或者通过response对象设置
ctx.response.set('Content-Type', 'application/json');
// 设置内容类型
ctx.type = 'json'; // 自动设置Content-Type为application/json
ctx.type = 'html'; // 自动设置Content-Type为text/html
ctx.type = 'text/plain'; // 显式设置Content-Type
// 设置内容长度
ctx.length = Buffer.byteLength(ctx.body);
// 设置ETag
ctx.response.etag = '12345';
// 设置最后修改时间
ctx.response.lastModified = new Date();
});
响应重定向
javascript
app.use(async (ctx) => {
// 内部重定向
ctx.redirect('/new-path');
// 外部重定向
ctx.redirect('https://example.com');
// 重定向回上一页
ctx.redirect('back');
// 或者通过response对象
ctx.response.redirect('/new-path');
});
高级请求处理
文件上传处理
javascript
const multer = require('@koa/multer');
const upload = multer({ dest: 'uploads/' });
// 单文件上传
router.post('/upload', upload.single('file'), async (ctx) => {
const file = ctx.file;
ctx.body = {
filename: file.filename,
originalName: file.originalname,
size: file.size
};
});
// 多文件上传
router.post('/uploads', upload.array('files', 12), async (ctx) => {
const files = ctx.files;
ctx.body = {
fileCount: files.length,
files: files.map(file => ({
filename: file.filename,
originalName: file.originalname,
size: file.size
}))
};
});
请求流处理
javascript
const fs = require('fs');
app.use(async (ctx) => {
if (ctx.method === 'POST' && ctx.path === '/upload-stream') {
// 处理请求流
const writeStream = fs.createWriteStream('uploaded-file.txt');
// 将请求流管道到文件
ctx.req.pipe(writeStream);
// 等待写入完成
await new Promise((resolve, reject) => {
writeStream.on('finish', resolve);
writeStream.on('error', reject);
});
ctx.body = { message: 'File uploaded successfully' };
}
});
高级响应处理
响应流
javascript
const fs = require('fs');
// 流式响应大文件
router.get('/large-file', async (ctx) => {
ctx.set('Content-Type', 'application/octet-stream');
ctx.set('Content-Disposition', 'attachment; filename="large-file.bin"');
// 创建文件读取流
ctx.body = fs.createReadStream('large-file.bin');
});
// JSON流式响应
router.get('/stream-json', async (ctx) => {
ctx.set('Content-Type', 'application/json');
ctx.body = JSON.stringify({ data: 'streaming' });
});
响应压缩
javascript
const compress = require('koa-compress');
app.use(compress({
threshold: 2048, // 大于2KB的响应才压缩
gzip: {
flush: require('zlib').constants.Z_SYNC_FLUSH
},
deflate: {
flush: require('zlib').constants.Z_SYNC_FLUSH
},
br: false // 禁用Brotli压缩
}));
app.use(async (ctx) => {
// 大响应体会自动被压缩
ctx.body = new Array(10000).fill('Hello World').join('\n');
});
错误处理
请求验证错误
javascript
const validateRequest = async (ctx, next) => {
// 验证内容类型
if (ctx.method === 'POST' && !ctx.request.is('application/json')) {
ctx.status = 400;
ctx.body = { error: 'Content-Type must be application/json' };
return;
}
// 验证请求体大小
const contentLength = ctx.request.length;
if (contentLength > 10 * 1024 * 1024) { // 10MB限制
ctx.status = 413;
ctx.body = { error: 'Request body too large' };
return;
}
await next();
};
app.use(validateRequest);
响应错误处理
javascript
app.use(async (ctx, next) => {
try {
await next();
// 如果没有设置响应体且状态码是404
if (ctx.status === 404 && !ctx.body) {
ctx.status = 404;
ctx.body = { error: 'Not Found', path: ctx.path };
}
} catch (err) {
// 统一错误处理
ctx.status = err.status || 500;
ctx.body = {
error: err.expose ? err.message : 'Internal Server Error',
status: ctx.status
};
// 记录错误
console.error('Response error:', err);
}
});
性能优化
响应缓存
javascript
// 简单的响应缓存
const responseCache = new Map();
const cacheResponse = (ttl = 300) => {
return async (ctx, next) => {
const cacheKey = `${ctx.method}:${ctx.path}`;
const cached = responseCache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < ttl * 1000) {
ctx.body = cached.body;
ctx.status = cached.status;
ctx.set(cached.headers);
return;
}
await next();
// 只缓存成功的GET请求
if (ctx.method === 'GET' && ctx.status >= 200 && ctx.status < 300) {
responseCache.set(cacheKey, {
body: ctx.body,
status: ctx.status,
headers: { ...ctx.response.headers },
timestamp: Date.now()
});
}
};
};
router.get('/cached-data', cacheResponse(600), async (ctx) => {
ctx.body = await getExpensiveData();
});
条件请求处理
javascript
// 处理条件请求(If-None-Match, If-Modified-Since)
const handleConditionalRequest = async (ctx, next) => {
await next();
// 生成ETag
const etag = `"${Buffer.from(JSON.stringify(ctx.body)).toString('base64')}"`;
// 检查条件请求头
const ifNoneMatch = ctx.request.get('If-None-Match');
const ifModifiedSince = ctx.request.get('If-Modified-Since');
if (ifNoneMatch === etag) {
ctx.status = 304; // Not Modified
ctx.body = null;
return;
}
ctx.set('ETag', etag);
};
通过掌握Koa的请求和响应处理,可以构建功能强大且高效的Web应用。