簡體   English   中英

使用 Node.js HTTP 服務器獲取和設置單個 Cookie

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

我希望能夠設置單個 cookie,並在對 nodejs 服務器實例發出的每個請求中讀取該單個 cookie。 是否可以用幾行代碼完成,而不需要引入第三方庫?

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 獲取上面的代碼,並在其中添加一個 cookie。

沒有獲取/設置 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/');

這會將所有的cookies存儲到cookies對象中,寫head時需要設置cookies。

如果您使用 express 庫,就像許多 node.js 開發人員所做的那樣,有一種更簡單的方法。 查看 Express.js 文檔頁面以獲取更多信息。

上面的解析示例有效,但 express 為您提供了一個很好的功能來處理這個問題:

app.use(express.cookieParser());

要設置 cookie:

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

要清除 cookie:

res.clearCookie('cookiename');

RevNoah 給出了最好的答案,建議使用 Express 的cookie parser 但是,這個答案現在已經 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

作為對@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 模塊。 如果 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);

您可以使用“cookies”npm 模塊,該模塊具有一組全面的功能。

文檔和示例位於:
https://github.com/jed/cookies

要讓 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']

Cookies通過 HTTP-Headers傳輸
您只需要解析請求頭並放置響應頭。

讓我重復一下這里的答案忽略的這部分問題:

可以用幾行代碼完成,而不需要拉入第三方庫嗎?


讀取餅干

Cookie 是從帶有Cookie標頭的請求中讀取的。 它們只包含namevalue 由於路徑的工作方式,可以發送多個同名的 cookie。 在 NodeJS 中,所有 Cookie 都作為一個字符串在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()是為了避免數組的突變。)


設置 Cookie

“寫入”cookie 是通過在響應中使用Set-Cookie標頭完成的。 response.headers['Set-Cookie']對象實際上是一個數組,因此您將推送到它。 它接受一個字符串,但具有比namevalue更多的值。 最難的部分是編寫字符串,但這可以在一行中完成。

/**
 * @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標頭。 這就是為什么它是一個數組。


關於外部庫的注意事項:

如果您決定使用expresscookie-parsercookie ,請注意它們具有非標准的默認值。 解析的 Cookie 總是 URI 解碼(百分比解碼)。 這意味着如果您使用具有以下任何字符的名稱或值: !#$%&'()*+/:<=>?@[]^`{|}將在這些庫中以不同的方式處理它們。 如果您正在設置 cookie,它們將使用%{HEX}進行編碼。 如果您正在閱讀 cookie,則必須對其進行解碼。

例如,雖然email=name@domain.com是一個有效的 cookie,但這些庫會將其編碼為email=name%40domain.com 如果您在 cookie 中使用% ,則解碼可能會出現問題。 它會被破壞。 例如,您的 cookie 為: secretagentlevel=50%007and50%006變為secretagentlevel=507and506 這是一個邊緣情況,但如果切換庫需要注意。

此外,在這些庫中,cookie 設置為默認path=/ ,這意味着它們會在每個 url 請求上發送到主機。

如果您想自己編碼或解碼這些值,可以分別使用encodeURIComponentdecodeURIComponent


參考:


附加信息:

這是一個用於在 node.js 中管理 cookie 的簡潔的 copy-n-paste 補丁。 為了美觀,我將在 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
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);
}

第一個需要創建 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

如果您不關心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
  });
});

使用一些 ES5/6 魔法和正則表達式魔法

這是讀取cookie並將它們轉換為客戶端的鍵值對對象的選項,也可以在服務器端使用它。

注意:如果值中有= ,不用擔心。 如果鍵中有一個= ,天堂里的麻煩。

更多注釋:有些人可能會爭辯可讀性,因此可以根據需要將其分解。

我喜歡注釋:添加錯誤處理程序(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"]

然后我們映射該數組並使用正則表達式捕獲 parens=的第一次出現進行拆分:

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

然后使用我們的朋友`Object.fromEntries 使其成為key, val 對的對象。

諾斯。

我寫了這個簡單的函數只是傳遞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
}

我知道這個問題已經有很多答案,但這里有一個用原生 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;
}

它首先接收 document.cookie 字符串。 每個鍵值對都用分號 (;) 分隔。 因此,第一步是將字符串分成每個鍵值對。

之后,function 將“=”的第一個實例替換為不在字符串 rest 中的隨機字符,對於這個 function,我決定使用 NULL 字符 ( )。 鍵值對現在可以分成兩部分。 這兩部分現在可以組合成 JSON。

您可以使用 cookie 庫來解析傳入的多個 cookies,這樣您就不必擔心異常情況:-

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

要寫一個 cookie,你可以這樣做:-

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