نسخه:

رسیدگی به خطا

معرفی

هنگامی که یک پروژه جدید لاراول را شروع می کنید، مدیریت خطا و استثنا از قبل برای شما پیکربندی شده است. کلاس App\Exceptions\Handler جایی است که تمام استثناهای پرتاب شده توسط برنامه شما ثبت شده و سپس به کاربر ارائه می شود. ما در طول این مستندات عمیق‌تر به این کلاس خواهیم پرداخت.

پیکربندی

گزینه debug موجود در config/app.php فایل پیکربندی شما تعیین می کند که واقعاً چه مقدار اطلاعات در مورد یک خطا به کاربر نمایش داده می شود. به طور پیش فرض، این گزینه به گونه ای تنظیم شده است که به مقدار APP_DEBUG متغیر محیطی که در فایل شما ذخیره می شود، احترام بگذارد .env .

در طول توسعه محلی، باید APP_DEBUG متغیر محیطی را روی true . در محیط تولید شما، این مقدار باید همیشه باشد false . اگر مقدار true در مرحله تولید تنظیم شده باشد، در معرض خطر قرار دادن مقادیر حساس پیکربندی برای کاربران نهایی برنامه خود هستید.

کنترل کننده استثنا

استثناهای گزارش دهی

همه استثناها توسط App\Exceptions\Handler کلاس انجام می شود. این کلاس حاوی register روشی است که در آن می‌توانید گزارش‌های استثنایی سفارشی را ثبت کنید و پاسخ‌های تماس را ارائه کنید. هر یک از این مفاهیم را به تفصیل بررسی خواهیم کرد. گزارش استثنا برای ثبت استثناها یا ارسال آنها به یک سرویس خارجی مانند Flare ، Bugsnag یا Sentry استفاده می شود . به طور پیش فرض، استثناها بر اساس پیکربندی ورود به سیستم شما ثبت می شوند . با این حال، شما آزاد هستید که استثناها را هر طور که می خواهید ثبت کنید.

اگر نیاز به گزارش انواع مختلف استثناها به روش‌های مختلف دارید، می‌توانید از این reportable روش برای ثبت یک بسته استفاده کنید که باید در زمانی که استثنایی از یک نوع معین باید گزارش شود اجرا شود. لاراول با بررسی نوع اشاره بسته مشخص می کند که بسته شدن چه نوع استثنایی را گزارش می کند:

use App\Exceptions\InvalidOrderException;
 
/**
* Register the exception handling callbacks for the application.
*/
public function register(): void
{
$this->reportable(function (InvalidOrderException $e) {
// ...
});
}

هنگامی که با استفاده از این متد، یک استثنای سفارشی را ثبت می‌کنید reportable ، لاراول همچنان استثنا را با استفاده از پیکربندی پیش‌فرض گزارش‌گیری برای برنامه ثبت می‌کند. اگر می‌خواهید انتشار استثنا در پشته گزارش پیش‌فرض را متوقف کنید، می‌توانید stop هنگام تعریف پاسخ به تماس گزارشی خود یا بازگشت false از پاسخ به تماس از این روش استفاده کنید:

$this->reportable(function (InvalidOrderException $e) {
// ...
})->stop();
 
$this->reportable(function (InvalidOrderException $e) {
return false;
});

برای سفارشی کردن گزارش استثنا برای یک استثنا خاص، می‌توانید از استثناهای قابل گزارش نیز استفاده کنید .

زمینه ثبت جهانی

اگر در دسترس باشد، لاراول به طور خودکار شناسه کاربر فعلی را به عنوان داده های متنی به پیام گزارش هر استثنا اضافه می کند. شما می توانید داده های متنی جهانی خود را با تعریف context متدی در کلاس برنامه خود تعریف کنید App\Exceptions\Handler . این اطلاعات در هر پیام گزارش استثنایی که توسط برنامه شما نوشته شده است گنجانده می شود:

/**
* Get the default context variables for logging.
*
* @return array<string, mixed>
*/
protected function context(): array
{
return array_merge(parent::context(), [
'foo' => 'bar',
]);
}

متن گزارش استثنا

در حالی که افزودن زمینه به هر پیام گزارش می تواند مفید باشد، گاهی اوقات یک استثنا خاص ممکن است زمینه منحصر به فردی داشته باشد که شما بخواهید آن را در گزارش های خود بگنجانید. با تعریف context روشی در یکی از استثناهای برنامه خود، می‌توانید هر داده مرتبط با آن استثنا را که باید به ورودی گزارش استثنا اضافه شود، مشخص کنید:

<?php
 
