1. 创建http服务端
js
const http = require('http');
// 创建服务
const server = http.createServer((req, res) => {
// 获取请求行
console.log(req.method);
console.log(req.url);
console.log(req.httpVersion);
console.log(req.headers);
// 获取请求体
let body = '';
req.on('data', chunk=>{
body += chunk;
})
req.on('end', ()=>{
console.log(body);
// 写响应
//...
})
// 设置响应体
res.setHeader('Content-Type', 'text/plain');
res.end('hello');
});
// 监听端口9000
server.listen(9000, ()=>{
console.log('server start...');
})
2. 获取请求路径和查询参数
需要借助url模块
- 方式1
js
const http = require('http');
const url = require('url');
// 创建服务
const server = http.createServer((req, res) => {
// 解析req.url
let res = url.parse(req.url, true);
// 路径
let pathname = res.pathname;
// 查询字符串
let keyword = res.query.keyword;
// 设置响应体
res.setHeader('Content-Type', 'text/plain');
res.end('hello');
});
// 监听端口9000
server.listen(9000, ()=>{
console.log('server start...');
})
- 方式2 (推荐)
js
const http = require('http');
const url = require('url');
// 创建服务
const server = http.createServer((req, res) => {
let url = new URL(req.url, 'http://127.0.0.1');
console.log(url.pathname);
console.log(url.searchParams.get("keyword"));
// 设置响应体
res.setHeader('Content-Type', 'text/plain');
res.end('hello');
});
// 监听端口9000
server.listen(9000, ()=>{
console.log('server start...');
})
3. 设置资源类型(mime类型)
在HTTP模块中设置MIME类型(资源类型)是非常重要的,它告诉浏览器如何处理服务器返回的内容。以下是不同场景下设置MIME类型的方法:
js
const http = require('http');
const fs = require('fs');
http.createServer((req, res) => {
// 根据文件扩展名设置Content-Type
const filePath = req.url === '/' ? './index.html' : `.${req.url}`;
const extname = path.extname(filePath);
let contentType = 'text/html'; // 默认类型
// 设置常见MIME类型
switch(extname) {
case '.js':
contentType = 'text/javascript';
break;
case '.css':
contentType = 'text/css';
break;
case '.json':
contentType = 'application/json';
break;
case '.png':
contentType = 'image/png';
break;
case '.jpg':
contentType = 'image/jpg';
break;
}
// 设置响应头
// 设置字符集防止乱码,优先级最高
res.setHeader('Content-Type', contentType+';charset=utf-8');
// 读取文件并返回
fs.readFile(filePath, (err, content) => {
if(err) {
// 错误处理...
} else {
res.end(content);
}
});
}).listen(8080);
文件类型 | MIME类型 |
---|---|
HTML | text/html |
纯文本 | text/plain |
CSS | text/css |
JavaScript | text/javascript |
JSON | application/json |
application/pdf | |
JPEG图像 | image/jpeg |
PNG图像 | image/png |
GIF图像 | image/gif |
SVG图像 | image/svg+xml |
MP4视频 | video/mp4 |
MP3音频 | audio/mpeg |
- 确保为所有响应设置正确的MIME类型,否则浏览器可能无法正确处理内容
- 对于未知文件类型,可以设置为
application/octet-stream
,这会触发下载行为- 某些特殊文件类型可能需要特定的MIME类型才能正常工作
- 现代浏览器可以自动检测某些文件的类型,但显式设置MIME类型是最佳实践
4:错误处理
不同的错误需要设置不同的响应码
js
const http = require('http');
const fs = require('fs');
const path = require('path');
const server = http.createServer(async (req, res) => {
try {
// 1. 安全限制 - 防止目录遍历攻击
const baseDirectory = path.join(__dirname, 'public');
let requestedPath = path.join(baseDirectory, req.url);
// 规范化路径并检查是否在基础目录内
requestedPath = path.normalize(requestedPath);
if (!requestedPath.startsWith(baseDirectory)) {
throw { code: 403, message: 'Forbidden' };
}
// 2. 处理根路径
if (req.url === '/') {
req.url = '/index.html';
}
// 3. 获取文件扩展名并设置Content-Type
const extname = path.extname(req.url);
const mimeTypes = {
'.html': 'text/html',
'.js': 'text/javascript',
'.css': 'text/css',
'.json': 'application/json',
'.png': 'image/png',
'.jpg': 'image/jpg',
'.gif': 'image/gif',
'.svg': 'image/svg+xml',
};
// 4. 设置默认内容类型
const contentType = mimeTypes[extname] || 'application/octet-stream';
// 5. 使用Promise包装fs.readFile以便使用async/await
const filePath = path.join(baseDirectory, req.url);
const data = await fs.promises.readFile(filePath);
// 6. 成功响应
res.writeHead(200, { 'Content-Type': contentType });
res.end(data, 'utf-8');
} catch (error) {
// 7. 错误处理
console.error(`Error processing request ${req.url}:`, error);
let statusCode = 500;
let message = 'Internal Server Error';
if (error.code === 'ENOENT') {
statusCode = 404;
message = 'File Not Found';
} else if (error.code === 403) {
statusCode = 403;
message = error.message;
} else if (error.code === 'EACCES') {
statusCode = 403;
message = 'Permission Denied';
}
res.writeHead(statusCode, { 'Content-Type': 'text/plain' });
res.end(message);
}
});
// 8. 服务器错误处理
server.on('error', (error) => {
console.error('Server error:', error);
if (error.code === 'EADDRINUSE') {
console.error('Port is already in use');
process.exit(1);
}
});
// 9. 未捕获异常处理
process.on('uncaughtException', (error) => {
console.error('Uncaught Exception:', error);
// 优雅关闭
server.close(() => process.exit(1));
});
// 10. 未处理的Promise rejection
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
});
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});