多种内容类型的响应处理
处理JSON响应
Koa2中处理JSON响应是最常见的场景之一。通过ctx.body直接返回JavaScript对象,Koa会自动将其转换为JSON格式并设置正确的Content-Type头。
router.get('/api/user', async (ctx) => {
ctx.body = {
id: 1,
name: '张三',
age: 28,
hobbies: ['编程', '阅读', '旅行']
};
});
当客户端请求这个路由时,会收到如下响应:
{
"id": 1,
"name": "张三",
"age": 28,
"hobbies": ["编程", "阅读", "旅行"]
}
对于更复杂的场景,可以使用koa-json中间件来自定义JSON格式:
const json = require('koa-json');
app.use(json({
pretty: process.env.NODE_ENV !== 'production',
param: 'pretty',
spaces: 2
}));
处理HTML响应
返回HTML内容时,需要手动设置Content-Type为text/html。可以使用模板引擎如ejs、pug等,也可以直接返回HTML字符串。
router.get('/welcome', async (ctx) => {
ctx.type = 'text/html';
ctx.body = `
<!DOCTYPE html>
<html>
<head>
<title>欢迎页</title>
</head>
<body>
<h1>欢迎来到我们的网站</h1>
<p>当前时间: ${new Date().toLocaleString()}</p>
</body>
</html>
`;
});
使用ejs模板引擎的示例:
const views = require('koa-views');
app.use(views(__dirname + '/views', {
extension: 'ejs'
}));
router.get('/profile', async (ctx) => {
await ctx.render('profile', {
user: {
name: '李四',
avatar: '/images/avatar.jpg'
}
});
});
处理文件下载
对于文件下载响应,需要设置适当的Content-Disposition头。Koa提供了便捷的attachment方法。
const fs = require('fs');
const path = require('path');
router.get('/download', async (ctx) => {
const filePath = path.join(__dirname, 'files/report.pdf');
ctx.attachment('月度报告.pdf');
ctx.type = 'application/pdf';
ctx.body = fs.createReadStream(filePath);
});
如果要实现动态生成文件并下载:
router.get('/export-csv', async (ctx) => {
const data = [
['姓名', '年龄', '城市'],
['张三', '28', '北京'],
['李四', '32', '上海']
];
const csvContent = data.map(row => row.join(',')).join('\n');
ctx.attachment('用户数据.csv');
ctx.type = 'text/csv';
ctx.body = csvContent;
});
处理流式响应
Koa原生支持流式响应,这对于大文件或实时数据非常有用。
const { PassThrough } = require('stream');
router.get('/stream', async (ctx) => {
ctx.type = 'text/plain';
const stream = new PassThrough();
let count = 0;
const timer = setInterval(() => {
stream.write(`数据块 ${count++}\n`);
if (count >= 10) {
clearInterval(timer);
stream.end();
}
}, 500);
ctx.body = stream;
});
另一个实际例子是代理转发:
const axios = require('axios');
router.get('/proxy-image', async (ctx) => {
const response = await axios.get('https://example.com/large-image.jpg', {
responseType: 'stream'
});
ctx.type = response.headers['content-type'];
ctx.body = response.data;
});
处理SSE(Server-Sent Events)
SSE允许服务器向客户端推送事件,适用于实时更新场景。
router.get('/sse', async (ctx) => {
ctx.request.socket.setTimeout(0);
ctx.req.socket.setNoDelay(true);
ctx.req.socket.setKeepAlive(true);
ctx.set({
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
const stream = new PassThrough();
ctx.body = stream;
let count = 0;
const sendEvent = () => {
stream.write(`event: update\n`);
stream.write(`id: ${Date.now()}\n`);
stream.write(`data: ${JSON.stringify({ count: count++, time: new Date() })}\n\n`);
};
const timer = setInterval(sendEvent, 1000);
ctx.req.on('close', () => {
clearInterval(timer);
stream.end();
});
});
处理GraphQL响应
在Koa中集成GraphQL服务需要额外的中间件。
const { graphqlHTTP } = require('koa-graphql');
const { buildSchema } = require('graphql');
const schema = buildSchema(`
type Query {
hello: String
user(id: ID!): User
}
type User {
id: ID!
name: String
email: String
}
`);
const rootValue = {
hello: () => 'Hello world!',
user: ({ id }) => ({
id,
name: '用户' + id,
email: `user${id}@example.com`
})
};
router.all('/graphql', graphqlHTTP({
schema,
rootValue,
graphiql: true
}));
处理WebSocket升级
虽然Koa本身不直接处理WebSocket,但可以配合其他库实现。
const Koa = require('koa');
const WebSocket = require('ws');
const app = new Koa();
const server = app.listen(3000);
const wss = new WebSocket.Server({ server });
wss.on('connection', (ws) => {
ws.on('message', (message) => {
console.log('收到消息:', message);
ws.send(`服务器回复: ${message}`);
});
ws.send('连接已建立');
});
// Koa中间件
app.use(async (ctx) => {
// 普通HTTP请求处理
ctx.body = 'HTTP请求处理';
});
处理内容协商
根据客户端Accept头返回不同格式的响应。
router.get('/resource', async (ctx) => {
const data = {
id: 123,
title: '内容协商示例',
content: '这是一个演示不同响应格式的例子'
};
switch (ctx.accepts('json', 'html', 'xml', 'text')) {
case 'json':
ctx.body = data;
break;
case 'html':
ctx.type = 'text/html';
ctx.body = `
<!DOCTYPE html>
<html>
<head><title>${data.title}</title></head>
<body>
<h1>${data.title}</h1>
<p>${data.content}</p>
</body>
</html>
`;
break;
case 'xml':
ctx.type = 'application/xml';
ctx.body = `
<?xml version="1.0" encoding="UTF-8"?>
<resource>
<id>${data.id}</id>
<title>${data.title}</title>
<content>${data.content}</content>
</resource>
`;
break;
default:
ctx.type = 'text/plain';
ctx.body = `${data.title}\n\n${data.content}`;
}
});
处理错误响应
统一的错误处理中间件可以规范化错误响应格式。
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.status = err.status || 500;
ctx.type = 'application/json';
ctx.body = {
error: {
code: err.code || 'INTERNAL_ERROR',
message: err.message,
details: err.details,
timestamp: new Date().toISOString()
}
};
if (process.env.NODE_ENV === 'development') {
ctx.body.error.stack = err.stack;
}
ctx.app.emit('error', err, ctx);
}
});
// 使用示例
router.get('/protected', async (ctx) => {
if (!ctx.headers.authorization) {
const error = new Error('未授权访问');
error.status = 401;
error.code = 'UNAUTHORIZED';
throw error;
}
ctx.body = { message: '欢迎访问受保护资源' };
});
处理重定向响应
Koa提供了便捷的重定向方法。
router.get('/old-route', async (ctx) => {
ctx.redirect('/new-route');
ctx.status = 301; // 永久重定向
});
router.get('/login', async (ctx) => {
if (!ctx.session.user) {
ctx.redirect('/auth?return_url=' + encodeURIComponent(ctx.url));
return;
}
ctx.body = '欢迎回来';
});
// 带闪存消息的重定向
router.post('/comments', async (ctx) => {
try {
await createComment(ctx.request.body);
ctx.flash = { type: 'success', message: '评论发布成功' };
ctx.redirect('back');
} catch (err) {
ctx.flash = { type: 'error', message: err.message };
ctx.redirect('back');
}
});
处理自定义内容类型
对于非标准内容类型,可以手动设置Content-Type。
router.get('/ical', async (ctx) => {
const icalContent = `BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//CalDAV Client//EN
BEGIN:VEVENT
UID:12345
DTSTAMP:20230501T120000Z
DTSTART:20230501T130000Z
DTEND:20230501T140000Z
SUMMARY:团队会议
END:VEVENT
END:VCALENDAR`;
ctx.type = 'text/calendar';
ctx.body = icalContent;
});
router.get('/rss', async (ctx) => {
const rssContent = `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>示例RSS</title>
<description>这是一个示例RSS源</description>
<item>
<title>第一条新闻</title>
<description>这是第一条新闻的内容</description>
</item>
</channel>
</rss>`;
ctx.type = 'application/rss+xml';
ctx.body = rssContent;
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:请求头信息的读取与设置
下一篇:Cookie 操作与安全设置