namespace App\Exceptions;
 
use Exception;
 
class InvalidOrderException extends Exception
{
// ...
 
/**
* Get the exception's context information.
*
* @return array<string, mixed>
*/
public function context(): array
{
return ['order_id' => $this->orderId];
}
}

یاور report _

گاهی اوقات ممکن است لازم باشد یک استثنا را گزارش کنید اما به رسیدگی به درخواست فعلی ادامه دهید. تابع helper report به شما امکان می دهد تا به سرعت یک استثنا را از طریق کنترل کننده استثنا بدون ارائه صفحه خطا به کاربر گزارش دهید:

public function isValid(string $value): bool
{
try {
// Validate the value...
} catch (Throwable $e) {
report($e);
 
return false;
}
}

کپی برداری از استثناهای گزارش شده

اگر از این تابع در سراسر برنامه خود استفاده می کنید report ، ممکن است گهگاه یک استثنا را چندین بار گزارش دهید و ورودی های تکراری در گزارش های خود ایجاد کنید.

اگر می خواهید اطمینان حاصل کنید که یک نمونه از یک استثنا فقط یک بار گزارش می شود، می توانید $withoutDuplicates ویژگی را در کلاس true برنامه خود تنظیم کنید: App\Exceptions\Handler

namespace App\Exceptions;
 
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
 
class Handler extends ExceptionHandler
{
/**
* Indicates that an exception instance should only be reported once.
*
* @var bool
*/
protected $withoutDuplicates = true;
 
// ...
}

اکنون، وقتی report کمک کننده با همان نمونه استثنا فراخوانی می شود، فقط اولین تماس گزارش می شود:

$original = new RuntimeException('Whoops!');
 
report($original); // reported
 
try {
throw $original;
} catch (Throwable $caught) {
report($caught); // ignored
}
 
report($original); // ignored
report($caught); // ignored

سطوح ثبت استثنا

وقتی پیام‌ها در گزارش‌های برنامه شما نوشته می‌شوند ، پیام‌ها در سطح گزارش مشخصی نوشته می‌شوند ، که نشان‌دهنده شدت یا اهمیت پیام ثبت‌شده است.

همانطور که در بالا ذکر شد، حتی زمانی که با استفاده از این روش، یک استثنا سفارشی را ثبت می‌کنید reportable ، لاراول همچنان استثنا را با استفاده از پیکربندی پیش‌فرض گزارش‌گیری برای برنامه ثبت می‌کند. با این حال، از آنجایی که سطح گزارش گاهی اوقات می‌تواند بر کانال‌هایی که یک پیام در آنها ثبت می‌شود تأثیر بگذارد، ممکن است بخواهید سطح گزارشی را پیکربندی کنید که استثناهای خاصی در آن ثبت شوند.

برای انجام این کار، می توانید یک $levels ویژگی را در کنترل کننده استثنای برنامه خود تعریف کنید. این ویژگی باید دارای آرایه ای از انواع استثنا و سطوح گزارش مربوط به آنها باشد:

use PDOException;
use Psr\Log\LogLevel;
 
/**
* A list of exception types with their corresponding custom log levels.
*
* @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
*/
protected $levels = [
PDOException::class => LogLevel::CRITICAL,
];

نادیده گرفتن استثناها بر اساس نوع

هنگام ساخت برنامه خود، انواع استثناهایی وجود دارد که هرگز نمی خواهید گزارش دهید. برای نادیده گرفتن این استثناها، یک $dontReport ویژگی را در کنترل کننده استثنای برنامه خود تعریف کنید. هر کلاسی که به این ویژگی اضافه کنید هرگز گزارش نخواهد شد. با این حال، ممکن است هنوز منطق رندر سفارشی داشته باشند:

use App\Exceptions\InvalidOrderException;
 
/**
* A list of the exception types that are not reported.
*
* @var array<int, class-string<\Throwable>>
*/
protected $dontReport = [
InvalidOrderException::class,
];

در داخل، لاراول در حال حاضر برخی از انواع خطاها را برای شما نادیده می گیرد، مانند استثناهای ناشی از خطاهای 404 HTTP یا 419 پاسخ HTTP تولید شده توسط توکن های نامعتبر CSRF. اگر می‌خواهید به لاراول دستور دهید تا از نادیده گرفتن یک نوع استثنای خاص خودداری کند، می‌توانید متد را در متد stopIgnoring کنترل‌کننده استثنا خود فراخوانی کنید: register

use Symfony\Component\HttpKernel\Exception\HttpException;
 
