عنوان:

‫Cloudflare Workers برای توسعه‌دهندگان ASP.NET Core؛ بخش دوم: ورکرها به‌عنوان یک لایه میانی


نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۰۵/۲۲ ۰۸:۱۰
آدرس: www.dntips.ir
موضوع: Cloudflare Workers به‌عنوان یک لایه میانی (Middleware) برای ASP.NET Core

مقدمه: در بخش قبل با اصول اولیه Workers آشنا شدیم. در این بخش، به یکی از قدرتمندترین و پرکاربردترین الگوهای استفاده از Workers می‌پردازیم: استفاده از آن به عنوان یک "Middleware در لبه شبکه". شما به خوبی با مفهوم Middleware در ASP.NET Core آشنا هستید؛ یک سری کامپوننت که در خط لوله پردازش درخواست (Request Pipeline) قرار می‌گیرند. حالا تصور کنید که این خط لوله را قبل از رسیدن درخواست به سرور اصلی‌تان، در سراسر جهان، مستقر کنید.

درس ۱: مقایسه Cloudflare Workers با Middleware در ASP.NET Core

۱.۱- یادآوری سریع: Middleware در ASP.NET Core
در ASP.NET Core، خط لوله پردازش درخواست به این شکل است:
Request -> Middleware 1 -> Middleware 2 -> ... -> Endpoint (Controller/Minimal API) -> ... -> Middleware 2 -> Middleware 1 -> Response
این Middlewareها روی همان سروری اجرا می‌شوند که اپلیکیشن شما روی آن قرار دارد (مثلاً Kestrel یا IIS) و به تمام سرویس‌های برنامه (مانند Dependency Injection, DbContext) دسترسی دارند.

۱.۲- مدل جدید: Middleware در لبه شبکه (Edge Middleware)
وقتی یک Worker را معرفی می‌کنیم، خط لوله به این شکل تغییر می‌کند:
User Request -> Cloudflare Edge (Worker) -> [Internet] -> Your ASP.NET Core Server (Origin) -> [ASP.NET Core Middleware Pipeline]
Worker اولین خط دفاع و پردازش است.

۱.۳- جدول مقایسه:
ویژگیMiddleware در ASP.NET CoreCloudflare Workers (به عنوان Middleware)
محل اجراروی سرور اصلی (Origin)شبکه جهانی کلادفلر (نزدیک به کاربر)
زبان و محیطC# / .NET RuntimeJavaScript, TypeScript / V8 Isolates
تأثیر بر عملکردپردازش آن به زمان پاسخ سرور اضافه می‌کندبا پردازش زود هنگام، زمان پاسخ نهایی را کاهش می‌دهد
حوزه تأثیرمحدود به همان اپلیکیشنمی‌تواند روی یک دامنه کامل (شامل چندین اپلیکیشن) اعمال شود
دسترسی‌هادسترسی کامل به DI، دیتابیس و منطق برنامهدسترسی به سرویس‌های Edge (KV, R2) و APIهای خارجی
هزینههزینه CPU و RAM سرور اصلی شما را مصرف می‌کندهزینه بر اساس تعداد درخواست‌ها محاسبه می‌شود

۱.۴- چه زمانی از کدام استفاده کنیم؟
از Middleware خود ASP.NET Core استفاده کنید وقتی:
  • منطق شما نیاز به دسترسی مستقیم به سرویس‌های ثبت شده در DI دارد (مثلاً DbContext, ILogger).
  • در حال پیاده‌سازی منطق اصلی و پیچیده کسب‌وکار (Business Logic) هستید.

از Cloudflare Worker به عنوان Middleware استفاده کنید وقتی:
  • می‌خواهید درخواست‌های نامعتبر یا مخرب را قبل از رسیدن به سرور اصلی‌تان فیلتر کنید (امنیت).
  • منطق شما عمومی است و می‌تواند برای چندین سرویس به کار رود (مانند مدیریت CORS).
  • می‌خواهید پاسخ‌ها را کش کنید تا بار روی سرور اصلی کاهش یابد (عملکرد).
  • نیاز به اجرای منطق بر اساس موقعیت جغرافیایی کاربر دارید.


