middleware
middleware.ts: إعادة التوجيه وإعادة الكتابة والترويسات وملفات تعريف الارتباط قبل كل طلب، باستخدام config.matcher.
يشغّل middleware الشيفرة قبل اكتمال الطلب. ويمكنه إعادة التوجيه، وإعادة الكتابة، وتعيين الترويسات وملفات تعريف الارتباط، أو إرجاع استجابة بنفسه.
اصطلاح التسمية
أنشئ middleware.ts في جذر المشروع (أو src/)، بجوار app/. صدّر دالة middleware (أو تصديرًا افتراضيًا) وكائن config اختياريًا.
import { NextResponse, type NextRequest } from "@textjs/app/server";export function middleware(request: NextRequest) { if (!request.cookies.has("session")) { return NextResponse.redirect(new URL("/login", request.url)); } return NextResponse.next();}export const config = { matcher: ["/dashboard/:path*", "/api/:path*"],};يعمل middleware قبل معالجات المسارات والملفات الثابتة وصفحات وحدة التحكم، عند تشغيل text dev و text start ومع كل محوّل نشر للخادم. ويستخدم واجهتي الويب Request و Response، لذا يعمل الملف نفسه على Node و Bun و Vercel Edge و Cloudflare Workers.
مطابقة المسارات
بدون config.matcher، يعمل middleware لكل طلب. تستخدم أنماط المطابقة الصياغة نفسها المستخدمة في Next.js:
| نمط المطابقة | المسارات المطابقة |
|---|---|
/about | /about و /about/ |
/blog/:slug | /blog/hello |
/docs/:path* | /docs، /docs/a/b |
/a/:rest+ | /a/b، وليس /a |
/user/:id(\\d+) | /user/42، وليس /user/ada |
/((?!api/).*) | كل شيء باستثناء /api/* |
أضف شروطًا باستخدام has و missing (ترويسة أو ملف تعريف ارتباط أو استعلام أو مضيف):
export const config = { matcher: [{ source: "/beta/:path*", has: [{ type: "cookie", key: "beta", value: "1" }] }],};تنطبق أنماط المطابقة على اسم المسار دون basePath. في text dev، لا تشغّل طلبات الوحدات الخاصة بأداة Vite (/@vite/* وملفات المصدر) middleware مطلقًا.
يحدّد كل مضيف اسم مسار موحّدًا قبل تشغيل middleware، ويستخدمه للمطابقة و request.nextUrl والبحث عن المسار والمعالج والملف. تُفك ترميزات النسبة المئوية ويُعاد ترميز المسار بالطريقة التي يكتبه بها new URL()، وتُحل المقاطع . و ..، وتُزال الشرطات المائلة المتكررة والختامية. يصل /ad%6din//users/ إلى middleware بصيغة /admin/users، بينما يبقى /café بصيغة /caf%C3%A9. يطابق النمط المكتوب بشرطة مائلة ختامية، مثل /admin/، المسار /admin. تتلقى الطلبات التي يحتوي مسارها على شرطة مائلة أو شرطة مائلة عكسية مرمّزة (%2F، %5C)، أو محرف تحكم أو ترميز نسبة مئوية غير صالح، الحالة 400. تراعي المسارات حالة الأحرف: لا يطابق /Admin المسار /admin، ولا تُقدّم الملفات الثابتة إلا بأسمائها المطابقة تمامًا.
طابق عناوين URL التي يُقدّم الملف من خلالها، لا اسم الملف وحده. تقدّم Cloudflare Pages الملف report.html على /report أيضًا، لذا لا يحمي نمط مطابقة /report.html المسار /report. أدرج كليهما، matcher: ["/report", "/report.html"]، أو احتفظ بالملفات المحمية في دليل وطابقه، مثل /reports/:path* للملفات في public/reports/.
NextResponse
| واجهة API | التأثير |
|---|---|
NextResponse.next() | المتابعة إلى المسار |
NextResponse.redirect(url, status?) | 307 افتراضيًا؛ يجب أن يكون url مطلقًا |
NextResponse.rewrite(url) | تقديم مسار آخر؛ تمرير الطلبات إلى الأصول الخارجية عبر وكيل |
NextResponse.json(body, init?) | الاستجابة بصيغة JSON |
new Response(...) | الاستجابة بأي محتوى |
لا تُرجع شيئًا للمتابعة دون تغيير. تقبل next() و rewrite() الكائن { status } لتغيير حالة الاستجابة النهائية. لا تتحكم في التوجيه إلا الاستجابات المنشأة باستخدام NextResponse.next() / NextResponse.rewrite(): تُرسل أي استجابة Response أخرى، بما فيها تلك التي تُرجعها fetch()، إلى العميل كما هي مع إزالة ترويسات x-middleware-* منها.
يستثني request.nextUrl القيمة basePath من pathname، بينما تتضمنها خاصية href والدالة toString(). للبقاء داخل التطبيق، انسخه: const url = request.nextUrl.clone(); url.pathname = "/login"; return NextResponse.redirect(url);.
export function middleware(request: NextRequest) { if (request.nextUrl.pathname === "/status") { return NextResponse.rewrite(new URL("/api/health", request.url)); }}الترويسات
مرّر request.headers لتغيير ما تتلقاه المعالجات اللاحقة، و headers لإضافة ترويسات الاستجابة:
export function middleware(request: NextRequest) { const headers = new Headers(request.headers); headers.set("x-user", "ada"); const response = NextResponse.next({ request: { headers } }); response.headers.set("x-frame", "▼"); return response;}تُضاف ترويسات استجابة middleware إلى الاستجابة النهائية ما لم يعيّن معالج المسار الترويسة نفسها.
ملفات تعريف الارتباط
يقرأ request.cookies ترويسة Cookie؛ ويكتب response.cookies ترويسة Set-Cookie.
export function middleware(request: NextRequest) { const theme = request.cookies.get("theme")?.value ?? "geistDark"; const response = NextResponse.next(); response.cookies.set("theme", theme, { httpOnly: true, sameSite: "lax", maxAge: 60 * 60 * 24 }); return response;}تكون ملفات تعريف الارتباط المعيّنة في استجابة next() أو rewrite() متاحة أيضًا لمعالج المسار في الطلب نفسه.
العمل في الخلفية
تتضمن الوسيطة الثانية waitUntil()، التي تُبقي بيئات التشغيل بلا خادم نشطة بعد إرسال الاستجابة:
import type { NextFetchEvent, NextRequest } from "@textjs/app/server";export function middleware(request: NextRequest, event: NextFetchEvent) { event.waitUntil(fetch("https://analytics.example.com", { method: "POST", body: request.url }));}ملاحظات
- تُعرض الصفحات المعروضة على العميل في المتصفح، لذا تغيّر إعادة الكتابة إلى صفحة أخرى الملف الذي يُقدّم، لا المسار الذي يعرضه تطبيق وحدة التحكم. تعمل إعادة الكتابة إلى معالجات المسارات والملفات الثابتة والأصول الخارجية كما هو متوقع.
- يستطيع middleware قراءة جسم الطلب؛ وتظل معالجات المسارات تتلقاه. لا يُمس جسم الطلبات التي يستثنيها نمط المطابقة مطلقًا.
- يتضمن
request.urlدائمًا أصل الخادم: تُوحّد أهداف الطلبات مثل//other.host/xلتبدأ بشرطة مائلة واحدة. - لا يستطيع المحوّل الثابت تشغيل middleware.