183

我希望能够设置一个 cookie,并在对 nodejs 服务器实例的每个请求中读取该单个 cookie。是否可以在几行代码中完成,而不需要引入第三方库?

var http = require('http');

http.createServer(function (request, response) {
  response.writeHead(200, {'Content-Type': 'text/plain'});
  response.end('Hello World\n');
}).listen(8124);

console.log('Server running at http://127.0.0.1:8124/');

只是尝试直接从 nodejs.org 获取上面的代码,并在其中添加一个 cookie。

4

14 回答 14

222

没有快速访问获取/设置 cookie 的功能,所以我想出了以下 hack:

const http = require('http');

function parseCookies (request) {
    const list = {};
    const cookieHeader = request.headers?.cookie;
    if (!cookieHeader) return list;

    cookieHeader.split(`;`).forEach(function(cookie) {
        let [ name, ...rest] = cookie.split(`=`);
        name = name?.trim();
        if (!name) return;
        const value = rest.join(`=`).trim();
        if (!value) return;
        list[name] = decodeURIComponent(value);
    });

    return list;
}

const server = http.createServer(function (request, response) {
    // To Read a Cookie
    const cookies = parseCookies(request);

    // To Write a Cookie
    response.writeHead(200, {
        "Set-Cookie": `mycookie=test`,
        "Content-Type": `text/plain`
    });

    response.end(`Hello World\n`);
}).listen(8124);

const {address, port} = server.address();
console.log(`Server running at http://${address}:${port}`);

这会将所有的cookies存储到cookies对象中,你需要在写head的时候设置cookies。

于 2010-08-04T19:38:37.323 回答
129

如果您使用 express 库,就像许多 node.js 开发人员所做的那样,有一种更简单的方法。查看 Express.js 文档页面以获取更多信息。

上面的解析示例有效,但 express 为您提供了一个很好的功能来处理它:

app.use(express.cookieParser());

要设置 cookie:

res.cookie('cookiename', 'cookievalue', { maxAge: 900000, httpOnly: true });

要清除 cookie:

res.clearCookie('cookiename');
于 2012-07-20T16:46:19.613 回答
45

RevNoah 对使用 Express 的cookie parser的建议给出了最好的答案。但是,这个答案现在已经 3 岁了,而且已经过时了。

使用Express,您可以按如下方式读取 cookie

var express = require('express');
var cookieParser = require('cookie-parser');
var app = express();
app.use(cookieParser());
app.get('/myapi', function(req, resp) {
   console.log(req.cookies['Your-Cookie-Name-Here']);
})

并使用以下内容更新您package.json的内容,替换相应的相对最新版本。

"dependencies": {
    "express": "4.12.3",
    "cookie-parser": "1.4.0"
  },

此处此处描述了设置和解析 cookie 等更多操作

于 2015-09-30T21:29:12.333 回答
14

作为对@Corey Hart 答案的增强,我重写了parseCookies()使用:

这是工作示例:

let http = require('http');

function parseCookies(str) {
  let rx = /([^;=\s]*)=([^;]*)/g;
  let obj = { };
  for ( let m ; m = rx.exec(str) ; )
    obj[ m[1] ] = decodeURIComponent( m[2] );
  return obj;
}

function stringifyCookies(cookies) {
  return Object.entries( cookies )
    .map( ([k,v]) => k + '=' + encodeURIComponent(v) )
    .join( '; ');
}

http.createServer(function ( request, response ) {
  let cookies = parseCookies( request.headers.cookie );
  console.log( 'Input cookies: ', cookies );
  cookies.search = 'google';
  if ( cookies.counter )
    cookies.counter++;
  else
    cookies.counter = 1;
  console.log( 'Output cookies: ', cookies );
  response.writeHead( 200, {
    'Set-Cookie': stringifyCookies(cookies),
    'Content-Type': 'text/plain'
  } );
  response.end('Hello World\n');
} ).listen(1234);

我还注意到 OP 使用 http 模块。如果 OP 使用的是restify,他可以使用restify-cookies

var CookieParser = require('restify-cookies');
var Restify = require('restify');
var server = Restify.createServer();
server.use(CookieParser.parse);
server.get('/', function(req, res, next){
  var cookies = req.cookies; // Gets read-only cookies from the request
  res.setCookie('my-new-cookie', 'Hi There'); // Adds a new cookie to the response
  res.send(JSON.stringify(cookies));
});
server.listen(8080);
于 2015-07-27T06:07:32.953 回答
12

让我重复这部分问题,这里的答案忽略了:

是否可以在几行代码中完成,而不需要引入第三方库?


阅读饼干