درس ۲: پیاده‌سازی الگوهای رایج Middleware در Edge

حالا بیایید چند سناریوی عملی را پیاده‌سازی کنیم.

۲.۱- سناریوی اول: احراز هویت با JWT در Edge
در ASP.NET Core: شما از پکیج Microsoft.AspNetCore.Authentication.JwtBearer استفاده کرده و با افزودن [Authorize] روی کنترلرها، توکن JWT را روی سرور خودتان اعتبارسنجی می‌کنید.
چرا در Edge؟ چرا باید منابع سرور (CPU و Memory) را برای اعتبارسنجی توکنی که از ابتدا نامعتبر یا منقضی شده است، هدر دهیم؟ با اعتبارسنجی توکن در Edge، فقط درخواست‌های معتبر به سرور ASP.NET Core شما می‌رسند.

نحوه پیاده‌سازی:
نصب یک کتابخانه کمکی: اعتبارسنجی JWT به صورت دستی پیچیده است. از یک کتابخانه معتبر مانند jose استفاده می‌کنیم:
npm install jose
ذخیره کردن کلید امن (Secret Key): هرگز کلید امن را در کد هاردکد نکنید! از Wrangler Secrets استفاده کنید که معادل secrets.json یا Azure Key Vault در دنیای .NET است.
wrangler secret put JWT_SECRET
# سپس کلید خود را paste کنید
این متغیر در آبجکت env در دسترس خواهد بود.

کد Worker:
import { jwtVerify } from 'jose';

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // ۱. توکن را از هدر استخراج کن
    const authHeader = request.headers.get('Authorization');
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
      return new Response('Missing or invalid Authorization header', { status: 401 });
    }
    const token = authHeader.substring(7);

    try {
      // ۲. کلید امن را به فرمت صحیح تبدیل کن
      const secret = new TextEncoder().encode(env.JWT_SECRET);

      // ۳. توکن را اعتبارسنجی کن
      await jwtVerify(token, secret);

      // ۴. اگر معتبر بود، درخواست را به سرور اصلی بفرست
      return fetch(request); // Pass through to the origin server

    } catch (error) {
      // ۵. اگر نامعتبر بود، دسترسی را مسدود کن
      return new Response('Invalid token', { status: 401 });
    }
  },
};

interface Env {
    JWT_SECRET: string;
}

۲.۲- سناریوی دوم: مدیریت CORS در Edge
در ASP.NET Core: شما app.UseCors() را در Program.cs فراخوانی کرده و پالیسی‌های مختلفی را تعریف می‌کنید.
چرا در Edge؟
  • سادگی: می‌توانید یک قانون CORS را برای تمام APIها و سرویس‌های تحت یک دامنه، در یک مکان واحد، مدیریت کنید.
  • عملکرد: درخواست‌های OPTIONS (که به pre-flight request معروفند) در Edge پاسخ داده می‌شوند و هرگز به سرور شما نمی‌رسند و بار اضافی ایجاد نمی‌کنند.

