Response handling for multiple content types
Handling JSON Responses
In Koa2, handling JSON responses is one of the most common scenarios. By directly returning a JavaScript object via ctx.body
, Koa automatically converts it to JSON format and sets the correct Content-Type
header.
router.get('/api/user', async (ctx) => {
ctx.body = {
id: 1,
name: '张三',
age: 28,
hobbies: ['编程', '阅读', '旅行']
};
});
When a client requests this route, they will receive the following response:
{
"id": 1,
"name": "张三",
"age": 28,
"hobbies": ["编程", "阅读", "旅行"]
}
For more complex scenarios, you can use the koa-json
middleware to customize the JSON format:
const json = require('koa-json');
app.use(json({
pretty: process.env.NODE_ENV !== 'production',
param: 'pretty',
spaces: 2
}));
Handling HTML Responses
When returning HTML content, you need to manually set the Content-Type
to text/html
. You can use template engines like EJS or Pug, or directly return an HTML string.
router.get('/welcome', async (ctx) => {
ctx.type = 'text/html';
ctx.body = `
<!DOCTYPE html>
<html>
<head>
<title>Welcome Page</title>
</head>
<body>
<h1>Welcome to Our Website</h1>
<p>Current Time: ${new Date().toLocaleString()}</p>
</body>
</html>
`;
});
Example using the EJS template engine:
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'
}
});
});
Handling File Downloads
For file download responses, you need to set the appropriate Content-Disposition
header. Koa provides a convenient attachment
method.
const fs = require('fs');
const path = require('path');
router.get('/download', async (ctx) => {
const filePath = path.join(__dirname, 'files/report.pdf');
ctx.attachment('Monthly Report.pdf');
ctx.type = 'application/pdf';
ctx.body = fs.createReadStream(filePath);
});
To dynamically generate and download a file:
router.get('/export-csv', async (ctx) => {
const data = [
['Name', 'Age', 'City'],
['张三', '28', '北京'],
['李四', '32', '上海']
];
const csvContent = data.map(row => row.join(',')).join('\n');
ctx.attachment('User Data.csv');
ctx.type = 'text/csv';
ctx.body = csvContent;
});
Handling Streaming Responses
Koa natively supports streaming responses, which is useful for large files or real-time data.
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(`Data Chunk ${count++}\n`);
if (count >= 10) {
clearInterval(timer);
stream.end();
}
}, 500);
ctx.body = stream;
});
Another practical example is proxy forwarding:
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;
});
Handling SSE (Server-Sent Events)
SSE allows the server to push events to the client, suitable for real-time updates.
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();
});
});
Handling GraphQL Responses
Integrating GraphQL services in Koa requires additional middleware.
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: 'User' + id,
email: `user${id}@example.com`
})
};
router.all('/graphql', graphqlHTTP({
schema,
rootValue,
graphiql: true
}));
Handling WebSocket Upgrades
Although Koa itself does not directly handle WebSocket, it can be implemented with other libraries.
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('Received message:', message);
ws.send(`Server reply: ${message}`);
});
ws.send('Connection established');
});
// Koa middleware
app.use(async (ctx) => {
// Regular HTTP request handling
ctx.body = 'HTTP request handled';
});
Handling Content Negotiation
Return different response formats based on the client's Accept
header.
router.get('/resource', async (ctx) => {
const data = {
id: 123,
title: 'Content Negotiation Example',
content: 'This is an example demonstrating different response formats'
};
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}`;
}
});
Handling Error Responses
A unified error-handling middleware can standardize error response formats.
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);
}
});
// Usage example
router.get('/protected', async (ctx) => {
if (!ctx.headers.authorization) {
const error = new Error('Unauthorized access');
error.status = 401;
error.code = 'UNAUTHORIZED';
throw error;
}
ctx.body = { message: 'Welcome to protected resource' };
});
Handling Redirect Responses
Koa provides convenient methods for redirection.
router.get('/old-route', async (ctx) => {
ctx.redirect('/new-route');
ctx.status = 301; // Permanent redirect
});
router.get('/login', async (ctx) => {
if (!ctx.session.user) {
ctx.redirect('/auth?return_url=' + encodeURIComponent(ctx.url));
return;
}
ctx.body = 'Welcome back';
});
// Redirect with flash messages
router.post('/comments', async (ctx) => {
try {
await createComment(ctx.request.body);
ctx.flash = { type: 'success', message: 'Comment published successfully' };
ctx.redirect('back');
} catch (err) {
ctx.flash = { type: 'error', message: err.message };
ctx.redirect('back');
}
});
Handling Custom Content Types
For non-standard content types, you can manually set the 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:Team Meeting
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>Example RSS</title>
<description>This is an example RSS feed</description>
<item>
<title>First News</title>
<description>This is the content of the first news</description>
</item>
</channel>
</rss>`;
ctx.type = 'application/rss+xml';
ctx.body = rssContent;
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:请求头信息的读取与设置
下一篇:Cookie 操作与安全设置