Javascript getCookie 函数

IT技术 javascript cookies
2021-01-16 20:20:12

我找到了两个使用 Javascript 获取 cookie 数据的函数,一个在w3schools.com 上,另一个在quirksmode.org
我想知道我应该使用哪一个?

例如,我相信我在某处读到某些浏览器拆分;分号存在问题

w3schools:

function getCookie(c_name) {
    if (document.cookie.length > 0) {
        c_start = document.cookie.indexOf(c_name + "=");
        if (c_start != -1) {
            c_start = c_start + c_name.length + 1;
            c_end = document.cookie.indexOf(";", c_start);
            if (c_end == -1) c_end = document.cookie.length;
            return unescape(document.cookie.substring(c_start, c_end));
        }
    }
    return "";
}

怪癖模式:

function readCokie(name) {
    var nameEQ = name + "=";
    var ca = document.cookie.split(';');
    for(var i = 0; i < ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0) == ' ') c = c.substring(1, c.length);
        if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
    }
    return null;
}
5个回答

W3CSchool 的函数是错误的。如果有多个具有相同后缀的 cookie,则它会失败,例如:

ffoo=bar; foo=baz

当您搜索时,foo它将返回ffoo而不是foo的值

现在我要做的是:首先,您需要了解 cookie 传输方式的语法。Netscape 的原始规范(在 haxx.se上只有这样的副本)使用分号来分隔多个 cookie,而每个名称/值对都具有以下语法:

NAME =VALUE
这个字符串是一个不包括分号、逗号和空格的字符序列。如果需要在名称或值中放置此类数据,建议使用一些编码方法,例如 URL 样式%XX编码,但不定义或不需要编码。

因此,document.cookie在分号或逗号处拆分字符串是一个可行的选择。

除此之外,RFC 2109还指定 cookie 用分号或逗号分隔:

cookie          =       "Cookie:" cookie-version
                        1*((";" | ",") cookie-value)
cookie-value    =       NAME "=" VALUE [";" path] [";" domain]
cookie-version  =       "$Version" "=" value
NAME            =       attr
VALUE           =       value
path            =       "$Path" "=" value
domain          =       "$Domain" "=" value

尽管两者都允许,但首选逗号,因为它们是 HTTP 中列表项的默认分隔符。

注意:为了向后兼容,Cookie 标头中的分隔符;到处都是分号 ( )。服务器还应接受逗号 ( ,) 作为 cookie 值之间的分隔符,以便将来兼容。

此外,名称/值对还有一些进一步的限制,因为VALUE也可以是RFC 2616 中指定的带引号的字符串

attr        =     token
value       =     token | quoted-string

所以这两个cookie版本需要分别对待:

