Menu
CoddyTech

المعترضات

جزء من قسم TypeScript وNestJS في رحلة Node.js على Coddy. الدرس 23 من 24.

تريد أن توضّح كل إجابة المدة التي استغرقتها، من دون تعديل كل معالج. يغلّف المعترض المعالج: يُنفَّذ الرمز البرمجي قبل next.handle() قبله، ويُنفَّذ الرمز البرمجي على ما تُعيده next.handle() بعده.

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

تعيد next.handle() نتيجة المعالج على هيئة قيمة قابلة للملاحظة من RxJS، وهي قيمة تصل لاحقًا. تنفّذ .pipe(tap(...)) دالةً عند وصول القيمة، قبل أن يرسلها Nest؛ ولا علاقة لهذه pipe بأنابيب التحقق في Nest. تعمل الحراس قبل أي معترض، لذا فإن الطلب الذي يرفضه أحد الحراس لا يصل إليه أبدًا. أَعِد القيمة القابلة للملاحظة التي مرّت عبر الأنبوب: يرسل Nest ما تحمله. تعيد context.switchToHttp().getResponse() القيمة res، التي تضيف ترويسةً باستخدام setHeader.

يطبّق app.useGlobalInterceptors(...) معترضًا على كل مسار؛ أما @UseInterceptors(...) فيطبّقه على متحكّم واحد.

challenge icon

التحدي

سهل

يبدأ Run متتبّعك من جديد باستخدام src/main.ts. يمكن أن تبقى ملفات التدريب المنفصلة في شجرة الملفات؛ فهذا الخادم لا يبدأ تشغيلها.

يريد العملاء وأدوات المراقبة الخاصة بك معرفة المدة التي استغرقها كل طلب. أضف ترويسة استجابة، X-Response-Time، إلى كل استجابة ناجحة، من دون تغيير أي مسار.

  1. افتح src/common/response-time.interceptor.ts. في الطريقة intercept ضمن ResponseTimeInterceptor، سجّل الوقت الحالي أولًا باستخدام Date.now(). يُنفَّذ الرمز في أعلى intercept قبل طريقة المسار.
  2. احصل على res الخاص بـ Express باستخدام context.switchToHttp().getResponse().
  3. تنفّذ next.handle() طريقة المسار، وتعيد نتيجتها على هيئة Observable من RxJS: قيمة تصل لاحقًا. واصل إعادتها، لكن مرّرها عبر tap، المستوردة بالفعل من rxjs، باستخدام طريقة Observable المسماة .pipe(). تُنفّذ الدالة التي تمرّرها إلى tap عند وصول النتيجة، قبل أن يرسلها Nest. استدعِ في تلك الدالة res.setHeader لضبط X-Response-Time على عدد المللي ثانية المنقضية منذ تسجيلك للوقت، متبوعًا بـ ms، مثل 3ms. (لا علاقة لـ .pipe() في RxJS بأنابيب التحقق في الفصل 3؛ فهي تربط خطوةً بـ Observable.)
  4. في src/main.ts، سجّل المعترض للتطبيق بأكمله باستخدام app.useGlobalInterceptors(...)، ومرّر إليه كائنًا جديدًا من ResponseTimeInterceptor، بجوار الأنبوب والمرشّح.
  5. يجب ألا تتغير أجسام الاستجابات. يظل GET /tasks يعيد { "items": [...], "page": 1, "total": 3, "pages": 2 }.

جرّب ذلك: أرسل GET /tasks وافتح علامة تبويب Headers في الاستجابة. سترى X-Response-Time مع قيمة مثل 0ms أو 2ms. يظهر أيضًا مع GET /stats، وكذلك مع طلب POST يتضمن { "title": "Buy milk" } ويُرسل مع الترويسة x-api-key: tasks-key-123. ثم أرسل طلب POST هذا من دون المفتاح: ستحصل على 403، ولن تظهر X-Response-Time. يشغّل Nest الحراس قبل المعترضات، لذا رفض الحارس الطلب قبل أن يبدأ المعترض عمله. كما لن تظهر الترويسة إذا حدث خطأ داخل أحد المسارات، لأن tap لا يُنفّذ إلا عند وصول نتيجة.

جرّب بنفسك

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> {
        // سجّل الوقت، ودع المعالج يعمل، ثم اضبط X-Response-Time.
        return next.handle();
    }
}
quiz iconاختبر نفسك

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

جميع دروس TypeScript وNestJS

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