انتقل إلى المحتوى
التوثيق بالعربية

هذه ترجمة للصفحة الأصلية بالإنجليزية. عند وجود أي اختلاف، تُعتمد النسخة الإنجليزية.

الأخطاء وproblem+json

الأخطاء في DaloyJS مواطن من الدرجة الأولى. كل HttpError يُرمى يُسلسل إلى problem+json وفق RFC 9457 مع URI ثابت في type، ومعرّف طلب، وترويسة Content-Type المناسبة.

من الخطأ المرمي إلى problem+json
  1. 01المعالجthrow new NotFoundError(...)أي صنف فرعي من HttpError
  2. 02الإطارالتحويل إلى حقول RFC 9457type · title · status · detail
  3. 03الإنتاجتنقيح detail في 5xxيظهر فقط عندما تكون البيئة development أو test
  4. 04الشبكةالاستجابة المُسلسلةapplication/problem+json + x-request-id
يصبح HttpError المرمي مستند problem+json تلقائيًا. وما لم تكن البيئة development أو test صراحةً، يُنقَّح الحقل detail في استجابات 5xx حتى لا تصل تتبعات المكدس ومقاطع SQL إلى العميل أبدًا، بينما يظل الخطأ الكامل يصل إلى خطاف onError لديك.

أصناف الأخطاء المدمجة

ts
import {
  HttpError,                         // الصنف الأساسي: أي حالة، مع type/title/headers مخصصة
  BadRequestError,                   // 400
  UnauthorizedError,                 // 401 + cache-control: no-store
  ForbiddenError,                    // 403 + cache-control: no-store
  NotFoundError,                     // 404
  MethodNotAllowedError,             // 405 + ترويسة Allow
  RequestTimeoutError,               // 408 / 503 / 504 + cache-control: no-store
  ConflictError,                     // 409 + cache-control: no-store
  PayloadTooLargeError,              // 413
  UnsupportedMediaTypeError,         // 415
  ValidationError,                   // 422
  TooManyRequestsError,              // 429 + Retry-After + cache-control: no-store
  RequestHeaderFieldsTooLargeError,  // 431
  InternalError,                     // 500 (يُنقَّح detail خارج dev/test)
  ServiceUnavailableError,           // 503 + Retry-After + cache-control: no-store
} from "@daloyjs/core";

يتحقق TooManyRequestsError وServiceUnavailableError من retryAfterSeconds بالطريقة نفسها: أي قيمة ليست عددًا صحيحًا آمنًا غير سالب ترمي RangeError عند الإنشاء، فلا تتحول قيمة NaN الناتجة عن متغير بيئة مفقود أبدًا إلى ترويسة Retry-After غير صالحة. ومنذ 1.16.0 يكون RequestTimeoutError (مهلة requestTimeoutMs، راجع requestTimeoutStatus (بالإنجليزية)) بالقيمة no-store في كل صيغ الحالة، حتى لا تستمر ذاكرة تخزين مؤقت مشتركة في تقديم 503/504 بعد تعافي الخادم الخلفي. ويحصل new HttpError(status, …) المجرد على type للمشكلة بالصيغة https://httpstatuses.io/<status>؛ أما الأصناف المدمجة فتحمل عناوين URI ثابتة بالصيغة https://daloyjs.dev/errors/<slug>.

الرمي داخل معالج

ts
import { NotFoundError } from "@daloyjs/core";

app.get(
  "/users/:id",
  {
    operationId: "getUser",
    responses: { 200: { description: "ok" }, 404: { description: "missing" } },
  },
  async ({ params }) => {
    const user = await db.find(params.id);
    if (!user) throw new NotFoundError(`user ${params.id} not found`);
    return { status: 200, body: user };
  },
);

صيغة الإرسال

يُعاد معرّف الطلب إلى العميل في موضعين: ترويسة الاستجابة x-request-id، والحقل instance في مستند المشكلة (وفق RFC 9457 §3.1) على هيئة URN بالصيغة urn:request:<uuid>. لا توجد خاصية requestId في المستوى الأعلى، لذا ينبغي للعملاء قراءة الترويسة أو تحليل الـ URN من instance.

json
HTTP/1.1 404 Not Found
content-type: application/problem+json
x-request-id: c9aa8e1c-7a6e-4f1e-9f44-c2e5d2c4a431

