k6 Cookie 处理
(Grafana k6 登堂入室, Part 13)
k6 中的 Cookie
HTTP Cookie 是网站在用户设备上存储状态信息的机制。服务器通过 Set-Cookie 响应头告诉客户端要存什么,客户端后续请求同一主机时会自动在 Cookie 请求头中带上这些数据。
好消息是,k6 默认就帮你处理了 Cookie 的接收、存储和发送。大多数情况下,你不需要做任何额外的事情,基于 Cookie 的登录态、会话管理等功能开箱即用。
但有时候你需要更精细的控制——比如手动设置 Cookie、检查 Cookie 属性、或者在不同请求间隔离 Cookie。
设置简单的 Cookie
最直接的方式是在请求参数中带上 Cookie:
import http from 'k6/http';
export default function () {
http.get('https://quickpizza.grafana.com/api/cookies', {
cookies: {
my_cookie: 'hello world',
},
});
}
这种方式只对当前这个请求生效,后续请求不会自动带上这个 Cookie。
使用 Cookie Jar
如果希望 Cookie 在后续请求中持续生效,需要把它添加到 Cookie Jar 中。k6 为每个 VU 维护了一个默认的 Cookie Jar:
import http from 'k6/http';
export default function () {
const jar = http.cookieJar();
jar.set('https://quickpizza.grafana.com/api/cookies', 'my_cookie', 'hello world');
// 后续请求会自动带上这个 Cookie
http.get('https://quickpizza.grafana.com/api/cookies');
}
VU 级别的 Cookie Jar 还会自动存储服务器返回的 Set-Cookie。
如果需要覆盖 Jar 里已有的 Cookie,可以用 replace: true:
import http from 'k6/http';
import { check } from 'k6';
export default function () {
const jar = http.cookieJar();
jar.set('https://quickpizza.grafana.com/api/cookies', 'my_cookie', 'hello world');
const cookies = {
my_cookie: {
value: 'hello world 2',
replace: true,
},
};
const res = http.get('https://quickpizza.grafana.com/api/cookies', {
cookies,
});
check(res.json(), {
'cookie has correct value': (b) => b.cookies.my_cookie == 'hello world 2',
});
}
访问响应中的 Cookie
通过响应对象的 cookies 属性可以查看服务器设置了哪些 Cookie:
import http from 'k6/http';
import { check } from 'k6';
export default function () {
const res = http.post('https://quickpizza.grafana.com/api/cookies?my_cookie=hello%20world', {
redirects: 0,
});
check(res, {
"has cookie 'my_cookie'": (r) => r.cookies.my_cookie.length > 0,
'cookie has correct value': (r) => r.cookies.my_cookie[0].value === 'hello world',
});
}
res.cookies 是一个 map,key 是 Cookie 名称,value 是一个数组(因为同名 Cookie 可能有不同的 domain 或 path)。
响应 Cookie 对象包含以下属性:
| 属性 | 类型 | 说明 |
|---|---|---|
name |
string | Cookie 名称 |
value |
string | Cookie 值 |
domain |
string | 决定 Cookie 发送到哪些主机 |
path |
string | 路径匹配时才发送 |
expires |
string | 过期时间,RFC1123 格式 |
max_age |
number | 有效秒数,和 expires 作用类似 |
secure |
boolean | 是否仅在 HTTPS 下发送 |
http_only |
boolean | 是否对浏览器 JavaScript 不可见 |
检查 Jar 中的 Cookie
用 cookiesForURL() 查看某个 URL 对应的 Cookie:
import http from 'k6/http';
import { check } from 'k6';
export default function () {
const res = http.post('https://quickpizza.grafana.com/api/cookies?my_cookie=hello%20world', {
redirects: 0,
});
const jar = http.cookieJar();
const cookies = jar.cookiesForURL('https://quickpizza.grafana.com/api/cookies');
check(res, {
"has cookie 'my_cookie'": (r) => cookies.my_cookie.length > 0,
'cookie has correct value': (r) => cookies.my_cookie[0] === 'hello world',
});
}
设置带属性的 Cookie
通过 Cookie Jar 可以精确控制 Cookie 的属性:
import http from 'k6/http';
import { check } from 'k6';
export default function () {
const jar = http.cookieJar();
jar.set('https://quickpizza.grafana.com/api/cookies', 'my_cookie', 'hello world', {
domain: 'quickpizza.grafana.com',
path: '/api/cookies',
secure: true,
max_age: 600,
});
const res = http.get('https://quickpizza.grafana.com/api/cookies');
check(res, {
'has status 200': (r) => r.status === 200,
'cookie has correct value': (r) => r.json().cookies.my_cookie == 'hello world',
});
}
局部 Cookie Jar
除了 VU 级别的全局 Cookie Jar,还可以创建局部的 Cookie Jar,让特定请求使用独立的 Cookie 空间:
import http from 'k6/http';
import { check } from 'k6';
export default function () {
const jar = new http.CookieJar();
const cookieOptions = {
domain: 'quickpizza.grafana.com',
path: '/api/cookies',
secure: true,
max_age: 600,
};
jar.set('https://quickpizza.grafana.com/api/cookies', 'my_cookie', 'hello world', cookieOptions);
// 用局部 Jar 替代 VU 级别的 Jar
const res = http.get('https://quickpizza.grafana.com/api/cookies', { jar });
check(res, {
'has status 200': (r) => r.status === 200,
'cookie has correct value': (r) => r.json().cookies.my_cookie == 'hello world',
});
}
这在需要模拟"未登录"状态或者隔离不同请求的 Cookie 时很有用。