Obter e definir um único cookie com o servidor HTTP Node.js.

159

Quero poder definir um único cookie e ler esse cookie único com cada solicitação feita à instância do servidor nodejs. Isso pode ser feito em algumas linhas de código, sem a necessidade de puxar uma lib de terceiros?

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

Apenas tentando pegar o código acima diretamente do nodejs.org e inserir um cookie nele.

Corey Hart
fonte

Respostas:

190

Não há acesso rápido às funções para obter / configurar cookies, então criei o seguinte hack:

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

Isso armazenará todos os cookies no objeto de cookies e você precisará configurá-los quando escrever a cabeça.

Corey Hart
fonte
11
O código acima funcionará incorretamente se o valor de um cookie contiver um =sinal de igual ( ) como em um dos cookies do Facebook, como fbm_1234123412341234=base_domain=.domain.com.
Olho
3
os valores dos cookies não precisam ser codificados em URL / codificado em porcentagem? = no valor doockie seria inválido nesse caso, certo?
les2
2
Nesse caso, divida até ;então pela primeira instância de =. Esquerda é a chave, direita é valor.
ILoch
4
Corri para o mesmo problema que @Eye, em vez de ir a rota de @iLoch eu mudei parts[0]para parts.shift()e parts[1]paraparts.join('=')
aron.duby
3
O código fornecido apresentava bugs graves e as pessoas costumavam copiá-lo. Veja minha última atualização
Dan
124

Se você estiver usando a biblioteca expressa, como fazem muitos desenvolvedores do node.js., existe uma maneira mais fácil. Consulte a página de documentação do Express.js para obter mais informações.

O exemplo de análise acima funciona, mas express fornece uma boa função para cuidar disso:

app.use(express.cookieParser());

Para definir um cookie:

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

Para limpar o cookie:

res.clearCookie('cookiename');
RevNoah
fonte
14
A biblioteca de cookies é realmente da conexão da biblioteca subjacente; você não precisa usar todo o expresso para obter ajuda de cookies.
Ajax
1
na verdade, a biblioteca cookie não é parte de conexão (que não é possível definir cookie)
framp
10
cookie-parsernão faz mais parte do express e / ou da conexão, mas está disponível como middleware: github.com/expressjs/cookie-parser
Koen.
7
Como alguém "obtém" um cookie neste exemplo? Para completar e responder à pergunta.
Julio
1
Downvoting devido à necessidade de instalar expressar apenas para usam cookies
Steven
34

RevNoah teve a melhor resposta com a sugestão de usar o analisador de cookies do Express . Mas, essa resposta agora tem 3 anos e está desatualizada.

Usando o Express , você pode ler um cookie da seguinte maneira

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']);
})

E atualize o seu package.jsoncom o seguinte, substituindo as versões relativamente mais recentes apropriadas.

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

Mais operações, como configurar e analisar cookies, são descritas aqui e aqui

Kirby
fonte
8
A pergunta pergunta como obter e definir. Para definir, use isto: #res.cookie('cookieName', cookieValue, { maxAge: 900000, httpOnly: true });
Augie Gardner
Você pode definir e obter cookies por sessão (sem a necessidade de conhecer e manipular chaves)?
precisa
12

Como aprimoramento da resposta de @Corey Hart, reescrevi o parseCookies()uso:

Aqui está o exemplo de trabalho:

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

Percebo também que o OP usa o módulo http. Se o OP estava usando restify , ele pode fazer uso de 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);
Stephen Quan
fonte
1
algumas sugestões agora, 3,5 anos depois, para futuros espectadores. Use let/conste stringifyCookiespode ser alinhado com um em mapvez de construir uma matriz dessa maneira.
Ascherer
10

Você pode usar o módulo npm "cookies", que possui um conjunto abrangente de recursos.

Documentação e exemplos em:
https://github.com/jed/cookies

zah
fonte
1
Parece que esse módulo deve ser usado em servidores http. Existe uma ferramenta cookiejar para manipular cookies em clientes http? Basicamente, devo informar à lib do cliente http: se você obtiver algum cabeçalho Set-Cookie, lembre-se automaticamente e depois repasse as solicitações de saída subsequentes conforme apropriado (quando o domínio corresponder).
Cheeso
Esse seria um recurso da sua biblioteca cliente http de escolha. Eu posso sugerir superagente como um bom exemplo.
ZAH
Desisti de tentar fazer com que essa biblioteca funcionasse expressamente após algumas horas ... use o connect.
enko 27/08/13
7

Para que um divisor de cookies funcione com cookies que tenham '=' nos valores dos cookies:

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

para obter um cookie individual:

get_cookies(request)['my_cookie']
David Beckwith
fonte
6

Os cookies são transferidos através de cabeçalhos HTTP
Você só precisará analisar os cabeçalhos de solicitação e colocar cabeçalhos de resposta.

Tobias P.
fonte
Wikipedia não é mais fácil
Hos Mercury
2

Aqui está um patch puro de copiar e colar para gerenciar cookies no nó. Farei isso no CoffeeScript, pela beleza.

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

Agora você poderá manipular cookies da maneira esperada:

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
Philip Orange
fonte
3
Basta copiar e colar esse código na guia TRY COFFESCRIPT no coffeescript.org . Sua resposta me ajudou e o coffeescript não é tão difícil de ler se você souber javascript.
Mattijs
1

