用 Cookie 记住信息
本课属于 Coddy Node.js 学习旅程的“身份验证与安全”单元。第 8 课,共 24 课。
HTTP 不会在请求之间记住你:每个请求都是独立的,服务器无法判断它们是否来自同一个浏览器。Cookie 可以解决这个问题。服务器会通过 Set-Cookie 标头进行响应,而浏览器在之后向该网站发送的每个请求中,都会通过 Cookie 标头发回这个值。
res.cookie(name, value) 用于设置一个 Cookie。req.get('cookie') 会读取浏览器发送的 Cookie 标头,并将其作为一行文本返回。Express 不会替你读取 Cookie 标头(cookie-parser 包可以做到),因此 cookies.js 为你提供了 parseCookies。每个值都会以文本形式传入。
app.get('/theme/:name', (req, res) => {
res.cookie('theme', req.params.name);
res.send('Saved');
});
app.get('/', (req, res) => {
const { theme = 'light' } = parseCookies(req.get('cookie'));
res.send(`Your theme is ${theme}`);
});挑战
简单GET /visit 应统计此浏览器调用它的次数。服务器不会保存计数。计数会存放在 Cookie 中,浏览器会在每次请求时将它带回来。
- 在
server.js的GET /visit中,按照本课所示,使用parseCookies读取浏览器发送的 Cookie。计数存放在名为visits的 Cookie 中。它以文本形式传来,例如"2"。首次访问时,该 Cookie 不存在,此时计数为 0。 - 将计数作为数字加 1,这样
"2"会变成3,而不是"21"。 - 将
visitsCookie 设置为新的计数,这样浏览器下次就会把它发送回来。 - 以数字而不是文本的形式返回新的计数:
{"visits": 3}。
试试看:按“运行”。发送 GET /visit:{"visits": 1}。再发送一次:{"visits": 2}。第三次发送:{"visits": 3}。打开该响应的 Headers 选项卡:Set-Cookie 显示 visits=3; Path=/。
亲手试试
import express from 'express';
import { parseCookies } from './cookies.js';
import { notes, users } from './db.js';
import { hashPassword, verifyPassword } from './passwords.js';
const app = express();
app.use(express.json());
app.post('/signup', async (req, res) => {
const username = String(req.body.username ?? '').trim().toLowerCase();
const password = String(req.body.password ?? '');
if (!username || password.length < 8) {
return res.status(400).json({ error: 'Pick a username and a password of at least 8 characters' });
}
if (await users.findOne({ username })) {
return res.status(409).json({ error: 'That username is taken' });
}
await users.insertOne({ username, passwordHash: await hashPassword(password) });
res.status(201).json({ username });
});
app.post('/login', async (req, res) => {
const username = String(req.body.username ?? '').trim().toLowerCase();
const user = await users.findOne({ username });
if (!user || !(await verifyPassword(String(req.body.password ?? ''), user.passwordHash))) {
return res.status(401).json({ error: 'Wrong username or password' });
}
res.json({ username: user.username });
});
// 统计这个浏览器调用 GET /visit 的次数。
app.get('/visit', (req, res) => {
const visits = 1;
res.json({ visits });
});
// 笔记。目前任何人都可以读取和写入笔记。
app.get('/notes', async (req, res) => {
res.json(await notes.find().toArray());
});
app.post('/notes', async (req, res) => {
const note = { text: String(req.body.text ?? '') };
await notes.insertOne(note);
res.status(201).json(note);
});
app.listen(3000, () => {
console.log('Vault is running on port 3000');
});
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
身份验证与安全中的所有课
自己练习: Express 在线编辑器