Khám phá Chức Năng Chính của express-http-proxy: Lọc Yêu Cầu và Ghi Đè Đường Dẫn

express-http-proxy là một middleware proxy mạnh mẽ và nhẹ dành cho các ứng dụng Express/Connect, cung cấp khả năng điều khiển luồng yêu cầu HTTP một cách linh hoạt. Hai tính năng cốt lõi giúp xây dựng các dịch vụ proxy API hiệu quả là lọc yêu cầu (request filtering) và ghi đè đường dẫn (path rewriting). Bài viết này sẽ đi sâu vào cơ chế hoạt động và cách triển khai của hai chức năng này, giúp bạn tận dụng tối đa express-http-proxy để tối ưu hóa quy trình xử lý yêu cầu.

Lọc Yêu Cầu: Kiểm Soát Chính Xác Luồng Dữ Liệu

Tính năng lọc yêu cầu cho phép bạn định nghĩa các quy tắc tùy chỉnh để quyết định liệu một yêu cầu đến có nên được xử lý tiếp bởi proxy hay không. Điều này cực kỳ hữu ích trong các tình huống như xác thực quyền truy cập, tạo danh sách trắng (whitelist) cho các API, hoặc giới hạn loại yêu cầu được phép.

1.1. Cơ Chế Hoạt Động

Trong express-http-proxy, quá trình lọc được quản lý bởi một hàm bộ lọc. Hàm này nhận vào đối tượng yêu cầu của người dùng (req) và đối tượng phản hồi (res), sau đó trả về một giá trị boolean. Nếu true, yêu cầu sẽ tiếp tục được xử lý; nếu false, yêu cầu sẽ bị từ chối.

Mô-đun chịu trách nhiệm thực hiện việc này là app/steps/filterUserRequest.js.

// Mô phỏng logic lọc yêu cầu
function kiemTraYeuCauNguoiDung(context) {
  const boLocTuyChinh = context.options.filter || macDinhBoLoc;
  return Promise
    .resolve(boLocTuyChinh(context.request.user, context.response.user))
    .then(function (duocTiepTuc) {
      if (duocTiepTuc) {
        return Promise.resolve(context);
      } else {
        return Promise.reject(new Error('Yêu cầu bị từ chối bởi bộ lọc.'));
      }
    });
}

// Hàm lọc mặc định cho phép tất cả các yêu cầu đi qua
function macDinhBoLoc(yeuCauGoc, phanHoiGoc) {
  return true;
}

1.2. Cấu Hình Thực Tế

Để thiết lập bộ lọc tùy chỉnh, bạn chỉ cần cung cấp tùy chọn filter trong cấu hình proxy:

const express = require('express');
const proxy = require('express-http-proxy');
const app = express();

app.use('/api', proxy('http://dichvu.example.com', {
  filter: function(req, res) {
    // Chỉ cho phép các yêu cầu GET đi qua proxy
    return req.method === 'GET';
  }
}));

app.listen(3000, () => console.log('Proxy đang chạy trên cổng 3000'));

Ví dụ trên minh họa cách chỉ cho phép các yêu cầu có phương thức HTTP là GET được chuyển tiếp qua proxy, giúp kiểm soát đơn giản nhưng hiệu quả các loại yêu cầu.

Ghi Đè Đường Dẫn: Điều Chỉnh URL Linh Hoạt

Chức năng ghi đè đường dẫn cho phép bạn thay đổi URL của yêu cầu trước khi nó được chuyển tiếp đến máy chủ đích. Điều này rất hữu ích trong việc quản lý phiên bản API, ánh xạ đường dẫn phức tạp, hoặc ẩn cấu trúc đường dẫn nội bộ khỏi người dùng cuối.

2.1. Cơ Chế Hoạt Động

Việc ghi đè đường dẫn được xử lý bởi một hàm phân giải đường dẫn tùy chỉnh, được cung cấp qua tùy chọn proxyReqPathResolver. Hàm này sẽ nhận đối tượng yêu cầu gốc (req) và trả về một chuỗi là đường dẫn mới mà proxy sẽ sử dụng để gửi đến máy chủ đích.

