در این مقاله تخصصی، قصد داریم به مقایسه عمیق و فنی دو پیادهسازی متفاوت از این میدلور بپردازیم؛ یکی مبتنی بر تولید پاسخ مستقیم HTML به همراه گارد حفاظتی نرخدهی (Rate Limiting) و دیگری مبتنی بر ریدایرکت سمت کاربر به صفحه خطا (/error).
در تکه کد اول، میدلور به شکل زیر پیادهسازی شده است:
app.UseStatusCodePages(async context =>
{
var response = context.HttpContext.Response;
// never redirect rate-limit rejections (redirecting them loops while limited)
if (response.StatusCode == StatusCodes.Status429TooManyRequests ||
response.StatusCode.StatusCodes.Status503ServiceUnavailable) // (نکته: در کد اصلی StatusCodes.Status503ServiceUnavailable است)
return;
if (response.StatusCode >= 400 && !response.HasStarted)
{
response.ContentType = "text/html; charset=utf-8";
await response.WriteAsync(
"<!doctype html><html lang=\"fa\" dir=\"rtl\"><body style=\"font-family:tahoma;text-align:center;padding-top:80px\">" +
$"<h1>{response.StatusCode}</h1><p>این صفحه در دسترس نیست.</p>" +
"<a href=\"/\">برگشت به سایت</a></body></html>");
}
});
ویژگیها و نقاط کلیدی تکه کد اول:
بررسی استثناهای نرخدهی (Rate Limiting) و سرویسدهی (429 و 503):
خطای 429 Too Many Requests زمانی رخ میدهد که کلاینت بیش از حد مجاز درخواست فرستاده است. اگر این خطا رخ دهد و سرور بخواهد ترافیک را پردازش یا هدایت کند، ممکن است خود مکانیسم خطا باعث بار اضافه یا حلقه شود.
خطای 503 Service Unavailable نشاندهنده بیشبارگذاری سرور یا حالت نگهداری (Maintenance) است. در این حالت، سرور تحت فشار شدید است و نباید منابع خود را صرف رندر کردن صفحات پیچیده یا ریدایرکتهای متعدد کند.
با دستور return; در این شرط، میدلور از مداخله در این کدهای خاص خودداری میکند و اجازه میدهد پاسخ پیشفرض سرور (یا بدنه خالی) ارسال شود.
بررسی شرط !response.HasStarted:
این شرط یکی از ایمنیهای حیاتی در ASP.NET Core است. اگر هدرهای پاسخ یا بخشی از بدنه قبلاً به سمت کلاینت ارسال شده باشند (HasStarted == true)، تلاش برای تغییر ContentType یا نوشتن متن جدید باعث بروز استثنای معروف InvalidOperationException: The response has already started میشود. این شرط از کرش کردن اپلیکیشن جلوگیری میکند.
تولید پاسخ کاملاً مستقل (Inline HTML):
پاسخ مستقیماً در استریم خروجی نوشته میشود. هیچ وابستگی به کنترلرها، مسیرها (Routing) یا موتورهای رندرینگ (مثل Razor Views) وجود ندارد. این روش بسیار سریع، سبک و امن در برابر خطاهای ثانویه (Cascading Errors) است.
در تکه کد دوم، رویکرد به شکل زیر است:
app.UseStatusCodePages(async context =>
{
var response = context.HttpContext.Response;
if (response.StatusCode >= 400 && !response.HasStarted)
{
//response.ContentType = "text/html; charset=utf-8";
//await response.WriteAsync(...);
response.Redirect("/error");
}
});
ویژگیها و پیامدهای این تکه کد:
استفاده از response.Redirect("/error"):
این متد یک پاسخ HTTP با کد وضعیت ریدایرکت (معمولاً 302 Found یا 303 See Other) به همراه هدر Location: /error به مرورگر کلاینت ارسال میکند.
کلاینت (مرورگر) پس از دریافت این پاسخ، موظف است یک درخواست کاملاً جدید به مسیر /error ارسال کند.
خطرات و معایب ساختاری این روش:
از دست رفتن کد وضعیت اصلی (Status Code Loss): وقتی سرور ریدایرکت 302 میفرستد، مرورگر به صفحه /error میرود و اگر آن صفحه به درستی پیادهسازی نشده باشد، کد وضعیت HTTP نهایی که به کلاینت و موتورهای جستجو میرسد 200 OK خواهد بود (چون صفحه /error با موفقیت لود شده است)! این یک فاجعه برای SEO است؛ زیرا موتورهای جستجو مانند گوگل فکر میکنند صفحهای که وجود نداشته (مثلاً 404) حالا یک صفحه معتبر با کد 200 است و آن را ایندکس میکنند.
حلقه بینهایت ریدایرکت (Infinite Redirect Loop): اگر خود مسیر /error به هر دلیلی دچار خطایی شود (مثلاً خطای 500 در کنترلر خطا یا خطای 404 در صورتی که مسیر /error وجود نداشته باشد)، میدلور دوباره فعال میشود، دستور response.Redirect("/error") را صادر میکند و این چرخه تا بینهایت ادامه مییابد تا مرورگر خطای ERR_TOO_MANY_REDIRECTS را نشان دهد.
سربار شبکه (Performance Overhead): ریدایرکت مستلزم یک رفتوبرگشت مضاعف (Round-trip) شبکه بین کلاینت و سرور است که سرعت لود صفحه خطا را کاهش میدهد.
برای اینکه درک بهتری از تفاوتهای این دو رویکرد داشته باشیم، آنها را در ابعاد گوناگون مقایسه میکنیم:
| معیار مقایسه | تکه کد اول (پاسخ مستقیم + گارد نرخدهی) | تکه کد دوم (ریدایرکت به /error) |
| عملکرد و سرعت (Performance) | بسیار بالا: بدون نیاز به مسیریابی مجدد یا رفتوبرگشت شبکه، پاسخ مستقیماً نوشته میشود. | پایین: نیازمند یک درخواست HTTP جدید (Round-trip) و اجرای مجدد پایپلاین. |
| امنیت در برابر حلقه ریدایرکت | ایمن: با بررسی کدهای 429 و 503 و شرط !response.HasStarted از حلقه جلوگیری میکند. | آسیبپذیر: مستعد ایجاد حلقه بینهایت ریدایرکت در صورت بروز خطا در مسیر /error. |
| تاثیر بر سئو (SEO) | استاندارد: کد وضعیت واقعی خطا (مثل 404 یا 500) حفظ میشود. | منفی: کد وضعیت اصلی به 302 تغییر یافته و ممکن است صفحه خطا با کد 200 ایندکس شود. |
| پایداری در شرایط بحرانی | عالی: حتی اگر دیتابیس یا سیستمهای جانبی قطع باشند، متن HTML ثابت بدون وابستگی رندر میشود. | ضعیف: اگر مسیر /error به دیتابیس وابسته باشد، در زمان قطع دیتابیس اپلیکیشن کاملاً مختل میشود. |
| پیچیدگی نگهداری | سورس کد HTML داخل میدلور قرار دارد که برای تغییرات بزرگ طاهری کمی سختتر است. | از کنترلرها و ویوهای استاندارد MVC/Razor استفاده میکند که برای طراحیهای پیچیده مناسبتر است. |
اگر هدف شما داشتن یک صفحه خطای کاملاً اختصاصی (مانند استفاده از کنترلرها، ویوها یا صفحات Razor) بدون معایب خطرناک روش ریدایرکت (تکه کد دوم) است، فریمورک ASP.NET Core متد بسیار بهتری به نام UseStatusCodePagesWithReExecute را پیشنهاد میکند.
به جای ریدایرکت (که یک درخواست جدید با کد 302 میسازد)، این میدلور پایپلاین درخواست را مجدداً در سرور برای مسیر مشخصشده اجرا میکند (Internal Re-execution):
// استفاده از متد استاندارد برای هدایت داخلی بدون تغییر کد وضعیت اصلی
app.UseStatusCodePagesWithReExecute("/error/{0}");
در این حالت:
کد وضعیت اصلی (مثلاً 404) حفظ میشود.
هیچ ریدایرکت شبکه رخ نمیدهد و سربار به حداقل میرسد.
میتوانید در کنترلر /error/{0} به راحتی کد وضعیت را دریافت کرده و یک صفحه گرافیکی و زیبا به کاربر نمایش دهید.
وقتی صفحه ای خطا 404 را خواهد داشت یک HTTP Response Header به شکل زیر دریافت میشود.:
HTTP/1.1 404 Not Found ← THE STATUS CODE (this is what Google reads)
Content-Type: text/html; charset=utf-8
Content-Length: 312
← THE BODY (this is what the human sees)
...
جهت پیدا کردن و مشاهده 404 دو راه ساده وجود دارد:
1) از طریق inspect مرورگر chrome بشکل زیر اقدام کنید:

