Menu
CoddyTech

Quản lý phiên bản

Thuộc phần REST API trong hành trình Node.js của Coddy. Bài học 22 trên 25.

Thay đổi hình dạng của câu trả lời sẽ làm hỏng mọi ứng dụng khách đang sử dụng nó. Thêm phiên bản vào đường dẫn giúp các ứng dụng khách cũ và mới hoạt động song song: /api/v1/orders hiện tại, /api/v2/orders khi hình dạng thay đổi. Khi đường dẫn thay đổi, đường dẫn cũ sẽ chuyển hướng đến đường dẫn mới:

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

301 và 302 cho phép ứng dụng khách gửi lại yêu cầu POST dưới dạng GET, làm mất phần thân yêu cầu. 308 có nghĩa là “gửi lại cùng yêu cầu đến đó”, bao gồm cả phương thức và phần thân. Bên trong middleware được gắn vào ứng dụng, req.url không còn phần đường dẫn gắn middleware, còn req.originalUrl giữ lại toàn bộ đường dẫn:

// trong app.use('/api/orders', ...) với GET /api/orders/5?full=1
req.url          // '/5?full=1'
req.originalUrl  // '/api/orders/5?full=1'
challenge icon

Thử thách

Dễ

API của bạn sẽ thay đổi theo những cách mà các client cũ không mong đợi, vì vậy hãy thêm số phiên bản vào các đường dẫn. API hiện tại trở thành phiên bản 1 tại /api/v1/tasks, còn các địa chỉ cũ sẽ chuyển client đến đó.

  1. Trong server.js, gắn bộ định tuyến tasks tại /api/v1/tasks thay vì /api/tasks.
  2. Cũng trong server.js, thêm middleware gắn tại /api/tasks chỉ làm nhiệm vụ chuyển hướng. Middleware trả về trạng thái 308 đến cùng địa chỉ dưới /api/v1, giữ nguyên phần còn lại của đường dẫn và chuỗi truy vấn. Bên trong middleware đã gắn, req.originalUrl chứa toàn bộ địa chỉ cũ. Ví dụ:
    • GET /api/tasks/2 chuyển hướng đến /api/v1/tasks/2.
    • POST /api/tasks chuyển hướng đến /api/v1/tasks.
    • GET /api/tasks?done=false&sort=title chuyển hướng đến /api/v1/tasks?done=false&sort=title.

    Dùng 308, không dùng 301: sau phản hồi 308, client gửi lại yêu cầu POST dưới dạng POST, bao gồm cả phần thân.

  3. Trong routes/tasks.js, tuyến POST đặt Location thành địa chỉ mới, ví dụ /api/v1/tasks/4 cho nhiệm vụ 4.

Thử ngay: GET /api/tasks/2 trả về 308, và thẻ Headers hiển thị Location: /api/v1/tasks/2. Thanh yêu cầu hiển thị chính phản hồi chuyển hướng, chứ không tự đi theo chuyển hướng đó. Gửi POST { "title": "Order new chairs" } đến /api/tasks: bạn nhận được 308 với Location: /api/v1/tasks, và không có nhiệm vụ nào được tạo. Gửi POST cùng phần thân đó đến /api/v1/tasks: nhận 201 với Location: /api/v1/tasks/4. GET /api/v1/tasks/2 trả về nhiệm vụ 2.

Tự làm thử

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();

// Cho phép các trang trên BOARD_ORIGIN gọi API này từ trình duyệt.
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);

// Lỗi từ các tuyến được xử lý tại đây. Phần thân không phải JSON hợp lệ là
// lỗi của máy khách (400); các lỗi còn lại thuộc về chúng ta (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');
});

Tất cả bài học trong REST API

Tự luyện tập: Playground Express