{
  "type": "https://daloyjs.dev/errors/not-found",
  "title": "Not Found",
  "status": 404,
  "detail": "user 42 not found",
  "instance": "urn:request:c9aa8e1c-7a6e-4f1e-9f44-c2e5d2c4a431"
}

التنقيح في الإنتاج

يحذف DaloyJS الحقل detail من أي استجابة 5xx ما لم تكن البيئة development أو test بشكل مؤكد، حتى لا تتسرب رسائل الأخطاء الداخلية ومقاطع SQL إلى العملاء. ويظل الخطأ الكامل يُرسل إلى أداة التسجيل لديك وإلى خطاف onError.

الإعدادdetail في 5xx
env: "development" أو env: "test"ظاهر
دون env، وNODE_ENV=development أو testظاهر
دون env، وNODE_ENV غير مضبوط أو فارغ أو أي قيمة أخرىمنقّح
env: "production" أو production: true أو NODE_ENV=productionمنقّح
production: false مع NODE_ENV غير مضبوطمنقّح

البيئة غير المضبوطة تفشل بشكل مغلق عن قصد. فبيئات الحافة مثل Cloudflare Workers وDeno Deploy لا تملك NODE_ENV، ومعاملة ذلك كبيئة تطوير سترسل رسائل الاستثناءات إلى العملاء. يضبط daloy dev القيمة NODE_ENV=development لعمليته الفرعية إن لم تضبطها أنت، لذا يظل التطوير المحلي يعرض التفاصيل. أما الأخطاء التي تسلسلها بنفسك (مثل جسم استجابة مبني من err.message داخل كتلة catch) فلا تُنقَّح. ارمِها بدلًا من ذلك، أو ارمِ HttpError مع detail آمن.

أصناف أخطاء مخصصة

ts
import { HttpError } from "@daloyjs/core";

export class QuotaExceededError extends HttpError {
  constructor(resource: string) {
    super(429, {
      title: "Quota exceeded",
      type: "https://api.example.com/errors/quota-exceeded",
      detail: `Quota exceeded for ${resource}`,
    });
  }
}

تضبط كل الأصناف المدمجة أعلاه type بالصيغة https://daloyjs.dev/errors/<slug>. أما HttpError العادي المرمي دون type فيعود إلى https://httpstatuses.io/<status>. والبرمجيات الوسيطة الرسمية التي تجيب بـ 503 (loadShedding() وconcurrencyLimit() وrequestTimeoutStatus: 503) تضبط type خاصًا بها على daloyjs.dev، لذا لا تنطبق القيمة البديلة إلا على الأخطاء التي تنشئها بنفسك. اضبط type عندما يعتمد عليه العملاء، أو استخدم صنفًا مدمجًا:

ts
throw new HttpError(503, { title: "Service Unavailable" });
// "type": "https://httpstatuses.io/503"

throw new HttpError(503, {
  title: "Service Unavailable",
  type: "https://api.example.com/errors/service-unavailable",
});

الخدمة غير متاحة

لوضع الصيانة، أو تعطل اعتمادية، أو نسخة قيد التصريف، ارمِ ServiceUnavailableError. فهو يجيب بـ 503 مع type ثابت هو https://daloyjs.dev/errors/service-unavailable، وRetry-After اختياري، وcache-control: no-store، حتى لا تستمر شبكة CDN في تقديم صفحة التعطل بعد تعافيك.

ts
import { ServiceUnavailableError } from "@daloyjs/core";

if (await maintenance.isOn()) {
  // Retry-After: 120. يُنقَّح detail خارج development/test،
  // لذا فالترويسة هي ما يستطيع العميل فعلًا التصرف بناءً عليه.
  throw new ServiceUnavailableError(120, "scheduled database maintenance");
}

يجب أن يكون retryAfterSeconds عددًا صحيحًا غير سالب. أي قيمة أخرى (NaN من متغير بيئة مفقود، أو -1، أو 1.5) ترمي RangeError بدلًا من إرسال ترويسة Retry-After مشوّهة.

onError مخصص

ts
app.use({
  onError: async (error, ctx) => {
    logger.error({ err: error, requestId: ctx?.state.requestId }, "request failed");
    // أعد Response للاستبدال؛ وإلا يسلسل DaloyJS استجابة problem+json
  },
});

آخر تحديث: