Obtenga y establezca una cookie única con el servidor HTTP Node.js

159

Quiero poder configurar una única cookie y leer esa única cookie con cada solicitud realizada a la instancia del servidor nodejs. ¿Se puede hacer en unas pocas líneas de código, sin la necesidad de obtener una biblioteca de terceros?

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

Solo trato de tomar el código anterior directamente de nodejs.org y aplicarle una cookie.

Corey Hart
fuente

Respuestas:

190

No hay acceso rápido a las funciones para obtener / configurar cookies, así que se me ocurrió el siguiente truco:

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

Esto almacenará todas las cookies en el objeto cookies, y debe configurarlas cuando escriba el encabezado.

Corey Hart
fuente
11
El código anterior funcionará incorrectamente si el valor de una cookie contiene un =signo igual ( ) como en una de las cookies de Facebook como fbm_1234123412341234=base_domain=.domain.com.
Ojo
3
¿los valores de las cookies no tienen que estar codificados por URL / por ciento? = en el valor doockie sería inválido en ese caso, ¿verdad?
les2
2
En ese caso dividido por ;entonces por la primera instancia de =. Izquierda es clave, derecha es valor.
iLoch
44
Me encontré con el mismo problema que @Eye, en vez de ir la ruta de @iLoch me cambió parts[0]a parts.shift()y parts[1]aparts.join('=')
aron.duby
3
El código proporcionado tenía errores graves y la gente creía que podía copiarlo. Vea mi última actualización
Dan
124

Si está utilizando la biblioteca express, como lo hacen muchos desarrolladores de node.js, hay una manera más fácil. Consulte la página de documentación de Express.js para obtener más información.

El ejemplo de análisis anterior funciona pero express le brinda una buena función para encargarse de eso:

app.use(express.cookieParser());

Para configurar una cookie:

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

Para borrar la cookie:

res.clearCookie('cookiename');
RevNoah
fuente
14
La biblioteca de cookies es en realidad de la biblioteca subyacente connect; No necesita tomar todo expreso para obtener cookies de ayuda.
Ajax
1
en realidad, la biblioteca de cookies no es parte de connect (que no puede establecer cookies)
framp
10
cookie-parserya no es parte de express y / o connect, pero está disponible como middleware: github.com/expressjs/cookie-parser
Koen.
77
¿Cómo se "consigue" una cookie en este ejemplo? Para completar y responder la pregunta.
arrogante
1
Downvoting debido a la necesidad de instalar express solo para usar cookies
Steven
34

RevNoah tuvo la mejor respuesta con la sugerencia de usar el analizador de cookies de Express . Pero esa respuesta tiene ahora 3 años y está desactualizada.

Con Express , puede leer una cookie de la siguiente manera

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

Y actualice su package.jsoncon lo siguiente, sustituyendo las versiones relativamente recientes apropiadas.

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

Más operaciones como la configuración y el análisis cookies se describen aquí y aquí

Kirby
fuente
8
La pregunta pregunta cómo llegar y establecer. Para configurar use esto:res.cookie('cookieName', cookieValue, { maxAge: 900000, httpOnly: true });
Augie Gardner
¿Puede configurar y obtener cookies como por sesión (sin la necesidad de conocer y manejar claves)?
Matej J
12

Como una mejora a la respuesta de @Corey Hart, he reescrito el parseCookies()uso de:

Aquí está el ejemplo de trabajo:

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

También noto que el OP usa el módulo http. Si el OP estaba usando restify , puede hacer 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
fuente
1
algunas sugerencias ahora, 3.5 años después, para futuros espectadores. Use let/consty stringifyCookiespuede estar alineado con un en maplugar de construir una matriz de esa manera.
Ascherer el
10

Puede usar el módulo npm "cookies", que tiene un conjunto completo de características.

Documentación y ejemplos en:
https://github.com/jed/cookies

zah
fuente
1
Parece que ese módulo está diseñado para su uso en servidores http. ¿Existe una herramienta cookiejar para manejar cookies en clientes http? Básicamente, no puedo decirle al cliente http lib: si obtienes encabezados Set-Cookie, recuérdalos automáticamente y luego pasa las solicitudes salientes posteriores según corresponda (cuando el dominio coincida).
Cheeso
Esta sería una característica de su biblioteca de cliente http de elección. Puedo sugerir superagente como un buen ejemplo.
zah
Dejé de intentar que esta lib funcionara en express después de un par de horas ... use connect en su lugar.
enko
7

Para que un divisor de cookies funcione con cookies que tienen '=' en los valores de 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;
};

luego para obtener una cookie individual:

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

Las cookies se transfieren a través de encabezados HTTP
. Solo tendrá que analizar los encabezados de solicitud y colocar encabezados de respuesta.

Tobias P.
fuente
Wikipedia no es más fácil
Hos Mercury
2

Aquí hay un parche limpio de copiar y pegar para administrar las cookies en el nodo. Lo haré en CoffeeScript, por la belleza.

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

Ahora podrá manejar las cookies tal como esperaría:

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
fuente
3
Simplemente copie y pegue ese código en la pestaña INTRODUCIR COFFESCRIPT en coffeescript.org . Su respuesta me ayudó, y coffeescript no es tan difícil de leer si conoce JavaScript.
Mattijs
1

