2. 内置模块之http模块

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
PDF application/pdf
JPEG图像 image/jpeg
PNG图像 image/png
GIF图像 image/gif
SVG图像 image/svg+xml
MP4视频 video/mp4
MP3音频 audio/mpeg
  1. 确保为所有响应设置正确的MIME类型,否则浏览器可能无法正确处理内容
  2. 对于未知文件类型,可以设置为application/octet-stream,这会触发下载行为
  3. 某些特殊文件类型可能需要特定的MIME类型才能正常工作
  4. 现代浏览器可以自动检测某些文件的类型,但显式设置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}`);
});
相关推荐
老任与码11 分钟前
Spring AI Alibaba(1)——基本使用
java·人工智能·后端·springaialibaba
烛阴12 分钟前
void 0 的奥秘:解锁 JavaScript 中 undefined 的正确打开方式
前端·javascript
初遇你时动了情29 分钟前
腾讯地图 vue3 使用 封装 地图组件
javascript·vue.js·腾讯地图
dssxyz33 分钟前
uniapp打包微信小程序主包过大问题_uniapp 微信小程序时主包太大和vendor.js过大
javascript·微信小程序·uni-app
华子w90892585943 分钟前
基于 SpringBoot+VueJS 的农产品研究报告管理系统设计与实现
vue.js·spring boot·后端
星辰离彬1 小时前
Java 与 MySQL 性能优化:Java应用中MySQL慢SQL诊断与优化实战
java·后端·sql·mysql·性能优化
GetcharZp2 小时前
彻底告别数据焦虑!这款开源神器 RustDesk,让你自建一个比向日葵、ToDesk 更安全的远程桌面
后端·rust
ohMyGod_1233 小时前
React16,17,18,19新特性更新对比
前端·javascript·react.js
@大迁世界3 小时前
第1章 React组件开发基础
前端·javascript·react.js·前端框架·ecmascript
一瓣橙子3 小时前
7.7日 实验03-Spark批处理开发(2)
开发语言·javascript·ajax