您现在的位置是:网站首页 > 请求与响应对象详解文章详情

请求与响应对象详解

请求对象详解

Express框架中的请求对象(req)封装了HTTP请求的详细信息。这个对象提供了访问客户端请求数据的接口,包含请求头、参数、正文等信息。

基本属性

请求对象包含多个常用属性:

  • req.method: 获取HTTP请求方法(GET、POST等)
  • req.url: 获取请求的URL路径
  • req.headers: 获取请求头对象
  • req.protocol: 获取协议类型(http或https)
app.get('/user', (req, res) => {
  console.log(req.method); // "GET"
  console.log(req.url); // "/user"
  console.log(req.headers['user-agent']);
});

查询参数处理

Express自动解析URL中的查询字符串,可通过req.query访问:

// 请求 /search?q=express&page=2
app.get('/search', (req, res) => {
  console.log(req.query.q); // "express"
  console.log(req.query.page); // "2"
});

路由参数获取

使用冒号定义的路由参数可通过req.params访问:

app.get('/users/:userId/posts/:postId', (req, res) => {
  console.log(req.params.userId); // 用户ID
  console.log(req.params.postId); // 帖子ID
});

请求体解析

处理POST请求时需要中间件解析请求体:

const express = require('express');
const bodyParser = require('body-parser');

app.use(bodyParser.json()); // 解析JSON格式
app.use(bodyParser.urlencoded({ extended: true })); // 解析表单数据

app.post('/submit', (req, res) => {
  console.log(req.body.username); // 表单字段
  console.log(req.body.password);
});

文件上传处理

使用multer中间件处理文件上传:

const multer = require('multer');
const upload = multer({ dest: 'uploads/' });

app.post('/upload', upload.single('avatar'), (req, res) => {
  console.log(req.file); // 上传文件信息
  console.log(req.body); // 其他表单字段
});

响应对象详解

响应对象(res)用于向客户端发送HTTP响应,控制响应的状态码、头信息和内容。

基本响应方法

最常用的响应方法包括:

  • res.send(): 发送各种类型响应
  • res.json(): 发送JSON响应
  • res.status(): 设置HTTP状态码
app.get('/api/data', (req, res) => {
  res.status(200).json({
    success: true,
    data: { id: 1, name: 'Example' }
  });
});

设置响应头

使用res.set()方法设置自定义响应头:

app.get('/download', (req, res) => {
  res.set('Content-Type', 'application/pdf');
  res.set('Content-Disposition', 'attachment; filename="report.pdf"');
  // 发送文件内容...
});

重定向处理

实现页面重定向的几种方式:

// 基本重定向
app.get('/old', (req, res) => {
  res.redirect('/new');
});

// 带状态码的重定向
app.get('/temp', (req, res) => {
  res.redirect(302, '/new-location');
});

文件下载

发送文件作为响应:

const path = require('path');

app.get('/download-report', (req, res) => {
  const filePath = path.join(__dirname, 'reports', 'annual.pdf');
  res.download(filePath, 'annual-report-2023.pdf', (err) => {
    if (err) {
      // 处理错误
    }
  });
});

流式响应

处理大文件时使用流:

const fs = require('fs');

app.get('/video', (req, res) => {
  const videoPath = 'assets/sample.mp4';
  const stat = fs.statSync(videoPath);
  const fileSize = stat.size;
  const range = req.headers.range;
  
  if (range) {
    // 处理部分内容请求(用于视频流)
    const parts = range.replace(/bytes=/, "").split("-");
    const start = parseInt(parts[0], 10);
    const end = parts[1] ? parseInt(parts[1], 10) : fileSize-1;
    
    res.writeHead(206, {
      'Content-Range': `bytes ${start}-${end}/${fileSize}`,
      'Accept-Ranges': 'bytes',
      'Content-Length': end-start+1,
      'Content-Type': 'video/mp4'
    });
    
    fs.createReadStream(videoPath, {start, end}).pipe(res);
  } else {
    res.writeHead(200, {
      'Content-Length': fileSize,
      'Content-Type': 'video/mp4'
    });
    fs.createReadStream(videoPath).pipe(res);
  }
});

请求与响应对象的高级用法

自定义中间件处理

创建处理请求和响应的中间件:

// 记录请求时间的中间件
app.use((req, res, next) => {
  req.requestTime = Date.now();
  
  const originalSend = res.send;
  res.send = function(body) {
    console.log(`请求处理时间: ${Date.now() - req.requestTime}ms`);
    originalSend.call(this, body);
  };
  
  next();
});

内容协商

根据Accept头返回不同格式的响应:

app.get('/resource', (req, res) => {
  const accepts = req.accepts(['json', 'html', 'xml']);
  
  switch(accepts) {
    case 'json':
      res.json({ data: 'value' });
      break;
    case 'xml':
      res.type('application/xml');
      res.send('<data>value</data>');
      break;
    default:
      res.send('<p>value</p>');
  }
});

错误处理中间件

集中处理请求过程中的错误:

app.get('/error-prone', (req, res, next) => {
  try {
    // 可能出错的代码
    throw new Error('示例错误');
  } catch(err) {
    next(err); // 传递给错误处理中间件
  }
});

// 错误处理中间件
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({
    error: err.message,
    stack: process.env.NODE_ENV === 'development' ? err.stack : undefined
  });
});

性能优化技巧

响应压缩

使用compression中间件减小响应体积:

const compression = require('compression');

// 在所有路由前使用
app.use(compression({
  level: 6, // 压缩级别
  threshold: 1024, // 大于1KB才压缩
  filter: (req, res) => {
    if (req.headers['x-no-compression']) {
      return false;
    }
    return compression.filter(req, res);
  }
}));

缓存控制

设置适当的缓存头:

app.get('/static-data', (req, res) => {
  res.set('Cache-Control', 'public, max-age=3600'); // 缓存1小时
  res.json(getCachedData());
});

流式处理大响应

避免内存中构建大响应:

app.get('/large-data', (req, res) => {
  const dataStream = getLargeDataStream(); // 返回可读流
  
  res.type('application/json');
  res.write('['); // 开始JSON数组
  
  let first = true;
  dataStream.on('data', (chunk) => {
    if (!first) res.write(',');
    first = false;
    res.write(JSON.stringify(chunk));
  });
  
  dataStream.on('end', () => {
    res.end(']'); // 结束JSON数组
  });
});

安全相关实践

CSRF防护

使用csurf中间件:

const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });

// 在表单页面生成token
app.get('/form', csrfProtection, (req, res) => {
  res.render('form', { csrfToken: req.csrfToken() });
});

// 验证提交的token
app.post('/process', csrfProtection, (req, res) => {
  // 如果token验证通过才会执行到这里
  res.send('表单处理成功');
});

安全头部设置

使用helmet中间件增强安全性:

const helmet = require('helmet');

app.use(helmet());
app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'", "'unsafe-inline'", 'cdn.example.com'],
    styleSrc: ["'self'", "'unsafe-inline'"],
    imgSrc: ["'self'", 'data:', 'cdn.example.com']
  }
}));

速率限制

防止暴力破解攻击:

const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15分钟
  max: 100, // 每个IP最多100次请求
  message: '请求过于频繁,请稍后再试'
});

app.use('/api/', limiter);

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步