Deixe-me repetir esta parte da pergunta que as respostas aqui estão ignorando:

Isso pode ser feito em algumas linhas de código, sem a necessidade de puxar uma lib de terceiros?


Lendo Cookies

Os cookies são lidos a partir de solicitações com o Cookiecabeçalho. Eles incluem apenas um namee value. Devido à maneira como os caminhos funcionam, vários cookies com o mesmo nome podem ser enviados. No NodeJS, todos os cookies são inseridos como uma sequência, pois são enviados no Cookiecabeçalho. Você os separou ;. Depois de ter um cookie, tudo à esquerda dos iguais (se houver) é o name, e tudo depois é o value. Alguns navegadores aceitam um cookie sem sinal de igual e assumem o nome em branco. Espaços em branco não contam como parte do cookie. Os valores também podem ser colocados entre aspas duplas ( "). Os valores também podem conter =. Por exemplo, formula=5+3=8é um cookie válido.

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

Se você não está esperando nomes duplicados, pode converter em um objeto que facilita as coisas. Então você pode acessar como object.myCookieNamepara obter o valor. Se você está esperando duplicatas, deseja fazer a iteração cookieEntries. Os navegadores alimentam os cookies com prioridade decrescente, portanto, a reversão garante que o cookie de maior prioridade seja exibido no objeto. (O .slice()objetivo é evitar a mutação da matriz.)


Cookies de configurações

"Escrever" cookies é feito usando o Set-Cookiecabeçalho na sua resposta. O response.headers['Set-Cookie']objeto é realmente uma matriz, então você estará pressionando para isso. Ele aceita uma string, mas tem mais valores do que apenas namee value. A parte mais difícil é escrever a string, mas isso pode ser feito em uma linha.

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

Lembre-se de que você pode definir vários cookies, porque na verdade pode definir vários Set-Cookiecabeçalhos em sua solicitação. É por isso que é uma matriz.


Nota sobre bibliotecas externas:

Se você decidir usar o express,, cookie-parserou cookie, observe que eles têm padrões que não são padrão. Os cookies analisados ​​são sempre decodificados por URI (decodificado em porcentagem). Isso significa que, se você usar um nome ou valor com qualquer um dos seguintes caracteres: !#$%&'()*+/:<=>?@[]^`{|}eles serão tratados de maneira diferente com essas bibliotecas. Se você estiver configurando cookies, eles serão codificados %{HEX}. E se você estiver lendo um cookie, precisará decodificá-lo.

Por exemplo, embora [email protected]seja um cookie válido, essas bibliotecas o codificarão como email=name%40domain.com. A decodificação pode apresentar problemas se você estiver usando o %em seu cookie. Vai ficar mutilado. Por exemplo, seu cookie que era: secretagentlevel=50%007and50%006se torna secretagentlevel=507and506. Esse é um caso extremo, mas algo a ser observado se você mudar de biblioteca.

Além disso, nessas bibliotecas, os cookies são configurados com um padrão, o path=/que significa que eles são enviados a cada solicitação de URL para o host.

Se você desejar codificar ou decodificar esses valores, poderá usar encodeURIComponentou decodeURIComponent, respectivamente.


Referências:


Informação adicional:

Pavio curto
fonte
0

Primeiro, é necessário criar um cookie (envolvi o token dentro do cookie como exemplo) e depois defini-lo em resposta.Para usar o cookie da seguinte maneira, instale o cookieParser

app.use(cookieParser());

O navegador o salvará na guia 'Recurso' e será usado para todas as solicitações posteriores, tendo o URL inicial como base

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

Também é fácil obter o cookie de uma solicitação no lado do servidor. Você precisa extrair o cookie da solicitação chamando a propriedade 'cookie' do objeto de solicitação.

var token = req.cookies.token; // Retrieving Token stored in cookies
Ankit kaushik
fonte
0

Se você não se importa com o conteúdo cookiee deseja apenas usá-lo, tente esta abordagem limpa usando request(um módulo de nó popular):

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
  });
});
Barba Negra
fonte
Desculpe, mas esse código não faz nenhum sentido para mim. Algum jar()nome de método atraente para obter a lista atual de cookies? Qual é o sentido das 2 invocações de solicitação? Isso é principalmente um anúncio, não uma resposta.
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);
}
Simon
fonte
0

Usando alguma feitiçaria ES5 / 6 e magia RegEx

Aqui está uma opção para ler os cookies e transformá-los em um objeto Chave, pares de valor para o lado do cliente, também pode usá-lo no lado do servidor.

Nota : Se houver um =valor, não se preocupe. Se houver um =na chave, problemas no paraíso.

Mais notas : alguns podem argumentar legibilidade, então divida-o como quiser.

Gosto de notas : adicionar um manipulador de erros (try catch) não faria mal.

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}`)
}

insira a descrição da imagem aqui

O que está acontecendo?

iLikeCookies()irá dividir os cookies por ;( espaço depois; ):

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

Em seguida, mapeamos essa matriz e dividimos pela primeira ocorrência do =uso de captura de expressões regulares :

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

Em seguida, use nosso amigo `Object.fromEntries para tornar isso um objeto de pares de chaves, val.

Nooice.

Steve
fonte
0

Eu escrevi essa função simples, basta passar req.headers.cookiee nome do 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
}
Ahmed Magdy
fonte