Cookie 是从带有Cookie标头的请求中读取的。它们只包括 anamevalue。由于路径的工作方式,可以发送多个同名 cookie。在 NodeJS 中,所有 Cookie 都作为一个字符串在Cookie标头中发送。你用;. 一旦你有了一个 cookie,等号左边的所有东西(如果有的话)就是name,后面的所有东西都是value. 一些浏览器会接受不带等号的 cookie 并假定名称为空白。空格不算作 cookie 的一部分。值也可以用双引号 ( ") 括起来。值也可以包含=. 例如,formula=5+3=8是一个有效的 cookie。

/**
 * @param {string} [cookieString='']
 * @return {[string,string][]} String Tuple
 */
function getEntriesFromCookie(cookieString = '') {
  return cookieString.split(';').map((pair) => {
    const indexOfEquals = pair.indexOf('=');
    let name;
    let value;
    if (indexOfEquals === -1) {
      name = '';
      value = pair.trim();
    } else {
      name = pair.substr(0, indexOfEquals).trim();
      value = pair.substr(indexOfEquals + 1).trim();
    }
    const firstQuote = value.indexOf('"');
    const lastQuote = value.lastIndexOf('"');
    if (firstQuote !== -1 && lastQuote !== -1) {
      value = value.substring(firstQuote + 1, lastQuote);
    }
    return [name, value];
  });
}

const cookieEntries = getEntriesFromCookie(request.headers.Cookie); 
const object = Object.fromEntries(cookieEntries.slice().reverse());

如果您不希望名称重复,则可以转换为使事情变得更容易的对象。然后就可以访问likeobject.myCookieName来获取值了。如果您期望重复,那么您想要遍历cookieEntries. 浏览器以递减的优先级提供 cookie,因此反转确保最高优先级的 cookie 出现在对象中。(这.slice()是为了避免数组的突变。)


设置 Cookie

“写入” cookie 是通过Set-Cookie在您的响应中使用标题来完成的。该response.headers['Set-Cookie']对象实际上是一个数组,因此您将向它推送。它接受一个字符串,但具有比nameand更多的值value。最难的部分是写字符串,但这可以在一行中完成。

/**
 * @param {Object} options
 * @param {string} [options.name='']
 * @param {string} [options.value='']
 * @param {Date} [options.expires]
 * @param {number} [options.maxAge]
 * @param {string} [options.domain]
 * @param {string} [options.path]
 * @param {boolean} [options.secure]
 * @param {boolean} [options.httpOnly]
 * @param {'Strict'|'Lax'|'None'} [options.sameSite]
 * @return {string}
 */
function createSetCookie(options) {
  return (`${options.name || ''}=${options.value || ''}`)
    + (options.expires != null ? `; Expires=${options.expires.toUTCString()}` : '')
    + (options.maxAge != null ? `; Max-Age=${options.maxAge}` : '')
    + (options.domain != null ? `; Domain=${options.domain}` : '')
    + (options.path != null ? `; Path=${options.path}` : '')
    + (options.secure ? '; Secure' : '')
    + (options.httpOnly ? '; HttpOnly' : '')
    + (options.sameSite != null ? `; SameSite=${options.sameSite}` : '');
}

const newCookie = createSetCookie({
  name: 'cookieName',
  value: 'cookieValue',
  path:'/',
});
response.headers['Set-Cookie'].push(newCookie);

请记住,您可以设置多个 cookie,因为您实际上可以Set-Cookie在请求中设置多个标头。这就是为什么它是一个数组。


关于外部库的注意事项:

如果您决定使用expresscookie-parsercookie,请注意它们具有非标准的默认值。解析的 Cookie 始终是 URI 解码(百分比解码)。这意味着如果您使用具有以下任何字符的名称或值:!#$%&'()*+/:<=>?@[]^`{|}它们将与这些库以不同方式处理。如果您正在设置 cookie,它们将使用%{HEX}. 如果您正在阅读 cookie,则必须对其进行解码。

例如,虽然email=name@domain.com是一个有效的 cookie,但这些库会将其编码为email=name%40domain.com. 如果您%在 cookie 中使用 ,解码可能会出现问题。它会被破坏。例如,您的 cookie 是:secretagentlevel=50%007and50%006变为secretagentlevel=507and506。这是一个极端情况,但如果切换库需要注意。

此外,在这些库中,cookie 设置为默认值path=/,这意味着它们会在每个 url 请求中发送到主机。

如果您想自己编码或解码这些值,您可以分别使用encodeURIComponentdecodeURIComponent


参考:


附加信息:

于 2020-06-02T17:26:13.300 回答
9

您可以使用“cookies”npm 模块,它具有一套全面的功能。

文档和示例位于:
https ://github.com/jed/cookies

于 2011-08-08T10:27:49.463 回答
7

要让 cookie 拆分器与 cookie 值中包含 '=' 的 cookie 一起工作:

var get_cookies = function(request) {
  var cookies = {};
  request.headers && request.headers.cookie.split(';').forEach(function(cookie) {
    var parts = cookie.match(/(.*?)=(.*)$/)
    cookies[ parts[1].trim() ] = (parts[2] || '').trim();
  });
  return cookies;
};

然后得到一个单独的cookie:

get_cookies(request)['my_cookie']
于 2013-03-22T05:45:27.900 回答
6

Cookies通过 HTTP-Headers
传输 您只需解析请求标头并放置响应标头。

于 2010-08-04T13:29:57.240 回答
2

这是一个简洁的复制粘贴补丁,用于管理节点中的 cookie。为了美观,我将在 CoffeeScript 中执行此操作。

http = require 'http'

http.IncomingMessage::getCookie = (name) ->
  cookies = {}
  this.headers.cookie && this.headers.cookie.split(';').forEach (cookie) ->
    parts = cookie.split '='
    cookies[parts[0].trim()] = (parts[1] || '').trim()
    return

  return cookies[name] || null

http.IncomingMessage::getCookies = ->
  cookies = {}
  this.headers.cookie && this.headers.cookie.split(';').forEach (cookie) ->
    parts = cookie.split '='
    cookies[parts[0].trim()] = (parts[1] || '').trim()
    return

  return cookies

http.OutgoingMessage::setCookie = (name, value, exdays, domain, path) ->
  cookies = this.getHeader 'Set-Cookie'
  if typeof cookies isnt 'object'
    cookies = []

  exdate = new Date()
  exdate.setDate(exdate.getDate() + exdays);
  cookieText = name+'='+value+';expires='+exdate.toUTCString()+';'
  if domain
    cookieText += 'domain='+domain+';'
  if path
    cookieText += 'path='+path+';'

  cookies.push cookieText
  this.setHeader 'Set-Cookie', cookies
  return

现在您将能够像您期望的那样处理 cookie:

server = http.createServer (request, response) ->
  #get individually
  cookieValue = request.getCookie 'testCookie'
  console.log 'testCookie\'s value is '+cookieValue

  #get altogether
  allCookies = request.getCookies()
  console.log allCookies

  #set
  response.setCookie 'newCookie', 'cookieValue', 30

  response.end 'I luvs da cookies';
  return

server.listen 8080
于 2012-08-27T23:12:53.803 回答
0
var cookie = 'your_cookie';
var cookie_value;
var i = request.headers.indexOf(cookie+'=');
if (i != -1) {
  var eq = i+cookie.length+1;
  var end = request.headers.indexOf(';', eq);
  cookie_value = request.headers.substring(eq, end == -1 ? undefined : end);
}
于 2013-03-01T11:30:19.057 回答
0

如果您不关心其中的内容cookie而只想使用它,请使用request(流行的节点模块)尝试这种干净的方法:

var request = require('request');
var j = request.jar();
var request = request.defaults({jar:j});
request('http://www.google.com', function () {
  request('http://images.google.com', function (error, response, body){
     // this request will will have the cookie which first request received
     // do stuff
  });
});
于 2018-09-07T11:36:04.417 回答
0

使用一些 ES5/6 魔法和正则表达式魔法

这是一个读取 cookie 并将它们转换为客户端的键、值对对象的选项,也可以在服务器端使用它。

注意:如果=值中有a,不用担心。如果有=钥匙,麻烦在天堂。

更多注意事项:有些人可能会争论可读性,因此请根据需要将其分解。

我喜欢注释:添加错误处理程序(try catch)不会有任何伤害。

const iLikeCookies = () => {
    return Object.fromEntries(document.cookie.split('; ').map(v => v.split(/=(.+)/))); 
}

const main = () => {
    // Add Test Cookies
    document.cookie = `name=Cookie Monster;expires=false;domain=localhost`
    document.cookie = `likesCookies=yes=withARandomEquals;expires=false;domain=localhost`;

    // Show the Objects
    console.log(document.cookie)
    console.log('The Object:', iLikeCookies())

    // Get a value from key
    console.log(`Username: ${iLikeCookies().name}`)
    console.log(`Enjoys Cookies: ${iLikeCookies().likesCookies}`)
}

在此处输入图像描述

到底是怎么回事?

iLikeCookies();将通过(空格后;)分割饼干:

["name=Cookie Monster", "likesCookies=yes=withARandomEquals"]

=然后我们映射该数组并通过使用正则表达式捕获parens的第一次出现进行拆分:

[["name", "Cookie Monster"], ["likesCookies", "yes=withARandomEquals"]]

然后使用我们的朋友 `Object.fromEntries 使其成为 key、val 对的对象。

诺伊斯。

于 2020-04-29T12:10:59.780 回答
0

我写了这个简单的函数只是传递 req.headers.cookiecookie名称

const getCookieByName =(cookies,name)=>{
    const arrOfCookies = cookies.split(' ')
    let yourCookie = null

    arrOfCookies.forEach(element => {
        if(element.includes(name)){
            yourCookie = element.replace(name+'=','')
        }
    });
    return yourCookie
}
于 2020-05-30T21:22:01.860 回答
-1

第一个需要创建cookie(我已经将令牌包装在cookie中作为示例)然后在响应中设置它。要以以下方式使用cookie安装cookieParser

app.use(cookieParser());

浏览器会将其保存在其“资源”选项卡中,并将用于此后以初始 URL 为基础的每个请求

var token = student.generateToken('authentication');
        res.cookie('token', token, {
            expires: new Date(Date.now() + 9999999),
            httpOnly: false
        }).status(200).send();

在服务器端从请求中获取 cookie 也很容易。您必须通过调用请求对象的 'cookie' 属性从请求中提取 cookie。

var token = req.cookies.token; // Retrieving Token stored in cookies
于 2016-09-07T18:39:56.970 回答