Mô-đun liên quan là app/steps/resolveProxyReqPath.js.

// Mô phỏng logic phân giải đường dẫn
function phanGiaiDuongDanProxy(context) {
  const hamPhanGiai = context.options.proxyReqPathResolver || macDinhPhanGiaiDuongDan;
  return Promise
    .resolve(hamPhanGiai(context.request.user))
    .then(function (duongDanDaPhanGiai) {
      context.proxy.requestOptions.path = duongDanDaPhanGiai;
      console.log('Đường dẫn proxy đã phân giải:', duongDanDaPhanGiai);
      return Promise.resolve(context);
    });
}

// Hàm phân giải đường dẫn mặc định: sử dụng đường dẫn gốc
function macDinhPhanGiaiDuongDan(yeuCauGoc) {
  const urlPhanTich = new URL(yeuCauGoc.url, `http://${yeuCauGoc.headers.host}`);
  return urlPhanTich.pathname;
}

2.2. Cấu Hình Thực Tế

Để ghi đè đường dẫn, bạn sử dụng tùy chọn proxyReqPathResolver:

const express = require('express');
const proxy = require('express-http-proxy');
const app = express();

app.use('/api', proxy('http://dichvu.example.com', {
  proxyReqPathResolver: function(req) {
    // Chuyển đổi đường dẫn từ /api/v1/users thành /v1/users
    // hoặc từ /api/sanpham thành /sanpham
    return req.originalUrl.replace('/api', '');
  }
}));

app.listen(3000, () => console.log('Proxy đang chạy trên cổng 3000'));

Cấu hình này sẽ biến các yêu cầu như /api/users thành /users trước khi chuyển tiếp đến máy chủ đích, giúp tách biệt giao diện công khai và cấu trúc API nội bộ.

Kết Hợp Chức Năng: Xây Dựng Luồng Xử Lý Yêu Cầu Nâng Cao

Khi kết hợp cả hai tính năng lọc yêu cầu và ghi đè đường dẫn, bạn có thể tạo ra các luồng xử lý yêu cầu phức tạp và mạnh mẽ hơn. Ví dụ, bạn có thể kiểm tra khóa API để xác thực yêu cầu, sau đó định tuyến lại đường dẫn dựa trên vai trò của người dùng.

const express = require('express');
const proxy = require('express-http-proxy');
const app = express();

// Hàm giả định để lấy vai trò người dùng (ví dụ)
function layVaiTroNguoiDung(request) {
  // Logic thực tế để xác định vai trò từ token, session, v.v.
  const apiKey = request.headers['x-api-key'];
  if (apiKey === 'admin-secret') return 'admin';
  if (apiKey === 'user-secret') return 'user';
  return 'guest'; // Mặc định
}

app.use('/protected-api', proxy('http://dichvu.example.com', {
  filter: function(req, res) {
    // Yêu cầu phải có khóa API hợp lệ
    const apiKey = req.headers['x-api-key'];
    return apiKey === 'admin-secret' || apiKey === 'user-secret';
  },
  proxyReqPathResolver: function(req) {
    const role = layVaiTroNguoiDung(req);
    let path = new URL(req.url, `http://${req.headers.host}`).pathname;
    
    // Loại bỏ tiền tố /protected-api và thêm tiền tố vai trò
    // Ví dụ: yêu cầu tới /protected-api/data sẽ được chuyển thành /api/{role}/data
    path = path.replace(/^\/protected-api/, ''); 
    return `/api/${role}${path}`;
  }
}));

app.listen(3000, () => console.log('Proxy kết hợp đang chạy trên cổng 3000'));

Trong ví dụ này, chỉ các yêu cầu có khóa API hợp lệ mới được chuyển tiếp. Sau đó, đường dẫn của yêu cầu sẽ được định tuyến lại dựa trên vai trò của người dùng (ví dụ: /api/admin/data hoặc /api/user/profile), cho phép máy chủ đích xử lý các yêu cầu một cách riêng biệt cho từng vai trò.

Thẻ: Express http-proxy middleware Node.js API Gateway

Đăng vào ngày 20 tháng 5 lúc 04:48