Получить и установить один файл cookie с HTTP-сервером Node.js

159

Я хочу иметь возможность установить один файл cookie и читать этот файл при каждом запросе к экземпляру сервера nodejs. Может ли это быть сделано в несколько строк кода, без необходимости использовать стороннюю библиотеку?

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 и вставить в него куки.

Кори Харт
источник

Ответы:

190

Нет быстрого доступа к функции получения / настройки файлов 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/');

Это сохранит все куки в объекте куки, и вам нужно установить куки, когда вы пишете заголовок.

Кори Харт
источник
11
Приведенный выше код будет работать некорректно, если значение cookie содержит знак равенства ( =), как в одном из файлов cookie Facebook fbm_1234123412341234=base_domain=.domain.com.
Eye
3
Значения cookie не должны быть в кодировке URL / в процентах? в этом случае значение doockie будет неверным, верно?
les2
2
В этом случае разделить к тому ;времени на первый экземпляр =. Слева - ключ, справа - значение.
ИЛох
4
Я столкнулся с той же проблемой , как @Eye, вместо того , чтобы идти по пути @iLoch I перешел parts[0]к parts.shift()и parts[1]кparts.join('=')
aron.duby
3
В приведенном коде были серьезные ошибки, и люди не могли его скопировать. Смотрите мое последнее обновление
Дан
124

Если вы используете экспресс-библиотеку, как это делают многие разработчики node.js, есть более простой способ. Посетите страницу документации Express.js для получения дополнительной информации.

Приведенный выше пример разбора работает, но express дает вам хорошую функцию, чтобы позаботиться об этом:

app.use(express.cookieParser());

Чтобы установить cookie:

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

Чтобы очистить куки:

res.clearCookie('cookiename');
RevNoah
источник
14
Библиотека cookie на самом деле из базовой библиотеки connect; Вам не нужно брать все экспрессы, чтобы получить cookie-помощник.
Аякс,
1
на самом деле библиотека cookie не является частью connect (которая не может установить cookie)
framp
10
cookie-parserбольше не входит в экспресс и / или соединение, но доступно как промежуточное ПО: github.com/expressjs/cookie-parser
Koen.
7
Как «получить» cookie в этом примере? Для полноты и ответа на вопрос.
Хубатиш
1
Понижение рейтинга из-за необходимости установки экспресса только для использования файлов cookie
Стивен
34

У RevNoah был лучший ответ с предложением использовать парсер файлов cookie Express . Но этому ответу уже 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, описаны здесь и здесь.

Kirby
источник
8
Вопрос спрашивает, как получить и установить. Для установки используйте это:res.cookie('cookieName', cookieValue, { maxAge: 900000, httpOnly: true });
Augie Gardner
Можете ли вы устанавливать и получать cookie-файлы как сеанс (без необходимости знать и обрабатывать ключи)?
Matej J
12

В качестве дополнения к ответу @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. Если ОП использовал 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);
Стивен Куан
источник
1
некоторые предложения сейчас, 3,5 года спустя, для будущих зрителей. Используйте let/constи stringifyCookiesможет быть один, выровненный с mapвместо того, чтобы строить массив таким способом.
Ашерер
10

Вы можете использовать модуль «cookies» npm, который имеет полный набор функций.

Документация и примеры по адресу:
https://github.com/jed/cookies

Zah
источник
1
Похоже, этот модуль предназначен для использования на http-серверах. Существует ли инструмент cookiejar для обработки файлов cookie в клиентах http? По сути, я хочу сообщить http-клиенту lib: если вы получите какие-либо заголовки Set-Cookie, запомните их автоматически, а затем при необходимости передайте последующие исходящие запросы (когда домен совпадает).
Cheeso
Это будет особенность вашей библиотеки http клиента по вашему выбору. Я могу предложить superagent в качестве хорошего примера.
Зах
Отказался от попыток заставить эту библиотеку работать в экспрессе через пару часов ... вместо этого используйте connect.
enko
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']
Дэвид Беквит
источник
6

Файлы cookie передаются через HTTP-заголовки.
Вам нужно будет только проанализировать заголовки запроса и поместить заголовки ответа.

