Menu
CoddyTech

Bộ đánh chặn

Thuộc phần TypeScript và NestJS trong hành trình Node.js của Coddy. Bài học 23 trên 24.

Bạn muốn mỗi câu trả lời đều cho biết mất bao lâu, mà không cần sửa từng handler. Một interceptor bọc quanh handler: code trước next.handle() chạy trước, còn code chạy trên kết quả trả về từ next.handle() sẽ chạy sau.

intercept(context: ExecutionContext, next: CallHandler) {
    console.log('before');
    return next.handle().pipe(
        tap(() => console.log('after')),
    );
}

next.handle() trả về kết quả của handler dưới dạng observable của RxJS, một giá trị sẽ đến sau. .pipe(tap(...)) chạy một hàm khi giá trị đến, trước khi Nest gửi giá trị đó; pipe này không liên quan đến các pipe xác thực của Nest. Guard chạy trước mọi interceptor, nên yêu cầu bị guard từ chối sẽ không bao giờ đến interceptor. Hãy trả về observable đã qua pipe: Nest gửi dữ liệu mà observable chứa. context.switchToHttp().getResponse() trả về res, và setHeader của nó thêm một header.

app.useGlobalInterceptors(...) áp dụng interceptor cho mọi route; @UseInterceptors(...) áp dụng cho một controller.

challenge icon

Thử thách

Dễ

Nút Chạy khởi động lại ứng dụng theo dõi của bạn bằng src/main.ts. Các tệp luyện tập riêng có thể vẫn nằm trong thư mục, máy chủ này không khởi động chúng.

Ứng dụng khách và công cụ giám sát của bạn cần biết mỗi yêu cầu mất bao lâu. Thêm tiêu đề phản hồi X-Response-Time vào mọi phản hồi thành công mà không chỉnh sửa bất kỳ route nào.

  1. Mở src/common/response-time.interceptor.ts. Trong phương thức intercept của ResponseTimeInterceptor, trước tiên hãy ghi lại thời gian hiện tại bằng Date.now(). Code ở đầu intercept chạy trước phương thức của route.
  2. Lấy res của Express bằng context.switchToHttp().getResponse().
  3. next.handle() chạy phương thức của route và trả về kết quả dưới dạng observable của RxJS (observable): một giá trị sẽ đến sau. Tiếp tục trả về giá trị đó, nhưng truyền nó qua tap, vốn đã được import từ rxjs, bằng phương thức .pipe() của observable. Hàm bạn truyền cho tap chạy khi kết quả đến, trước khi Nest gửi kết quả đi. Trong hàm đó, gọi res.setHeader để đặt X-Response-Time thành số mili giây kể từ lúc bạn ghi lại thời gian, theo sau là ms, chẳng hạn như 3ms. (Phương thức .pipe() của RxJS không liên quan đến các validation pipe ở chương 3. Nó nối thêm một bước vào observable.)
  4. Trong src/main.ts, đăng ký interceptor cho toàn bộ ứng dụng bằng app.useGlobalInterceptors(...), truyền vào một đối tượng ResponseTimeInterceptor mới, bên cạnh pipe và filter.
  5. Nội dung phản hồi không được thay đổi. GET /tasks vẫn trả về { "items": [...], "page": 1, "total": 3, "pages": 2 }.

Thử nhé: gửi GET /tasks rồi mở thẻ Headers của phản hồi. Bạn sẽ thấy X-Response-Time với giá trị như 0ms hoặc 2ms. GET /stats cũng có tiêu đề này, POST với { "title": "Buy milk" } được gửi kèm tiêu đề x-api-key: tasks-key-123 cũng vậy. Sau đó, gửi POST đó mà không có khóa: 403 và không có X-Response-Time. Nest chạy guard trước interceptor, nên guard đã từ chối yêu cầu trước khi interceptor của bạn bắt đầu chạy. Lỗi phát sinh bên trong route cũng khiến tiêu đề không được thêm vào, vì tap chỉ chạy khi có kết quả.

Tự làm thử

import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable, tap } from 'rxjs';

@Injectable()
export class ResponseTimeInterceptor implements NestInterceptor {
    intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
        // Ghi lại thời gian, cho handler chạy, rồi đặt X-Response-Time.
        return next.handle();
    }
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong TypeScript và NestJS

Tự luyện tập: Playground Express