2) از طریق cmd دستور زیر را وارد کنید و باید خروجی 404 باشد:
curl.exe -s -o NUL -w "%{http_code}`n" ENTER-YOUR-URL
تکه کد اول یک رویکرد دفاعی، پایدار و ایمن برای برنامههایی است که میخواهند در شرایط بحرانی (مانند حملات DDoS، ترافیک بالا، خطاهای نرخدهی 429 و قطعی سرویس 503) بدون وابستگی به لایههای بیرونی، یک پاسخ پایهای و سریع به کاربر نشان دهند. گارد حفاظتی موجود در این کد از بروز بسیاری از مشکلات پایداری جلوگیری میکند.
تکه کد دوم یک روش سادهلوحانه و پرخطر است که به دلیل ایجاد ریدایرکتهای ناخواسته، مشکلات سئو، از دست رفتن کدهای وضعیت و احتمال بالای ایجاد حلقههای بینهایت ریدایرکت، به هیچ وجه در محیطهای عملیاتی (Production) توصیه نمیشود.
برای توسعهدهندگان حرفهای، انتخاب روش مناسب بستگی به معماری پروژه دارد؛ اما رعایت اصول پایداری سرور (مانند مدیریت کدهای 429 و 503) که در تکه کد اول به زیبایی پیادهسازی شده، نشاندهنده تسلط برنامهنویس بر ابعاد پشت صحنه پروتکل HTTP و پایداری سامانه است.
0 نظر
هنوز نظری برای این مقاله ثبت نشده است.