/**
* Register the exception handling callbacks for the application.
*/
public function register(): void
{
$this->stopIgnoring(HttpException::class);
 
// ...
}

رندر استثناها

به طور پیش فرض، کنترل کننده استثنا لاراول استثناها را به یک پاسخ HTTP برای شما تبدیل می کند. با این حال، شما آزاد هستید که یک بسته رندر سفارشی را برای استثناهایی از یک نوع مشخص ثبت کنید. شما می توانید این کار را با فراخوانی renderable متد در کنترل کننده استثنای خود انجام دهید.

بسته شدن ارسال شده به renderable متد باید یک نمونه از را برگرداند Illuminate\Http\Response که ممکن است از طریق response کمک کننده ایجاد شود. لاراول با بررسی نوع اشاره بسته تعیین می کند که بسته شدن چه نوع استثنایی را ارائه می دهد:

use App\Exceptions\InvalidOrderException;
use Illuminate\Http\Request;
 
/**
* Register the exception handling callbacks for the application.
*/
public function register(): void
{
$this->renderable(function (InvalidOrderException $e, Request $request) {
return response()->view('errors.invalid-order', [], 500);
});
}

همچنین می توانید از این renderable روش برای نادیده گرفتن رفتار رندر برای استثناهای داخلی لاراول یا سیمفونی مانند NotFoundHttpException . اگر بسته شدن داده شده به renderable متد مقداری را برنگرداند، از رندر استثنایی پیش‌فرض لاراول استفاده می‌شود:

use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
/**
* Register the exception handling callbacks for the application.
*/
public function register(): void
{
$this->renderable(function (NotFoundHttpException $e, Request $request) {
if ($request->is('api/*')) {
return response()->json([
'message' => 'Record not found.'
], 404);
}
});
}

استثناهای گزارش پذیر و قابل اجرا

به جای تعریف گزارش‌دهی سفارشی و رفتار رندر در register متد کنترل‌کننده استثنا، می‌توانید مستقیماً استثناهای برنامه خود را تعریف report و متد کنید. render هنگامی که این متدها وجود داشته باشند، به طور خودکار توسط فریمورک فراخوانی می شوند:

<?php
 
namespace App\Exceptions;
 
use Exception;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
 
class InvalidOrderException extends Exception
{
/**
* Report the exception.
*/
public function report(): void
{
// ...
}
 
/**
* Render the exception into an HTTP response.
*/
public function render(Request $request): Response
{
return response(/* ... */);
}
}

اگر استثنای شما استثنایی را گسترش دهد که از قبل قابل اجرا است، مانند یک استثنای داخلی لاراول یا Symfony، می‌توانید false از متد استثنا render برای ارائه پاسخ HTTP پیش‌فرض استثنا برگردید:

/**
* Render the exception into an HTTP response.
*/
public function render(Request $request): Response|bool
{
if (/** Determine if the exception needs custom rendering */) {
 
return response(/* ... */);
}
 
return false;
}

اگر استثنای شما حاوی منطق گزارش دهی سفارشی است که فقط زمانی لازم است که شرایط خاصی برآورده شود، ممکن است لازم باشد به لاراول دستور دهید که گاهی استثنا را با استفاده از پیکربندی پیش فرض مدیریت استثنا گزارش دهد. برای انجام این کار، می توانید false از روش استثنا برگردید report :

/**
* Report the exception.
*/
public function report(): bool
{
if (/** Determine if the exception needs custom reporting */) {
 
// ...
 
return true;
}
 
return false;
}

می‌توانید وابستگی‌های مورد نیاز متد را تایپ کنید و آنها به طور خودکار توسط کانتینر سرویس report لاراول به متد تزریق می‌شوند .

کاهش استثناهای گزارش شده

اگر برنامه شما تعداد بسیار زیادی استثنا را گزارش می دهد، ممکن است بخواهید تعداد استثناهایی که واقعاً ثبت شده یا به سرویس ردیابی خطای خارجی برنامه شما ارسال شده اند را تعیین کنید.

برای گرفتن یک نرخ نمونه تصادفی از استثناها، می توانید یک Lottery نمونه از روش کنترل کننده استثنا خود را برگردانید throttle . اگر App\Exceptions\Handler کلاس شما حاوی این متد نیست، می توانید به سادگی آن را به کلاس اضافه کنید:

use Illuminate\Support\Lottery;
use Throwable;
 
/**
* Throttle incoming exceptions.
*/
protected function throttle(Throwable $e): mixed
{
return Lottery::odds(1, 1000);
}