Permítanme repetir esta parte de la pregunta que las respuestas aquí ignoran:

¿Se puede hacer en unas pocas líneas de código, sin la necesidad de obtener una biblioteca de terceros?


Cookies de lectura

Las cookies se leen de las solicitudes con el Cookieencabezado. Solo incluyen a namey value. Debido a la forma en que funcionan las rutas, se pueden enviar múltiples cookies del mismo nombre. En NodeJS, todas las cookies en una sola cadena a medida que se envían en el Cookieencabezado. Los dividiste con ellos ;. Una vez que tiene una cookie, todo a la izquierda de los iguales (si está presente) es el name, y todo lo que sigue es el value. Algunos navegadores aceptarán una cookie sin signo igual y supondrán que el nombre está en blanco. Los espacios en blanco no cuentan como parte de la cookie. Los valores también se pueden incluir entre comillas dobles ( "). Los valores también pueden contener =. Por ejemplo, formula=5+3=8es una cookie válida.

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

Si no espera nombres duplicados, puede convertirlos en un objeto que facilite las cosas. Entonces puede acceder como object.myCookieNamepara obtener el valor. Si espera duplicados, entonces desea iterar cookieEntries. Los navegadores alimentan las cookies con prioridad descendente, por lo que la inversión asegura que la cookie de mayor prioridad aparezca en el objeto. (El .slice()objetivo es evitar la mutación de la matriz).


Cookies de configuración

Las cookies de "escritura" se realizan utilizando el Set-Cookieencabezado en su respuesta. El response.headers['Set-Cookie']objeto es en realidad una matriz, por lo que lo empujará hacia él. Acepta una cadena pero tiene más valores que solo namey value. La parte más difícil es escribir la cadena, pero esto se puede hacer en una línea.

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

Recuerde que puede configurar múltiples cookies, porque en realidad puede configurar múltiples Set-Cookieencabezados en su solicitud. Por eso es una matriz.


Nota sobre bibliotecas externas:

Si decide utilizar el express, cookie-parsero cookie, tenga en cuenta que tienen valores predeterminados que no son estándar. Las cookies analizadas siempre se decodifican por URI (decodificación porcentual). Eso significa que si usa un nombre o valor que tenga alguno de los siguientes caracteres: !#$%&'()*+/:<=>?@[]^`{|}se manejarán de manera diferente con esas bibliotecas. Si está configurando cookies, están codificadas con %{HEX}. Y si estás leyendo una cookie, debes decodificarla.

Por ejemplo, si bien [email protected]es una cookie válida, estas bibliotecas la codificarán como email=name%40domain.com. La decodificación puede presentar problemas si está utilizando el %en su cookie. Se destrozará. Por ejemplo, su cookie que fue: se secretagentlevel=50%007and50%006convierte secretagentlevel=507and506. Ese es un caso extremo, pero algo a tener en cuenta si se cambia de biblioteca.

Además, en estas bibliotecas, las cookies se configuran de manera predeterminada, lo path=/que significa que se envían en cada solicitud de URL al host.

Si desea codificar o decodificar estos valores usted mismo, puede usar encodeURIComponento decodeURIComponent, respectivamente.


Referencias


Información Adicional:

Mecha corta
fuente
0

Primero es necesario crear una cookie (como ejemplo, he envuelto el token dentro de la cookie) y luego configurarla en respuesta. Para usar la cookie de la siguiente manera, instale cookieParser

app.use(cookieParser());

El navegador lo guardará en su pestaña 'Recurso' y luego se usará para cada solicitud tomando la URL inicial como base

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

También es fácil obtener cookies de una solicitud en el lado del servidor. Debe extraer la cookie de la solicitud llamando a la propiedad 'cookie' del objeto de solicitud.

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

Si no le importa lo que hay en el cookiey solo quiere usarlo, intente este enfoque limpio usando request(un módulo de nodo 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
fuente
Lo siento, pero este código no tiene ningún sentido para mí. ¿Hay jar()algún nombre cursi para obtener la lista actual de cookies? ¿Cuál es el punto de las 2 invocaciones de solicitud? Esto es principalmente un anuncio, no una respuesta.
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);
}
Simón
fuente
0

Uso de algunos ES5 / 6 Magia y magia mágica

Aquí hay una opción para leer las cookies y convertirlas en un objeto de clave, pares de valores para el lado del cliente, también podría usarlo en el lado del servidor.

Nota : Si hay un =valor en el valor, no se preocupe. Si hay una =clave, problemas en el paraíso.

Más notas : Algunos pueden argumentar legibilidad, así que desglosarlo como desee.

Me gustan las notas : Agregar un controlador de errores (intente capturar) no estaría de más.

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

ingrese la descripción de la imagen aquí

Que esta pasando?

iLikeCookies()dividirá las cookies por ;( espacio después; ):

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

Luego mapeamos esa matriz y dividimos por primera vez el =uso de expresiones regulares que capturan parens :

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

Luego use nuestro amigo `Object.fromEntries para hacer de este un objeto de clave, pares de val.

Nooice

Steve
fuente
0

Escribí esta simple función solo pasa req.headers.cookiey el nombre de la 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
fuente