简体   繁体   English

使用 Node.js HTTP 服务器获取和设置单个 Cookie

[英]Get and Set a Single Cookie with Node.js HTTP Server

I want to be able to set a single cookie, and read that single cookie with each request made to the nodejs server instance.我希望能够设置单个 cookie,并在对 nodejs 服务器实例发出的每个请求中读取该单个 cookie。 Can it be done in a few lines of code, without the need to pull in a third party lib?是否可以用几行代码完成,而不需要引入第三方库?

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/');

Just trying to take the above code directly from nodejs.org, and work a cookie into it.只是尝试直接从 nodejs.org 获取上面的代码,并在其中添加一个 cookie。

There is no quick function access to getting/setting cookies, so I came up with the following hack:没有获取/设置 cookie 的快速功能访问,所以我想出了以下技巧:

var http = require('http');

function parseCookies (request) {
    var list = {},
        rc = request.headers.cookie;

    rc && rc.split(';').forEach(function( cookie ) {
        var parts = cookie.split('=');
        list[parts.shift().trim()] = decodeURI(parts.join('='));
    });

    return list;
}


http.createServer(function (request, response) {

  // To Read a Cookie
  var 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);

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

This will store all cookies into the cookies object, and you need to set cookies when you write the head.这会将所有的cookies存储到cookies对象中,写head时需要设置cookies。

If you're using the express library, as many node.js developers do, there is an easier way.如果您使用 express 库,就像许多 node.js 开发人员所做的那样,有一种更简单的方法。 Check the Express.js documentation page for more information.查看 Express.js 文档页面以获取更多信息。

The parsing example above works but express gives you a nice function to take care of that:上面的解析示例有效,但 express 为您提供了一个很好的功能来处理这个问题:

app.use(express.cookieParser());

To set a cookie:要设置 cookie:

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

To clear the cookie:要清除 cookie:

res.clearCookie('cookiename');

RevNoah had the best answer with the suggestion of using Express's cookie parser . RevNoah 给出了最好的答案,建议使用 Express 的cookie parser But, that answer is now 3 years old and is out of date.但是,这个答案现在已经 3 年了,而且已经过时了。

Using Express , you can read a cookie as follows使用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']);
})

And update your package.json with the following, substituting the appropriate relatively latest versions.并使用以下内容更新您的package.json ,替换相应的相对最新版本。

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

More operations like setting and parsing cookies are described here and here此处此处描述更多操作,例如设置和解析 cookie

As an enhancement to @Corey Hart's answer, I've rewritten the parseCookies() using:作为对@Corey Hart 回答的增强,我使用以下代码重写了parseCookies()

Here's the working example:这是工作示例:

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);

I also note that the OP uses the http module.我还注意到 OP 使用了 http 模块。 If the OP was using restify , he can make use of restify-cookies :如果 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);

You can use the "cookies" npm module, which has a comprehensive set of features.您可以使用“cookies”npm 模块,该模块具有一组全面的功能。

Documentation and examples at:文档和示例位于:
https://github.com/jed/cookies https://github.com/jed/cookies

To get a cookie splitter to work with cookies that have '=' in the cookie values:要让 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;
};

then to get an individual cookie:然后获取一个单独的cookie:

get_cookies(request)['my_cookie']

Cookies are transfered through HTTP-Headers Cookies通过 HTTP-Headers传输
You'll only have to parse the request-headers and put response-headers.您只需要解析请求头并放置响应头。

Let me repeat this part of question that answers here are ignoring:让我重复一下这里的答案忽略的这部分问题:

Can it be done in a few lines of code, without the need to pull in a third party lib?可以用几行代码完成,而不需要拉入第三方库吗?


Reading Cookies读取饼干

