You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js中如何为HTTP2请求实现Cookie Jar(Cookie容器)

在Node.js的HTTP/2请求中管理Cookie会话

Node.js的内置http2模块确实没有像一些封装好的HTTP客户端那样自带Cookie容器功能,不过我们可以通过两种方式解决跨请求的Cookie会话问题:手动实现Cookie的存储与携带,或者借助第三方库简化操作。


方法一:手动实现Cookie管理

如果不想依赖第三方库,我们可以自己维护Cookie存储,解析响应的Set-Cookie头,并在后续请求中拼接Cookie头发送。

步骤1:编写Cookie解析与存储工具

先创建一个Cookie存储对象,再实现解析Set-Cookie和生成请求Cookie头的函数:

const http2 = require('http2');

// 存储Cookie的结构:{ 域名: { 路径: { Cookie名: { 值, 过期时间, 其他属性 } } } }
const cookieStore = {};

// 解析响应头中的Set-Cookie,存入存储对象
function parseSetCookie(setCookieHeader, requestDomain) {
  if (!setCookieHeader) return;
  const cookieStrList = Array.isArray(setCookieHeader) ? setCookieHeader : [setCookieHeader];
  
  cookieStrList.forEach(cookieStr => {
    const parts = cookieStr.split(';').map(item => item.trim());
    const [nameValuePair] = parts;
    const [name, value] = nameValuePair.split('=').map(item => item.trim());
    
    // 解析Cookie的附加属性(domain、path、expires等)
    const cookieProps = {};
    parts.slice(1).forEach(prop => {
      if (prop.startsWith('domain=')) {
        cookieProps.domain = prop.split('=')[1].trim();
      } else if (prop.startsWith('path=')) {
        cookieProps.path = prop.split('=')[1].trim();
      } else if (prop.startsWith('expires=')) {
        cookieProps.expires = new Date(prop.split('=')[1].trim());
      } else if (prop === 'HttpOnly' || prop === 'Secure') {
        cookieProps[prop.toLowerCase()] = true;
      }
    });
    
    // 确定Cookie的目标域名和路径(没有指定则用当前请求的域名和根路径)
    const targetDomain = cookieProps.domain || requestDomain.replace(/^https?:\/\//, '');
    const targetPath = cookieProps.path || '/';
    
    // 初始化存储结构
    if (!cookieStore[targetDomain]) cookieStore[targetDomain] = {};
    if (!cookieStore[targetDomain][targetPath]) cookieStore[targetDomain][targetPath] = {};
    
    // 存入Cookie
    cookieStore[targetDomain][targetPath][name] = {
      value,
      ...cookieProps
    };
  });
}

// 根据当前请求的域名和路径,生成符合要求的Cookie头字符串
function getRequestCookieHeader(requestDomain, requestPath) {
  const validCookies = [];
  const cleanDomain = requestDomain.replace(/^https?:\/\//, '');
  
  // 遍历所有匹配的域名(包括父域名)
  Object.keys(cookieStore).forEach(storeDomain => {
    if (cleanDomain.endsWith(storeDomain)) {
      Object.keys(cookieStore[storeDomain]).forEach(storePath => {
        // 匹配路径(当前请求路径以Cookie的路径开头)
        if (requestPath.startsWith(storePath)) {
          Object.keys(cookieStore[storeDomain][storePath]).forEach(name => {
            const cookie = cookieStore[storeDomain][storePath][name];
            // 跳过已过期的Cookie
            if (cookie.expires && cookie.expires < new Date()) {
              delete cookieStore[storeDomain][storePath][name];
              return;
            }
            validCookies.push(`${name}=${cookie.value}`);
          });
        }
      });
    }
  });
  
  return validCookies.join('; ');
}

步骤2:集成到请求流程中

基于你的原请求代码,加入Cookie管理逻辑,实现跨请求携带会话:

// 第一次请求:获取并存储Cookie
function sendFirstRequest() {
  const client = http2.connect('https://twitter.com');
  
  const req = client.request({
    ':method': 'GET',
    ':path': '/'
  });
  
  req.setEncoding("utf8");
  let body = "";
  
  req.on('response', (responseHeaders) => {
    // 解析响应的Set-Cookie头,存入存储对象
    parseSetCookie(responseHeaders['set-cookie'], 'https://twitter.com');
  });
  
  req.on('data', (chunk) => { body += chunk; });
  
  req.on('end', () => {
    console.log('第一次请求完成,已存储会话Cookie');
    client.destroy();
    // 发起第二次请求,携带Cookie
    sendSecondRequest();
  });
  
  req.end();
}

// 第二次请求:携带存储的Cookie
function sendSecondRequest() {
  const client = http2.connect('https://twitter.com');
  
  // 获取当前请求对应的Cookie头
  const cookieHeader = getRequestCookieHeader('https://twitter.com', '/home');
  
  const req = client.request({
    ':method': 'GET',
    ':path': '/home',
    'cookie': cookieHeader // 携带Cookie头
  });
  
  req.setEncoding("utf8");
  let body = "";
  
  req.on('data', (chunk) => { body += chunk; });
  
  req.on('end', () => {
    console.log('第二次请求响应内容:', body);
    client.destroy();
  });
  
  req.end();
}

// 启动请求流程
sendFirstRequest();

方法二:使用第三方Cookie管理库

如果觉得手动实现太繁琐,可以用成熟的第三方库简化操作,比如轻量级的cookiejar库。

步骤1:安装依赖

npm install cookiejar

步骤2:集成到HTTP/2请求中

const http2 = require('http2');
const { CookieJar, Cookie } = require('cookiejar');

// 创建CookieJar实例,用于管理Cookie
const cookieJar = new CookieJar();

// 第一次请求:获取并存储Cookie
function sendFirstRequest() {
  const client = http2.connect('https://twitter.com');
  
  const req = client.request({
    ':method': 'GET',
    ':path': '/'
  });
  
  req.setEncoding("utf8");
  let body = "";
  
  req.on('response', (responseHeaders) => {
    // 解析Set-Cookie并存入CookieJar
    const setCookieHeaders = responseHeaders['set-cookie'];
    if (setCookieHeaders) {
      const cookieStrList = Array.isArray(setCookieHeaders) ? setCookieHeaders : [setCookieHeaders];
      cookieStrList.forEach(cookieStr => {
        cookieJar.setCookie(Cookie.parse(cookieStr), 'https://twitter.com');
      });
    }
  });
  
  req.on('data', (chunk) => { body += chunk; });
  
  req.on('end', () => {
    console.log('第一次请求完成');
    client.destroy();
    sendSecondRequest();
  });
  
  req.end();
}

// 第二次请求:从CookieJar获取并携带Cookie
function sendSecondRequest() {
  const client = http2.connect('https://twitter.com');
  
  // 获取当前请求对应的Cookie字符串
  const cookieHeader = cookieJar.getCookieString('https://twitter.com');
  
  const req = client.request({
    ':method': 'GET',
    ':path': '/home',
    'cookie': cookieHeader
  });
  
  req.setEncoding("utf8");
  let body = "";
  
  req.on('data', (chunk) => { body += chunk; });
  
  req.on('end', () => {
    console.log('第二次请求响应内容:', body);
    client.destroy();
  });
  
  req.end();
}

sendFirstRequest();

小提示:HTTP/2的:authority头对应HTTP/1.1的Host头,解析Cookie域名时,可以用responseHeaders[':authority']替代手动处理的URL域名,结果会更准确。

内容的提问来源于stack exchange,提问作者James Morgan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 10:09:16