همچنین امکان نمونه گیری مشروط بر اساس نوع استثنا وجود دارد. اگر می‌خواهید فقط نمونه‌هایی از یک کلاس استثنای خاص را نمونه‌گیری کنید، می‌توانید یک Lottery نمونه را فقط برای آن کلاس برگردانید:

use App\Exceptions\ApiMonitoringException;
use Illuminate\Support\Lottery;
use Throwable;
 
/**
* Throttle incoming exceptions.
*/
protected function throttle(Throwable $e): mixed
{
if ($e instanceof ApiMonitoringException) {
return Lottery::odds(1, 1000);
}
}

همچنین می‌توانید استثناهای محدود ثبت شده یا ارسال شده به یک سرویس ردیابی خطای خارجی را با بازگرداندن یک Limit نمونه به جای یک رتبه‌بندی کنید Lottery . اگر می‌خواهید در برابر هجوم ناگهانی استثناها که لاگ‌هایتان را سرازیر می‌کنند، محافظت کنید، به عنوان مثال، هنگامی که یک سرویس شخص ثالث مورد استفاده برنامه شما قطع است، مفید است:

use Illuminate\Broadcasting\BroadcastException;
use Illuminate\Cache\RateLimiting\Limit;
use Throwable;
 
/**
* Throttle incoming exceptions.
*/
protected function throttle(Throwable $e): mixed
{
if ($e instanceof BroadcastException) {
return Limit::perMinute(300);
}
}

به طور پیش‌فرض، محدودیت‌ها از کلاس استثنا به عنوان کلید محدودیت نرخ استفاده می‌کنند. شما می توانید این را با تعیین کلید خود با استفاده از by روش زیر سفارشی کنید Limit :

use Illuminate\Broadcasting\BroadcastException;
use Illuminate\Cache\RateLimiting\Limit;
use Throwable;
 
/**
* Throttle incoming exceptions.
*/
protected function throttle(Throwable $e): mixed
{
if ($e instanceof BroadcastException) {
return Limit::perMinute(300)->by($e->getMessage());
}
}

البته، می‌توانید ترکیبی از Lottery و Limit نمونه‌ها را برای استثناهای مختلف برگردانید:

use App\Exceptions\ApiMonitoringException;
use Illuminate\Broadcasting\BroadcastException;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Lottery;
use Throwable;
 
/**
* Throttle incoming exceptions.
*/
protected function throttle(Throwable $e): mixed
{
return match (true) {
$e instanceof BroadcastException => Limit::perMinute(300),
$e instanceof ApiMonitoringException => Lottery::odds(1, 1000),
default => Limit::none(),
};
}

استثناهای HTTP

برخی استثناها کدهای خطای HTTP را از سرور توصیف می کنند. به عنوان مثال، این ممکن است یک خطای «صفحه یافت نشد» (404)، یک «خطای غیرمجاز» (401)، یا حتی یک خطای 500 توسط برنامه‌نویس باشد. برای ایجاد چنین پاسخی از هر نقطه در برنامه خود، می توانید از abort راهنما استفاده کنید:

abort(404);

صفحات خطای سفارشی HTTP

لاراول نمایش صفحات خطای سفارشی برای کدهای وضعیت HTTP مختلف را آسان می کند. به عنوان مثال، برای سفارشی کردن صفحه خطا برای کدهای وضعیت HTTP 404، یک resources/views/errors/404.blade.php قالب نمایش ایجاد کنید. این نمای برای تمام خطاهای 404 ایجاد شده توسط برنامه شما ارائه می شود. نماهای موجود در این فهرست باید به گونه ای نامگذاری شوند که با کد وضعیت HTTP مطابقت دارند. نمونه ای Symfony\Component\HttpKernel\Exception\HttpException که توسط abort تابع مطرح می شود به عنوان یک متغیر به view ارسال می شود $exception :

<h2>{{ $exception->getMessage() }}</h2>

شما می توانید قالب های صفحه خطای پیش فرض لاراول را با استفاده از vendor:publish دستور Artisan منتشر کنید. پس از انتشار قالب ها، می توانید آنها را به دلخواه شخصی سازی کنید:

php artisan vendor:publish --tag=laravel-errors

صفحات خطای بازگشتی HTTP

همچنین می‌توانید برای یک سری کدهای وضعیت HTTP یک صفحه خطای «بازگشت» تعریف کنید. اگر صفحه مربوط به کد وضعیت HTTP خاصی که رخ داده وجود نداشته باشد، این صفحه نمایش داده می شود. برای انجام این کار، یک 4xx.blade.php الگو و یک 5xx.blade.php الگو را در فهرست برنامه خود تعریف کنید resources/views/errors .