نحوه پیاده‌سازی:
export default {
  async fetch(request: Request): Promise<Response> {
    // لیست دامنه‌های مجاز
    const allowedOrigins = ['https://my-frontend-app.com', 'http://localhost:3000'];
    const origin = request.headers.get('Origin');

    // اگر دامنه درخواست‌کننده مجاز است، هدرها را تنظیم می‌کنیم
    const corsHeaders: { [key: string]: string } = {
      'Access-Control-Allow-Methods': 'GET,HEAD,POST,OPTIONS',
      'Access-Control-Max-Age': '86400',
      'Access-Control-Allow-Headers': request.headers.get('Access-Control-Request-Headers') || '',
    };
    if (origin && allowedOrigins.includes(origin)) {
        corsHeaders['Access-Control-Allow-Origin'] = origin;
    }

    // ۱. به درخواست‌های pre-flight در Edge پاسخ بده
    if (request.method === 'OPTIONS') {
      return new Response(null, { headers: corsHeaders });
    }

    // ۲. برای سایر درخواست‌ها، ابتدا به سرور اصلی برو
    const response = await fetch(request);

    // ۳. یک کپی قابل تغییر از پاسخ بساز
    const mutableResponse = new Response(response.body, response);

    // ۴. هدرهای CORS را به پاسخ نهایی اضافه کن
    for (const key in corsHeaders) {
        mutableResponse.headers.set(key, corsHeaders[key]);
    }

    return mutableResponse;
  },
};

۲.۳- سناریوی سوم: A/B Testing
در ASP.NET Core: پیاده‌سازی A/B Testing می‌تواند پیچیده باشد و نیاز به کتابخانه‌های Feature Flagging و تغییرات در کد برنامه دارد.
چرا در Edge؟ بسیار ساده‌تر است و کاملاً از کد برنامه شما جداست. می‌توانید دو نسخه کاملاً متفاوت از یک اپلیکیشن (مثلاً دو سرور مختلف) را بدون اینکه خودشان خبر داشته باشند، تست کنید.

نحوه پیاده‌سازی:
در این مثال، ۵۰٪ ترافیک را به نسخه جدید (B) و ۵۰٪ را به نسخه قدیمی (A) می‌فرستیم.
export default {
  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url);
    const versionA_Host = 'api-stable.mydomain.com';
    const versionB_Host = 'api-beta.mydomain.com';

    // بر اساس یک معیار (مثلاً شانس)، هاست مقصد را انتخاب کن
    if (Math.random() < 0.5) {
      url.hostname = versionA_Host;
    } else {
      url.hostname = versionB_Host;
    }

    // درخواست را با هاست جدید به مقصد بفرست
    return fetch(new Request(url, request));
  },
};

۲.۴- سناریوی چهارم: تغییر درخواست‌ها و پاسخ‌ها
چرا در Edge؟ برای افزودن یا حذف هدرها، افزودن اطلاعات جغرافیایی به درخواست یا پاک‌سازی پاسخ‌ها بدون دستکاری کد اصلی.
مثال: افزودن موقعیت جغرافیایی کاربر به هدرهای درخواست
کلادفلر به صورت خودکار اطلاعات جغرافیایی را در یک آبجکت به نام request.cf قرار می‌دهد.
export default {
  async fetch(request: Request): Promise<Response> {
    // یک کپی قابل تغییر از هدرها بساز
    const newHeaders = new Headers(request.headers);

    // اطلاعات کشور کاربر را به هدرها اضافه کن
    if (request.cf && request.cf.country) {
      newHeaders.set('X-User-Country', request.cf.country as string);
    }
    
    // یک درخواست جدید با هدرهای تغییر یافته بساز و به سرور اصلی بفرست
    const newRequest = new Request(request.url, {
        ...request,
        headers: newHeaders,
    });

    return fetch(newRequest);
  },
};
حالا در کنترلر ASP.NET Core خود، می‌توانید به سادگی این هدر (X-User-Country) را بخوانید و از آن استفاده کنید.

جمع‌بندی بخش دوم:
شما اکنون می‌دانید که Cloudflare Workers فقط یک ابزار جانبی نیست، بلکه می‌تواند یک بخش کلیدی از معماری برنامه شما باشد. با انتقال منطق‌هایی مانند احراز هویت، CORS و مسیریابی به Edge، می‌توانید اپلیکیشن‌های ASP.NET Core سریع‌تر، امن‌تر و مقیاس‌پذیرتری بسازید. در بخش بعدی، یاد می‌گیریم که چگونه در Edge داده‌ها را ذخیره و مدیریت کنیم.