Тобиас П.
источник
Википедия не проще
Хос Меркьюри
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
Филип Оранж
источник
3
Просто скопируйте и вставьте этот код во вкладку TRY COFFESCRIPT на coffeescript.org . Ваш ответ помог мне, и coffeescript не так сложно прочитать, если вы знаете javascript.
Маттийс
1

Позвольте мне повторить эту часть вопроса, ответы на которую здесь игнорируются:

Может ли это быть сделано в несколько строк кода, без необходимости использовать стороннюю библиотеку?


Чтение файлов cookie

Куки читаются по запросам с Cookieзаголовком. Они включают в себя только nameи value. Из-за способа работы путей можно отправлять несколько файлов cookie с одним и тем же именем. В NodeJS все Cookies в одной строке, как они отправляются в 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());

Если вы не ожидаете дублированных имен, вы можете преобразовать их в объект, который облегчит задачу. Затем вы можете получить доступ, как object.myCookieNameполучить значение. Если вы ожидаете дубликаты, то вы хотите выполнить итерацию cookieEntries. Браузеры подают файлы cookie с нисходящим приоритетом, поэтому обращение гарантирует, что файл cookie с наивысшим приоритетом появляется в объекте. ( .slice()Чтобы избежать мутации массива.)


Настройки Cookies

«Написание» куки осуществляется с помощью Set-Cookieзаголовка в вашем ответе. response.headers['Set-Cookie']Объект на самом деле массив, так что вы будете толкая к нему. Он принимает строку, но имеет больше значений, чем просто nameи 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заголовков в своем запросе. Вот почему это массив.


Примечание по внешним библиотекам:

Если вы решили использовать express, cookie-parserили cookie, обратите внимание , что они имеют значение по умолчанию , которые являются нестандартными. Анализируемые файлы cookie всегда декодируются по URI (в процентах). Это означает, что если вы используете имя или значение, которое имеет любой из следующих символов: !#$%&'()*+/:<=>?@[]^`{|}они будут обрабатываться по-разному в этих библиотеках. Если вы устанавливаете куки, они кодируются с %{HEX}. И если вы читаете печенье, вы должны их расшифровать.

Например, хотя email=name@domain.comэто допустимый файл cookie, эти библиотеки будут кодировать его как email=name%40domain.com. Декодирование может вызвать проблемы, если вы используете %в своем файле cookie. Это будет искажено. Например, ваш файл cookie, который был: secretagentlevel=50%007and50%006становитсяsecretagentlevel=507and506 . Это крайний случай, но на что следует обратить внимание при переключении библиотек.

Кроме того, в этих библиотеках для файлов cookie устанавливается значение по умолчанию, path=/что означает, что они отправляются на каждый URL-запрос к хосту.

Если вы хотите кодировать или декодировать эти значения самостоятельно, вы можете использовать encodeURIComponentили decodeURIComponent, соответственно.


Ссылки:


Дополнительная информация:

Короткий фитиль
источник
0

Сначала нужно создать 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
Анкит Каушик
источник
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
  });
});
Бороды
источник
Извините, но этот код не имеет никакого смысла для меня. Является ли jar()какое-нибудь подходящее имя метода для получения текущего списка файлов cookie? Какой смысл в 2 вызовах запроса? Это в основном реклама, а не ответ.
user1944491
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);
}
Саймон
источник
0

Использование волшебства ES5 / 6 Sorcery & RegEx Magic

Вот вариант, чтобы прочитать куки и превратить их в объект Key, пары значений для клиентской части, также может использовать его на стороне сервера.

Примечание : если =в значении есть значение, не беспокойтесь. Если есть =ключ, проблема в раю.

Дополнительные примечания : Некоторые могут поспорить с удобочитаемостью, поэтому разбивайте ее, как хотите.

Мне нравятся заметки : добавление обработчика ошибок (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"]

Затем мы отображаем этот массив и разделяем по первому вхождению =использования паренов с захватом регулярных выражений :

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

Затем используйте нашего друга `Object.fromEntries, чтобы сделать его объектом ключа, пары val.

Nooice.

Стив
источник
0

Я написал эту простую функцию просто передать req.headers.cookieи имя куки

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

    arrOfCookies.forEach(element => {
        if(element.includes(name)){
            yourCookie = element.replace(name+'=','')
        }
    });
    return yourCookie
}
Ахмед Магди
источник