Cookies are read from requests with the Cookie header. Cookie 是从带有Cookie标头的请求中读取的。 They only include a name and value .它们只包含namevalue Because of the way paths work, multiple cookies of the same name can be sent.由于路径的工作方式,可以发送多个同名的 cookie。 In NodeJS, all Cookies in as one string as they are sent in the Cookie header.在 NodeJS 中,所有 Cookie 都作为一个字符串在Cookie标头中发送。 You split them with ;你把它们分开; . . Once you have a cookie, everything to the left of the equals (if present) is the name , and everything after is the value .一旦你有了一个 cookie,等号左边的所有东西(如果有的话)都是name ,后面的都是value Some browsers will accept a cookie with no equal sign and presume the name blank.一些浏览器会接受不带等号的 cookie 并假定名称为空白。 Whitespaces do not count as part of the cookie.空格不算作 cookie 的一部分。 Values can also be wrapped in double quotes ( " ). Values can also contain = . For example, formula=5+3=8 is a valid 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());

If you're not expecting duplicated names, then you can convert to an object which makes things easier.如果您不希望有重复的名称,那么您可以转换为一个对象,这让事情变得更容易。 Then you can access like object.myCookieName to get the value.然后你可以访问像object.myCookieName来获取值。 If you are expecting duplicates, then you want to do iterate through cookieEntries .如果您期望重复,那么您希望通过cookieEntries进行迭代。 Browsers feed cookies in descending priority, so reversing ensures the highest priority cookie appears in the object.浏览器以降序提供 cookie,因此反向确保最高优先级的 cookie 出现在对象中。 (The .slice() is to avoid mutation of the array.) .slice()是为了避免数组的突变。)


Settings Cookies设置 Cookie

"Writing" cookies is done by using the Set-Cookie header in your response. “写入”cookie 是通过在响应中使用Set-Cookie标头完成的。 The response.headers['Set-Cookie'] object is actually an array, so you'll be pushing to it. response.headers['Set-Cookie']对象实际上是一个数组,因此您将推送到它。 It accepts a string but has more values than just name and value .它接受一个字符串,但具有比namevalue更多的值。 The hardest part is writing the string, but this can be done in one line.最难的部分是编写字符串,但这可以在一行中完成。

/**
 * @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);

Remember you can set multiple cookies, because you can actually set multiple Set-Cookie headers in your request.请记住,您可以设置多个 cookie,因为您实际上可以在请求中设置多个Set-Cookie标头。 That's why it's an array.这就是为什么它是一个数组。


Note on external libraries:关于外部库的注意事项:

If you decide to use the express , cookie-parser , or cookie , note they have defaults that are non-standard.如果您决定使用expresscookie-parsercookie ,请注意它们具有非标准的默认值。 Cookies parsed are always URI Decoded (percent-decoded).解析的 Cookie 总是 URI 解码(百分比解码)。 That means if you use a name or value that has any of the following characters: !#$%&'()*+/:<=>?@[]^`{|} they will be handled differently with those libraries.这意味着如果您使用具有以下任何字符的名称或值: !#$%&'()*+/:<=>?@[]^`{|}将在这些库中以不同的方式处理它们。 If you're setting cookies, they are encoded with %{HEX} .如果您正在设置 cookie,它们将使用%{HEX}进行编码。 And if you're reading a cookie you have to decode them.如果您正在阅读 cookie,则必须对其进行解码。

For example, while email=name@domain.com is a valid cookie, these libraries will encode it as email=name%40domain.com .例如,虽然email=name@domain.com是一个有效的 cookie,但这些库会将其编码为email=name%40domain.com Decoding can exhibit issues if you are using the % in your cookie.如果您在 cookie 中使用% ,则解码可能会出现问题。 It'll get mangled.它会被破坏。 For example, your cookie that was: secretagentlevel=50%007and50%006 becomes secretagentlevel=507and506 .例如,您的 cookie 为: secretagentlevel=50%007and50%006变为secretagentlevel=507and506 That's an edge case, but something to note if switching libraries.这是一个边缘情况,但如果切换库需要注意。

Also, on these libraries, cookies are set with a default path=/ which means they are sent on every url request to the host.此外,在这些库中,cookie 设置为默认path=/ ,这意味着它们会在每个 url 请求上发送到主机。

If you want to encode or decode these values yourself, you can use encodeURIComponent or decodeURIComponent , respectively.如果您想自己编码或解码这些值,可以分别使用encodeURIComponentdecodeURIComponent


References:参考:


Additional information:附加信息:

Here's a neat copy-n-paste patch for managing cookies in node.这是一个用于在 node.js 中管理 cookie 的简洁的 copy-n-paste 补丁。 I'll do this in CoffeeScript, for the beauty.为了美观,我将在 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

Now you'll be able to handle cookies just as you'd expect:现在,您将能够像预期的那样处理 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
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);
}

First one needs to create cookie (I have wrapped token inside cookie as an example) and then set it in response.To use the cookie in following way install cookieParser第一个需要创建 cookie(我将令牌包装在 cookie 中作为示例)然后在响应中设置它。以下列方式使用 cookie 安装 cookieParser

app.use(cookieParser());

The browser will have it saved in its 'Resource' tab and will be used for every request thereafter taking the initial URL as base浏览器会将其保存在其“资源”选项卡中,并将用于此后以初始 URL 为基础的每个请求

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

To get cookie from a request on the server side is easy too.You have to extract the cookie from request by calling 'cookie' property of the request object.从服务器端的请求中获取 cookie 也很容易。您必须通过调用请求对象的 'cookie' 属性从请求中提取 cookie。

var token = req.cookies.token; // Retrieving Token stored in cookies

If you don't care what's in the cookie and you just want to use it, try this clean approach using request (a popular node module):如果您不关心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
  });
});

Using Some ES5/6 Sorcery & RegEx Magic使用一些 ES5/6 魔法和正则表达式魔法

Here is an option to read the cookies and turn them into an object of Key, Value pairs for client side, could also use it server side.这是读取cookie并将它们转换为客户端的键值对对象的选项,也可以在服务器端使用它。

Note : If there is a = in the value, no worries.注意:如果值中有= ,不用担心。 If there is an = in the key, trouble in paradise.如果键中有一个= ,天堂里的麻烦。

More Notes : Some may argue readability so break it down as you like.更多注释:有些人可能会争辩可读性,因此可以根据需要将其分解。

I Like Notes : Adding an error handler (try catch) wouldn't hurt.我喜欢注释:添加错误处理程序(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}`)
}

在此处输入图片说明

What is going on?到底是怎么回事?

iLikeCookies() will split the cookies by ; iLikeCookies()将通过; ( space after ; ): ;后的空格):

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

Then we map that array and split by first occurrence of = using regex capturing parens :然后我们映射该数组并使用正则表达式捕获 parens=的第一次出现进行拆分:

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

Then use our friend `Object.fromEntries to make this an object of key, val pairs.然后使用我们的朋友`Object.fromEntries 使其成为key, val 对的对象。

Nooice.诺斯。

I wrote this simple function just pass req.headers.cookie and cookie name我写了这个简单的函数只是传递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
}

I know that there are many answer to this question already, but here's a function made in native JS.我知道这个问题已经有很多答案,但这里有一个用原生 JS 制作的 function。

function parseCookies(cookieHeader) {
    var cookies = {};
    cookieHeader
        .split(";")
        .map(str => str.replace("=", "\u0000")
        .split("\u0000"))
        .forEach(x => cookies[x[0]] = x[1]); 

    return cookies;
}

It starts by taking in the document.cookie string.它首先接收 document.cookie 字符串。 Every key-value pair is separated by a semicolon (;).每个键值对都用分号 (;) 分隔。 Therefore the first step is to divide the string up each key-value pair.因此,第一步是将字符串分成每个键值对。

After that, the function replaces the first instance of "=" with a random character that isn't in the rest of the string, for this function I decided to use the NULL character ( ).之后,function 将“=”的第一个实例替换为不在字符串 rest 中的随机字符,对于这个 function,我决定使用 NULL 字符 ( )。 The key-value pair can now be split into just two pieces.键值对现在可以分成两部分。 The two pieces can now be combined into JSON.这两部分现在可以组合成 JSON。

you can use cookie lib to parse incoming multiple cookies, so that you won't have to worry about exceptions cases:-您可以使用 cookie 库来解析传入的多个 cookies,这样您就不必担心异常情况:-

var cookies = cookie.parse('foo=bar; equation=E%3Dmc%5E2'); var cookies = cookie.parse('foo=bar; equation=E%3Dmc%5E2'); // { foo: 'bar', equation: 'E=mc^2' } // { foo: 'bar', 等式: 'E=mc^2' }

to write a cookie you can do like this:-要写一个 cookie,你可以这样做:-

response.writeHead(200, { "Set-Cookie": mycookie=cookie , "Content-Type": text/plain }); response.writeHead(200, { "Set-Cookie": mycookie=cookie , "Content-Type": text/plain });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM