Obtenir et définir un cookie unique avec le serveur HTTP Node.js

159

Je veux pouvoir définir un seul cookie et lire ce cookie unique à chaque demande adressée à l'instance de serveur nodejs. Cela peut-il être fait en quelques lignes de code, sans avoir besoin de tirer une bibliothèque tierce?

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

J'essaie juste de prendre le code ci-dessus directement à partir de nodejs.org et d'y insérer un cookie.

Corey Hart
la source

Réponses:

190

Il n'y a pas d'accès rapide aux fonctions pour obtenir / configurer des cookies, j'ai donc proposé le hack suivant:

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

Cela stockera tous les cookies dans l'objet cookies, et vous devez définir des cookies lorsque vous écrivez la tête.

Corey Hart
la source
11
Le code ci-dessus ne fonctionnera pas correctement si la valeur d'un cookie contient un =signe égal ( ) comme dans l'un des cookies de Facebook comme fbm_1234123412341234=base_domain=.domain.com.
Eye
3
les valeurs des cookies ne doivent-elles pas être encodées en URL / en pourcentage? = dans la valeur doockie serait invalide dans ce cas, non?
les2
2
Dans ce cas, divisé d'ici ;là par la première instance de =. La gauche est la clé, la droite est la valeur.
iLoch
4
Je suis tombé sur la même question que @Eye, au lieu d'aller la route de @iLoch je suis passé parts[0]à parts.shift()et parts[1]àparts.join('=')
aron.duby
3
Le code donné contenait de sérieux bugs et les gens étaient prêts à le copier. Voir ma dernière mise à jour
Dan
124

Si vous utilisez la bibliothèque express, comme le font de nombreux développeurs node.js, il existe un moyen plus simple. Consultez la page de documentation d'Express.js pour plus d'informations.

L'exemple d'analyse ci-dessus fonctionne mais express vous offre une fonction intéressante pour vous en occuper:

app.use(express.cookieParser());

Pour définir un cookie:

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

Pour effacer le cookie:

res.clearCookie('cookiename');
RevNoah
la source
14
La bibliothèque de cookies provient en fait de la bibliothèque sous-jacente connect; vous n'avez pas besoin de prendre tout l'express pour obtenir un assistant de cookie.
Ajax le
1
en fait, la bibliothèque de cookies ne fait pas partie de connect (qui ne peut pas définir de cookie)
framp
10
cookie-parserne fait plus partie d'express et / ou connect, mais est disponible en tant que middleware: github.com/expressjs/cookie-parser
Koen.
7
Comment "obtenir" un cookie dans cet exemple? Pour être complet et répondre à la question.
hubatish
1
Downvoting en raison de la nécessité d'installer express juste pour utiliser des cookies
Steven
34

RevNoah a eu la meilleure réponse avec la suggestion d'utiliser l' analyseur de cookies d'Express . Mais, cette réponse a maintenant 3 ans et est obsolète.

En utilisant Express , vous pouvez lire un cookie comme suit

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

Et mettez à jour votre package.jsonavec ce qui suit, en remplaçant les versions relativement récentes appropriées.

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

D'autres opérations telles que la configuration et l'analyse des cookies sont décrites ici et ici

Kirby
la source
8
La question demande comment obtenir et régler. Pour définir, utilisez ceci:res.cookie('cookieName', cookieValue, { maxAge: 900000, httpOnly: true });
Augie Gardner
Pouvez-vous définir et obtenir des cookies par session (sans avoir besoin de connaître et de gérer les clés)?
Matej J
12

Pour améliorer la réponse de @Corey Hart, j'ai réécrit l' parseCookies()utilisation:

Voici l'exemple de travail:

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

Je note également que l'OP utilise le module http. Si l'OP utilisait restify , il peut utiliser 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
la source
1
quelques suggestions maintenant, 3,5 ans plus tard, pour les futurs téléspectateurs. Utilisez let/constet stringifyCookiespeut être doublé d'un mapau lieu de créer un tableau de cette façon.
Ascherer
10

Vous pouvez utiliser le module "cookies" npm, qui dispose d'un ensemble complet de fonctionnalités.

Documentation et exemples sur:
https://github.com/jed/cookies

zah
la source
1
Il semble que ce module soit destiné à être utilisé sur des serveurs http. Existe-t-il un outil cookiejar pour gérer les cookies dans les clients http? Fondamentalement, je voudrais dire au client http lib: si vous obtenez des en-têtes Set-Cookie, mémorisez-les automatiquement, puis transmettez les demandes sortantes suivantes selon le cas (lorsque le domaine correspond).
Cheeso
Ce serait une fonctionnalité de votre bibliothèque cliente http de votre choix. Je peux suggérer le superagent comme bon exemple.
zah
J'ai renoncé à essayer de faire fonctionner cette bibliothèque en express après quelques heures ... utilisez plutôt connect.
enko
7

Pour faire fonctionner un séparateur de cookies avec les cookies qui ont «=» dans les valeurs de 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;
};

puis pour obtenir un cookie individuel:

get_cookies(request)['my_cookie']
David Beckwith
la source
6

Les cookies sont transférés via les en-têtes HTTP.
Vous n'aurez qu'à analyser les en-têtes de requête et mettre les en-têtes de réponse.

Tobias P.
la source
Wikipédia pas plus facile
Hos Mercury
2

Voici un correctif copier-coller pour gérer les cookies dans node. Je vais le faire dans CoffeeScript, pour la beauté.

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

Vous pourrez désormais gérer les cookies comme vous vous en doutez:

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
Philippe Orange
la source
3
Copiez simplement et collez ce code dans l'onglet ESSAYER COFFESCRIPT sur coffeescript.org . Votre réponse m'a aidé, et coffeescript n'est pas si difficile à lire si vous connaissez javascript.
Mattijs
1

Permettez-moi de répéter cette partie de la question dont les réponses ici ignorent:

Cela peut-il être fait en quelques lignes de code, sans avoir besoin de tirer une bibliothèque tierce?


Lire les cookies

Les cookies sont lus à partir des demandes avec l'en- Cookietête. Ils incluent uniquement un nameet value. En raison du fonctionnement des chemins, plusieurs cookies du même nom peuvent être envoyés. Dans NodeJS, tous les cookies sont en une seule chaîne lorsqu'ils sont envoyés dans l'en- Cookietête. Vous les avez séparés ;. Une fois que vous avez un cookie, tout ce qui se trouve à gauche de l'égal (si présent) est le name, et tout ce qui suit est le value. Certains navigateurs acceptent un cookie sans signe égal et présument que le nom est vide. Les espaces ne comptent pas comme faisant partie du cookie. Les valeurs peuvent également être placées entre guillemets ( "). Les valeurs peuvent également contenir =. Par exemple, formula=5+3=8est un cookie valide.

/**
 * @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 vous n'attendez pas de noms dupliqués, vous pouvez convertir en un objet, ce qui facilite les choses. Ensuite, vous pouvez accéder à comme object.myCookieNamepour obtenir la valeur. Si vous prévoyez des doublons, vous souhaitez effectuer une itération cookieEntries. Les navigateurs alimentent les cookies par ordre de priorité décroissante, donc l'inversion garantit que le cookie de priorité la plus élevée apparaît dans l'objet. (Le but .slice()est d'éviter la mutation du tableau.)


Cookies de paramètres

"L'écriture" des cookies se fait en utilisant l'en- Set-Cookietête dans votre réponse. L' response.headers['Set-Cookie']objet est en fait un tableau, vous y pousserez donc. Il accepte une chaîne mais a plus de valeurs que juste nameet value. Le plus difficile est d'écrire la chaîne, mais cela peut être fait en une seule ligne.

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

N'oubliez pas que vous pouvez définir plusieurs cookies, car vous pouvez en fait définir plusieurs en- Set-Cookietêtes dans votre demande. C'est pourquoi c'est un tableau.


Remarque sur les bibliothèques externes:

Si vous décidez d'utiliser le express,, cookie-parserou cookie, notez que leurs valeurs par défaut ne sont pas standard. Les cookies analysés sont toujours décodés par URI (décodés en pourcentage). Cela signifie que si vous utilisez un nom ou une valeur !#$%&'()*+/:<=>?@[]^`{|}contenant l' un des caractères suivants: ils seront traités différemment avec ces bibliothèques. Si vous définissez des cookies, ils sont encodés avec%{HEX} . Et si vous lisez un cookie, vous devez le décoder.

Par exemple, tant que [email protected]est un cookie valide, ces bibliothèques l'encoderont comme email=name%40domain.com. Le décodage peut présenter des problèmes si vous utilisez le %dans votre cookie. Ça va être mutilé. Par exemple, votre cookie qui était: secretagentlevel=50%007and50%006devient secretagentlevel=507and506. C'est un cas limite, mais quelque chose à noter en cas de changement de bibliothèque.

De plus, sur ces bibliothèques, les cookies sont définis par défaut, path=/ce qui signifie qu'ils sont envoyés à chaque demande d'URL à l'hôte.

Si vous souhaitez encoder ou décoder ces valeurs vous-même, vous pouvez utiliser respectivement encodeURIComponentou decodeURIComponent.


Références:


Information additionnelle:

Mèche courte
la source
0

Tout d'abord, il faut créer un cookie (j'ai enveloppé le jeton dans le cookie comme exemple), puis le définir en réponse.Pour utiliser le cookie de la manière suivante, installez cookieParser

app.use(cookieParser());

Le navigateur l'aura sauvegardé dans son onglet 'Ressource' et sera utilisé pour chaque demande par la suite en prenant l'URL initiale comme base

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

Obtenir un cookie à partir d'une requête côté serveur est également facile. Vous devez extraire le cookie de la requête en appelant la propriété 'cookie' de l'objet de requête.

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

Si vous ne vous souciez pas de ce qu'il y a dans le cookieet que vous voulez juste l'utiliser, essayez cette approche propre en utilisant request(un module de nœud populaire):

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
  });
});
BlackBeard
la source
Désolé, mais ce code n'a aucun sens pour moi. Y a-t-il jar()un nom de méthode mignonne pour obtenir la liste actuelle des cookies? Quel est l'intérêt des 2 invocations de requête? C'est surtout une publicité, pas une réponse.
user1944491 le
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
la source
0

Utilisation de certains ES5 / 6 Sorcery & RegEx Magic

Voici une option pour lire les cookies et les transformer en un objet de paires clé, valeur pour le côté client, pourrait également l'utiliser côté serveur.

Remarque : s'il y a un =dans la valeur, pas de soucis. S'il y a un =problème dans la clé, au paradis.

Plus de notes : Certains peuvent discuter de la lisibilité, alors décomposez-la comme vous le souhaitez.

J'aime les notes : L'ajout d'un gestionnaire d'erreurs (try catch) ne ferait pas de 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}`)
}

entrez la description de l'image ici

Que se passe-t-il?

iLikeCookies()divisera les cookies par ;( espace après; ):

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

Ensuite, nous mappons ce tableau et le divisons par la première occurrence de l' =utilisation de parens de capture de regex :

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

Ensuite, utilisez notre ami `Object.fromEntries pour en faire un objet de paires clé, val.

Nooice.

Steve
la source
0

J'ai écrit cette fonction simple juste passer req.headers.cookieet le nom du 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
la source