Menu
CoddyTech

التذكّر باستخدام ملفات تعريف الارتباط

جزء من قسم المصادقة والأمان في رحلة Node.js على Coddy. الدرس 8 من 24.

ينسى HTTP أمرك بين الطلبات: يصل كل طلب بمفرده، ولا يستطيع الخادم معرفة أنه جاء من المتصفح نفسه. يحلّ ملف تعريف الارتباط هذه المشكلة. يجيب الخادم بترويسة Set-Cookie، ويرسل المتصفح القيمة مرة أخرى في ترويسة Cookie مع كل طلب لاحق إلى ذلك الموقع.

تعيّن res.cookie(name, value) ملفًا واحدًا. تقرأ 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}`);
});
challenge icon

التحدي

سهل

ينبغي أن يحصي GET /visit عدد مرات استدعاء هذا المتصفح له. لا يحفظ الخادم العدد. ينتقل العدد في ملف تعريف ارتباط، ويعيده المتصفح مع كل طلب.

  1. في server.js، داخل GET /visit، اقرأ ملفات تعريف الارتباط التي أرسلها المتصفح، باستخدام parseCookies كما يوضح الدرس. يوجد العدد في ملف تعريف الارتباط المسمى visits. يصل على هيئة نص، مثل "2". في الزيارة الأولى يكون مفقودًا، وهذا يعني أن قيمته 0.
  2. أضف 1 إلى العدد بوصفه رقمًا، بحيث تصبح "2" هي 3، لا "21".
  3. عيّن ملف تعريف الارتباط visits إلى العدد الجديد، لكي يعيده المتصفح في المرة التالية.
  4. أرسل العدد الجديد بوصفه رقمًا، لا نصًا: {"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');
});
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس المصادقة والأمان

تدرّب بنفسك: Playground لـ Express