if (typeof String.prototype.trimLeft !== "function") {
    String.prototype.trimLeft = function() {
        return this.replace(/^\s+/, "");
    };
}
if (typeof String.prototype.trimRight !== "function") {
    String.prototype.trimRight = function() {
        return this.replace(/\s+$/, "");
    };
}
if (typeof Array.prototype.map !== "function") {
    Array.prototype.map = function(callback, thisArg) {
        for (var i=0, n=this.length, a=[]; i<n; i++) {
            if (i in this) a[i] = callback.call(thisArg, this[i]);
        }
        return a;
    };
}
function getCookies() {
    var c = document.cookie, v = 0, cookies = {};
    if (document.cookie.match(/^\s*\$Version=(?:"1"|1);\s*(.*)/)) {
        c = RegExp.$1;
        v = 1;
    }
    if (v === 0) {
        c.split(/[,;]/).map(function(cookie) {
            var parts = cookie.split(/=/, 2),
                name = decodeURIComponent(parts[0].trimLeft()),
                value = parts.length > 1 ? decodeURIComponent(parts[1].trimRight()) : null;
            cookies[name] = value;
        });
    } else {
        c.match(/(?:^|\s+)([!#$%&'*+\-.0-9A-Z^`a-z|~]+)=([!#$%&'*+\-.0-9A-Z^`a-z|~]*|"(?:[\x20-\x7E\x80\xFF]|\\[\x00-\x7F])*")(?=\s*[,;]|$)/g).map(function($0, $1) {
            var name = $0,
                value = $1.charAt(0) === '"'
                          ? $1.substr(1, -1).replace(/\\(.)/g, "$1")
                          : $1;
            cookies[name] = value;
        });
    }
    return cookies;
}
function getCookie(name) {
    return getCookies()[name];
}
@Marco Demaio:该$Version=1属性是 RFC 规范的一部分,用于识别这些规范的 cookie,并且在浏览器开始实施它们时可能会变得很重要(尽管我不确定该document.cookie值将如何反映)。
2021-03-12 20:20:12
您检查 $Version 的那部分代码是什么?这是我第一次在类似 getCookie 的函数中看到它。它还有用吗?
2021-03-20 20:20:12
这个答案似乎扼杀了任何带有 '=' 作为 cookie 值的一部分的内容。将答案的getCookies()功能与document.cookie.split('; ')
2021-03-22 20:20:12
这个答案是过度设计的一个典型例子。为什么要包含所有这些代码,因为通过创建一个很可能是唯一的 cookie 名称可以轻松解决问题。例如。NAMESPACE_KEY
2021-03-23 20:20:12
我会移动trimRighttrimLeft退出该getCookie功能:您只需要设置一次。
2021-04-08 20:20:12

是的,W3Schools 解决方案不正确

对于那些想要它的人,这里有一个更简单的解决方案它只是在前面加上一个空格,因此对 indexOf() 的单次调用只会返回正确的 cookie。

function getCookie(c_name) {
    var c_value = " " + document.cookie;
    var c_start = c_value.indexOf(" " + c_name + "=");
    if (c_start == -1) {
        c_value = null;
    }
    else {
        c_start = c_value.indexOf("=", c_start) + 1;
        var c_end = c_value.indexOf(";", c_start);
        if (c_end == -1) {
            c_end = c_value.length;
        }
        c_value = unescape(c_value.substring(c_start,c_end));
    }
    return c_value;
}

这来自 w3schools,是不正确的,因为它可能导致获取错误的 cookie:

c_start = document.cookie.indexOf(c_name + "=");

如果你去寻找一个名为foo(我们假设它是一个现有的 cookie)的 cookie,那么其中的某个地方document.cookie将是 string foo=bar

但是,也不能保证不会有同样是字符串xfoo=something请注意,这仍然包含子字符串,foo=因此 w3schools 代码会找到它。如果xfoocookie 碰巧首先列出,您将返回something值(错误地!)而不是预期的bar.

给定两段代码之间的选择,永远不要选择从根本上被破坏的那一段。

上面显示的所有代码都已损坏。两个常见的问题是 (1) 如果一个 cookie 名称是另一个 cookie 名称的正确后缀,则 getcookie 函数可能会返回错误的值;(2) setcookie 函数不保护 cookie 值,这意味着如果 cookie 值包含(例如)一个“;” 那么所有的 cookie 都已损坏且无法解析。

TL;DR 改用这个编写良好的库:https : //github.com/js-cookie/js-cookie

尝试了包含等号的双引号 cookie 值的公认答案,但没有奏效,但 js-cookie 完成了这项工作。
2021-04-04 20:20:12

这是我的版本,它涵盖了引用值的边缘情况。

function getCookies() {
  const REGEXP = /([\w\.]+)\s*=\s*(?:"((?:\\"|[^"])*)"|(.*?))\s*(?:[;,]|$)/g;
  let cookies = {};
  let match;
  while( (match = REGEXP.exec(document.cookie)) !== null ) {
    let value = match[2] || match[3];
    cookies[match[1]] = decodeURIComponent(value);
  }
  return cookies;
}