Menu
CoddyTech

إدارة الإصدارات

جزء من قسم واجهات REST API في رحلة Node.js على Coddy. الدرس 22 من 25.

يؤدي تغيير شكل الاستجابة إلى تعطيل كل عميل يستخدمها بالفعل. يتيح وضع الإصدار في المسار للعميلين القديم والجديد العمل جنبًا إلى جنب: /api/v1/orders اليوم، و/api/v2/orders عند تغيير الشكل. عندما يتغير المسار، يستجيب المسار القديم بإعادة توجيه إلى المسار الجديد:

GET  /api/orders/5   →  308  Location: /api/v1/orders/5
POST /api/orders     →  308  Location: /api/v1/orders

يسمح الرمزان 301 و302 للعملاء بإعادة إرسال طلب POST على هيئة GET، ما يؤدي إلى إسقاط نص الطلب. يعني 308 «أعِد إرسال الطلب نفسه إلى هناك»، مع تضمين الطريقة ونص الطلب. داخل وسيط مُركّب، يكون req.url قد فقد مسار التركيب، بينما يحتفظ req.originalUrl به كاملًا:

// في app.use('/api/orders', ...) لطلب GET /api/orders/5?full=1
req.url          // '/5?full=1'
req.originalUrl  // '/api/orders/5?full=1'
challenge icon

التحدي

سهل

ستتغير واجهة API بطرق لا تتوقعها العملاء القديمة، لذا أضف رقم إصدار إلى مساراتها. تصبح واجهة API الحالية الإصدار 1، على /api/v1/tasks، وتُوجّه العناوين القديمة العملاء إليها.

  1. في server.js، اربط موجّه المهام بالمسار /api/v1/tasks بدلًا من /api/tasks.
  2. أضف أيضًا في server.js برمجية وسيطة مرتبطة بالمسار /api/tasks لا تقوم إلا بإعادة التوجيه. تستجيب برمز الحالة 308 إلى العنوان نفسه ضمن /api/v1، مع الإبقاء على بقية المسار وسلسلة الاستعلام. داخل برمجية وسيطة مرتبطة بمسار، يحتوي req.originalUrl على العنوان القديم كاملًا. على سبيل المثال:
    • تُعيد GET /api/tasks/2 التوجيه إلى /api/v1/tasks/2.
    • تُعيد POST /api/tasks التوجيه إلى /api/v1/tasks.
    • تُعيد GET /api/tasks?done=false&sort=title التوجيه إلى /api/v1/tasks?done=false&sort=title.

    استخدم 308، لا 301: بعد استجابة 308، يعيد العميل إرسال طلب POST على هيئة POST، مع تضمين جسم الطلب.

  3. في routes/tasks.js، يضبط مسار POST قيمة Location على العنوان الجديد، مثل /api/v1/tasks/4 للمهمة 4.

جرّب ذلك: يُرجع GET /api/tasks/2 الرمز 308، وتُظهر علامة التبويب Headers القيمة Location: /api/v1/tasks/2. يعرض شريط الطلب إعادة التوجيه نفسها؛ ولا يتبعها. أرسل POST مع الجسم { "title": "Order new chairs" } إلى /api/tasks: ستحصل على 308 مع Location: /api/v1/tasks، ولن تُنشأ أي مهمة. أرسل POST بالجسم نفسه إلى /api/v1/tasks: ستحصل على 201 مع Location: /api/v1/tasks/4. يُرجع GET /api/v1/tasks/2 المهمة 2.

جرّب بنفسك

import express from 'express';
import { sendError } from './errors.js';
import taskRoutes from './routes/tasks.js';

const BOARD_ORIGIN = 'https://board.example.com';

const app = express();

// يسمح للصفحات على BOARD_ORIGIN باستدعاء واجهة API هذه من المتصفح.
app.use((req, res, next) => {
    res.set('Access-Control-Allow-Origin', BOARD_ORIGIN);

    if (req.method === 'OPTIONS') {
        res.set('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
        res.set('Access-Control-Allow-Headers', 'Content-Type');
        return res.status(204).end();
    }
    next();
});

app.use(express.json());

app.use(express.static('public'));

app.use('/api/tasks', taskRoutes);

// تصل أخطاء المسارات إلى هنا. إذا لم يكن متن الطلب بصيغة JSON صالحة، فهذا
// خطأ العميل (400)؛ وأي خطأ آخر فهو من جانبنا (500).
app.use((err, req, res, next) => {
    if (err.type === 'entity.parse.failed') {
        return sendError(res, 400, 'invalid_json', 'The body is not valid JSON');
    }
    console.error(err);
    sendError(res, 500, 'server_error', 'Something went wrong');
});

app.listen(3000, () => {
    console.log('Task tracker API on port 3000');
});

جميع دروس واجهات REST API

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