- Введение
- Конфигурирование
-
Обработчик исключений
- Отчет об исключениях
- Игнорирование исключений по типу
- Отображение исключений
- Отчетные и отображаемые исключения
- Сопоставление исключений по типу
-
HTTP-исключения
- Пользовательские страницы ошибок HTTP
Введение
Когда вы запускаете новый проект Laravel, обработка ошибок и исключений уже настроена для вас. Класс AppExceptionsHandler
– это то место, где все исключения, созданные вашим приложением, регистрируются и затем отображаются пользователю. В этой документации мы углубимся в этот класс.
Конфигурирование
Параметр debug
в конфигурационном файле config/app.php
определяет, сколько информации об ошибке фактически отобразится пользователю. По умолчанию этот параметр установлен, чтобы учесть значение переменной окружения APP_DEBUG
, которая содержится в вашем файле .env
.
Во время локальной разработки вы должны установить для переменной окружения APP_DEBUG
значение true
. Во время эксплуатации приложения это значение всегда должно быть false
. Если в рабочем окружении будет установлено значение true
, вы рискуете раскрыть конфиденциальные значения конфигурации конечным пользователям вашего приложения.
Обработчик исключений
Отчет об исключениях
Все исключения обрабатываются классом AppExceptionsHandler
. Этот класс содержит метод register
, в котором вы можете зарегистрировать свои отчеты об исключениях и замыкания рендеринга. Мы подробно рассмотрим каждую из этих концепций. Отчеты об исключениях используются для регистрации исключений или отправки их во внешнюю службу, например Flare, Bugsnag или Sentry. По умолчанию исключения будут регистрироваться в соответствии с вашей конфигурацией логирования. Однако вы можете регистрировать исключения как хотите.
Например, если вам нужно сообщать о различных типах исключений по-разному, вы можете использовать метод reportable
для регистрации замыкания, которое должно быть выполнено, когда необходимо сообщить об исключении конкретного типа. Laravel определит о каком типе исключения сообщает замыкание с помощью типизации аргументов:
use AppExceptionsInvalidOrderException;
/**
* Зарегистрировать замыкания, обрабатывающие исключения приложения.
*
* @return void
*/
public function register()
{
$this->reportable(function (InvalidOrderException $e) {
//
});
}
Когда вы регистрируете собственные замыкания для создания отчетов об исключениях, используя метод reportable
, Laravel по-прежнему регистрирует исключение, используя конфигурацию логирования по умолчанию для приложения. Если вы хотите остановить распространение исключения в стек журналов по умолчанию, вы можете использовать метод stop
при определении замыкания отчета или вернуть false
из замыкания:
$this->reportable(function (InvalidOrderException $e) {
//
})->stop();
$this->reportable(function (InvalidOrderException $e) {
return false;
});
Чтобы настроить отчет об исключениях для переданного исключения, вы можете рассмотреть возможность использования отчетных исключений.
Глобальное содержимое журнала
Если доступно, Laravel автоматически добавляет идентификатор текущего пользователя в каждое сообщение журнала исключения в качестве контекстных данных. Вы можете определить свои собственные глобальные контекстные данные, переопределив метод context
класса AppExceptionsHandler
вашего приложения. Эта информация будет включена в каждое сообщение журнала исключения, написанное вашим приложением:
/**
* Получить переменные контекста по умолчанию для ведения журнала.
*
* @return array
*/
protected function context()
{
return array_merge(parent::context(), [
'foo' => 'bar',
]);
}
Контекст журнала исключений
Хотя добавление контекста в каждое сообщение журнала может быть полезно, иногда конкретное исключение может иметь уникальный контекст, который вы хотели бы включить в свои журналы. Определив метод context
для конкретного исключения вашего приложения, вы можете указать любые данные, относящиеся к этому исключению, которые должны быть добавлены в запись журнала исключения:
<?php
namespace AppExceptions;
use Exception;
class InvalidOrderException extends Exception
{
// ...
/**
* Получить контекстную информацию исключения.
*
* @return array
*/
public function context()
{
return ['order_id' => $this->orderId];
}
}
Помощник report
По желанию может потребоваться сообщить об исключении, но продолжить обработку текущего запроса. Помощник report
позволяет вам быстро сообщить об исключении через обработчик исключений, не отображая страницу с ошибкой для пользователя:
public function isValid($value)
{
try {
// Проверка `$value` ...
} catch (Throwable $e) {
report($e);
return false;
}
}
Игнорирование исключений по типу
При создании приложения будут некоторые типы исключений, которые вы просто хотите игнорировать и никогда не сообщать о них. Обработчик исключений вашего приложения содержит свойство $dontReport
, которое инициализируется пустым массивом. Ни о каких классах, добавленных в это свойство, никогда не будет сообщено; однако у них все еще может быть собственная логика отображения:
use AppExceptionsInvalidOrderException;
/**
* Список типов исключений, о которых не следует сообщать.
*
* @var array
*/
protected $dontReport = [
InvalidOrderException::class,
];
За кулисами Laravel уже игнорирует для вас некоторые типы ошибок, такие как исключения, возникающие из-за ошибок 404 HTTP «не найдено» или 419 HTTP-ответ, сгенерированный при недопустимом CSRF-токене.
Отображение исключений
По умолчанию обработчик исключений Laravel будет преобразовывать исключения в HTTP-ответ за вас. Однако вы можете зарегистрировать свое замыкание для отображения исключений конкретного типа. Вы можете сделать это с помощью метода renderable
обработчика исключений.
Замыкание, переданное методу renderable
, должно вернуть экземпляр IlluminateHttpResponse
, который может быть сгенерирован с помощью функции response
. Laravel определит, какой тип исключения отображает замыкание с помощью типизации аргументов:
use AppExceptionsInvalidOrderException;
/**
* Зарегистрировать замыкания, обрабатывающие исключения приложения.
*
* @return void
*/
public function register()
{
$this->renderable(function (InvalidOrderException $e, $request) {
return response()->view('errors.invalid-order', [], 500);
});
}
Вы также можете использовать метод renderable
чтобы переопределить отображение для встроенных исключений Laravel или Symfony, таких, как NotFoundHttpException
. Если замыкание, переданное методу renderable
не возвращает значения, будет использоваться отрисовка исключений Laravel по умолчанию:
use SymfonyComponentHttpKernelExceptionNotFoundHttpException;
/**
* Зарегистрировать замыкания, обрабатывающие исключения приложения.
*
* @return void
*/
public function register()
{
$this->renderable(function (NotFoundHttpException $e, $request) {
if ($request->is('api/*')) {
return response()->json([
'message' => 'Record not found.'
], 404);
}
});
}
Отчетные и отображаемые исключения
Вместо проверки типов исключений в методе register
обработчика исключений вы можете определить методы report
и render
непосредственно для ваших исключений. Если эти методы существуют, то они будут автоматически вызываться фреймворком:
<?php
namespace AppExceptions;
use Exception;
class InvalidOrderException extends Exception
{
/**
* Отчитаться об исключении.
*
* @return bool|null
*/
public function report()
{
//
}
/**
* Преобразовать исключение в HTTP-ответ.
*
* @param IlluminateHttpRequest $request
* @return IlluminateHttpResponse
*/
public function render($request)
{
return response(...);
}
}
Если ваше исключение расширяет исключение, которое уже доступно для визуализации, например встроенное исключение Laravel или Symfony, вы можете вернуть false
из метода render
исключения, чтобы отобразить HTTP-ответ исключения по умолчанию:
/**
* Преобразовать исключение в HTTP-ответ.
*
* @param IlluminateHttpRequest $request
* @return IlluminateHttpResponse
*/
public function render($request)
{
// Определить, требуется ли для исключения пользовательское отображение...
return false;
}
Если ваше исключение содержит пользовательскую логику отчетности, которая необходима только при выполнении определенных условий, то вам может потребоваться указать Laravel когда сообщать об исключении, используя конфигурацию обработки исключений по умолчанию. Для этого вы можете вернуть false
из метода report
исключения:
/**
* Сообщить об исключении.
*
* @return bool|null
*/
public function report()
{
// Определить, требуется ли для исключения пользовательская отчетность ...
return false;
}
Вы можете указать любые требуемые зависимости метода
report
, и они будут автоматически внедрены в метод контейнером служб Laravel.
Сопоставление исключений по типу
Иногда сторонние библиотеки, используемые вашим приложением, могут генерировать исключения, которые вы хотите сделать доступными для рендеринга, но не можете этого сделать, потому что у вас нет контроля над определениями сторонних исключений.
К счастью, Laravel позволяет вам удобно сопоставлять эти исключения с другими типами исключений, которыми вы управляете в своем приложении. Для этого вызовите метод map
из метода register
вашего обработчика исключений :
use LeagueFlysystemException;
use AppExceptionsFilesystemException;
/**
* Register the exception handling callbacks for the application.
*
* @return void
*/
public function register()
{
$this->map(Exception::class, FilesystemException::class);
}
Если вы хотите больше контролировать создание целевого исключения, вы можете передать методу map
замыкание:
use LeagueFlysystemException;
use AppExceptionsFilesystemException;
$this->map(fn (Exception $e) => new FilesystemException($e));
HTTP-исключения
Некоторые исключения описывают коды HTTP-ошибок с сервера. Например, это может быть ошибка «страница не найдена» (404), «неавторизованный доступ» (401) или даже ошибка 500, сгенерированная разработчиком. Чтобы создать такой ответ из любой точки вашего приложения, вы можете использовать глобальный помощник abort
:
abort(404);
Пользовательские страницы ошибок HTTP
Laravel позволяет легко отображать пользовательские страницы ошибок для различных кодов состояния HTTP. Например, если вы хотите настроить страницу ошибок для кодов HTTP-состояния 404, создайте файл resources/views/errors/404.blade.php
. Это представление будет отображено для всех ошибок 404, сгенерированных вашим приложением. Шаблоны в этом каталоге должны быть названы в соответствии с кодом состояния HTTP, которому они соответствуют. Экземпляр SymfonyComponentHttpKernelExceptionHttpException
, вызванный функцией abort
, будет передан в шаблон как переменная $exception
:
<h2>{{ $exception->getMessage() }}</h2>
Вы можете опубликовать стандартные шаблоны страниц ошибок Laravel с помощью команды vendor:publish
Artisan. После публикации шаблонов вы можете настроить их по своему вкусу:
php artisan vendor:publish --tag=laravel-errors
git 0f19f3d770b961aee8576b24deb394a0b9a2f7ad
Обработка ошибок (Exception)
- Введение
- Конфигурирование
- Обработчик исключений
- Отчет об исключениях
- Игнорирование исключений по типу
- Отображение исключений
- Отчетные и отображаемые исключения
- Сопоставление исключений по типу
- HTTP-исключения
- Пользовательские страницы ошибок HTTP
Введение
Когда вы запускаете новый проект Laravel, обработка ошибок и исключений уже настроена для вас. Класс AppExceptionsHandler
– это то место, где все исключения, созданные вашим приложением, регистрируются и затем отображаются пользователю. В этой документации мы углубимся в этот класс.
Конфигурирование
Параметр debug
в конфигурационном файле config/app.php
определяет, сколько информации об ошибке фактически отобразится пользователю. По умолчанию этот параметр установлен, чтобы учесть значение переменной окружения APP_DEBUG
, которая содержится в вашем файле .env
.
Во время локальной разработки вы должны установить для переменной окружения APP_DEBUG
значение true
. Во время эксплуатации приложения это значение всегда должно быть false
. Если в рабочем окружении будет установлено значение true
, вы рискуете раскрыть конфиденциальные значения конфигурации конечным пользователям вашего приложения.
Обработчик исключений
Отчет об исключениях
Все исключения обрабатываются классом AppExceptionsHandler
. Этот класс содержит метод register
, в котором вы можете зарегистрировать свои отчеты об исключениях и замыкания рендеринга. Мы подробно рассмотрим каждую из этих концепций. Отчеты об исключениях используются для регистрации исключений или отправки их во внешнюю службу, например Flare, Bugsnag или Sentry. По умолчанию исключения будут регистрироваться в соответствии с вашей конфигурацией логирования. Однако вы можете регистрировать исключения как хотите.
Например, если вам нужно сообщать о различных типах исключений по-разному, вы можете использовать метод reportable
для регистрации замыкания, которое должно быть выполнено, когда необходимо сообщить об исключении конкретного типа. Laravel определит о каком типе исключения сообщает замыкание с помощью типизации аргументов:
use AppExceptionsInvalidOrderException;
/**
* Зарегистрировать замыкания, обрабатывающие исключения приложения.
*
* @return void
*/
public function register()
{
$this->reportable(function (InvalidOrderException $e) {
//
});
}
Когда вы регистрируете собственные замыкания для создания отчетов об исключениях, используя метод reportable
, Laravel по-прежнему регистрирует исключение, используя конфигурацию логирования по умолчанию для приложения. Если вы хотите остановить распространение исключения в стек журналов по умолчанию, вы можете использовать метод stop
при определении замыкания отчета или вернуть false
из замыкания:
$this->reportable(function (InvalidOrderException $e) {
//
})->stop();
$this->reportable(function (InvalidOrderException $e) {
return false;
});
{tip} Чтобы настроить отчет об исключениях для переданного исключения, вы можете рассмотреть возможность использования отчетных исключений.
Глобальное содержимое журнала
Если доступно, Laravel автоматически добавляет идентификатор текущего пользователя в каждое сообщение журнала исключения в качестве контекстных данных. Вы можете определить свои собственные глобальные контекстные данные, переопределив метод context
класса AppExceptionsHandler
вашего приложения. Эта информация будет включена в каждое сообщение журнала исключения, написанное вашим приложением:
/**
* Получить переменные контекста по умолчанию для ведения журнала.
*
* @return array
*/
protected function context()
{
return array_merge(parent::context(), [
'foo' => 'bar',
]);
}
Контекст журнала исключений
Хотя добавление контекста в каждое сообщение журнала может быть полезно, иногда конкретное исключение может иметь уникальный контекст, который вы хотели бы включить в свои журналы. Определив метод context
для конкретного исключения вашего приложения, вы можете указать любые данные, относящиеся к этому исключению, которые должны быть добавлены в запись журнала исключения:
<?php
namespace AppExceptions;
use Exception;
class InvalidOrderException extends Exception
{
// ...
/**
* Получить контекстную информацию исключения.
*
* @return array
*/
public function context()
{
return ['order_id' => $this->orderId];
}
}
Помощник report
По желанию может потребоваться сообщить об исключении, но продолжить обработку текущего запроса. Помощник report
позволяет вам быстро сообщить об исключении через обработчик исключений, не отображая страницу с ошибкой для пользователя:
public function isValid($value)
{
try {
// Проверка `$value` ...
} catch (Throwable $e) {
report($e);
return false;
}
}
Игнорирование исключений по типу
При создании приложения будут некоторые типы исключений, которые вы просто хотите игнорировать и никогда не сообщать о них. Обработчик исключений вашего приложения содержит свойство $dontReport
, которое инициализируется пустым массивом. Ни о каких классах, добавленных в это свойство, никогда не будет сообщено; однако у них все еще может быть собственная логика отображения:
use AppExceptionsInvalidOrderException;
/**
* Список типов исключений, о которых не следует сообщать.
*
* @var array
*/
protected $dontReport = [
InvalidOrderException::class,
];
{tip} За кулисами Laravel уже игнорирует для вас некоторые типы ошибок, такие как исключения, возникающие из-за ошибок 404 HTTP «не найдено» или 419 HTTP-ответ, сгенерированный при недопустимом CSRF-токене.
Отображение исключений
По умолчанию обработчик исключений Laravel будет преобразовывать исключения в HTTP-ответ за вас. Однако вы можете зарегистрировать свое замыкание для отображения исключений конкретного типа. Вы можете сделать это с помощью метода renderable
обработчика исключений.
Замыкание, переданное методу renderable
, должно вернуть экземпляр IlluminateHttpResponse
, который может быть сгенерирован с помощью функции response
. Laravel определит, какой тип исключения отображает замыкание с помощью типизации аргументов:
use AppExceptionsInvalidOrderException;
/**
* Зарегистрировать замыкания, обрабатывающие исключения приложения.
*
* @return void
*/
public function register()
{
$this->renderable(function (InvalidOrderException $e, $request) {
return response()->view('errors.invalid-order', [], 500);
});
}
Вы также можете использовать метод renderable
чтобы переопределить отображение для встроенных исключений Laravel или Symfony, таких, как NotFoundHttpException
. Если замыкание, переданное методу renderable
не возвращает значения, будет использоваться отрисовка исключений Laravel по умолчанию:
use SymfonyComponentHttpKernelExceptionNotFoundHttpException;
/**
* Зарегистрировать замыкания, обрабатывающие исключения приложения.
*
* @return void
*/
public function register()
{
$this->renderable(function (NotFoundHttpException $e, $request) {
if ($request->is('api/*')) {
return response()->json([
'message' => 'Record not found.'
], 404);
}
});
}
Отчетные и отображаемые исключения
Вместо проверки типов исключений в методе register
обработчика исключений вы можете определить методы report
и render
непосредственно для ваших исключений. Если эти методы существуют, то они будут автоматически вызываться фреймворком:
<?php
namespace AppExceptions;
use Exception;
class InvalidOrderException extends Exception
{
/**
* Отчитаться об исключении.
*
* @return bool|null
*/
public function report()
{
//
}
/**
* Преобразовать исключение в HTTP-ответ.
*
* @param IlluminateHttpRequest $request
* @return IlluminateHttpResponse
*/
public function render($request)
{
return response(...);
}
}
Если ваше исключение расширяет исключение, которое уже доступно для визуализации, например встроенное исключение Laravel или Symfony, вы можете вернуть false
из метода render
исключения, чтобы отобразить HTTP-ответ исключения по умолчанию:
/**
* Преобразовать исключение в HTTP-ответ.
*
* @param IlluminateHttpRequest $request
* @return IlluminateHttpResponse
*/
public function render($request)
{
// Определить, требуется ли для исключения пользовательское отображение...
return false;
}
Если ваше исключение содержит пользовательскую логику отчетности, которая необходима только при выполнении определенных условий, то вам может потребоваться указать Laravel когда сообщать об исключении, используя конфигурацию обработки исключений по умолчанию. Для этого вы можете вернуть false
из метода report
исключения:
/**
* Сообщить об исключении.
*
* @return bool|null
*/
public function report()
{
// Определить, требуется ли для исключения пользовательская отчетность ...
return false;
}
{tip} Вы можете указать любые требуемые зависимости метода
report
, и они будут автоматически внедрены в метод контейнером служб Laravel.
Сопоставление исключений по типу
Иногда сторонние библиотеки, используемые вашим приложением, могут генерировать исключения, которые вы хотите сделать доступными для рендеринга, но не можете этого сделать, потому что у вас нет контроля над определениями сторонних исключений.
К счастью, Laravel позволяет вам удобно сопоставлять эти исключения с другими типами исключений, которыми вы управляете в своем приложении. Для этого вызовите метод map
из метода register
вашего обработчика исключений :
use LeagueFlysystemException;
use AppExceptionsFilesystemException;
/**
* Register the exception handling callbacks for the application.
*
* @return void
*/
public function register()
{
$this->map(Exception::class, FilesystemException::class);
}
Если вы хотите больше контролировать создание целевого исключения, вы можете передать методу map
замыкание:
use LeagueFlysystemException;
use AppExceptionsFilesystemException;
$this->map(fn (Exception $e) => new FilesystemException($e));
HTTP-исключения
Некоторые исключения описывают коды HTTP-ошибок с сервера. Например, это может быть ошибка «страница не найдена» (404), «неавторизованный доступ» (401) или даже ошибка 500, сгенерированная разработчиком. Чтобы создать такой ответ из любой точки вашего приложения, вы можете использовать глобальный помощник abort
:
Пользовательские страницы ошибок HTTP
Laravel позволяет легко отображать пользовательские страницы ошибок для различных кодов состояния HTTP. Например, если вы хотите настроить страницу ошибок для кодов HTTP-состояния 404, создайте файл resources/views/errors/404.blade.php
. Это представление будет отображено для всех ошибок 404, сгенерированных вашим приложением. Шаблоны в этом каталоге должны быть названы в соответствии с кодом состояния HTTP, которому они соответствуют. Экземпляр SymfonyComponentHttpKernelExceptionHttpException
, вызванный функцией abort
, будет передан в шаблон как переменная $exception
:
<h2>{{ $exception->getMessage() }}</h2>
Вы можете опубликовать стандартные шаблоны страниц ошибок Laravel с помощью команды vendor:publish
Artisan. После публикации шаблонов вы можете настроить их по своему вкусу:
php artisan vendor:publish --tag=laravel-errors
I am trying to figure out to show 404 page not found if a route is not found. I followed many tutorials, but it doesn’t work.
I have 404.blade.php
in laravelresourcesviewserrors
Also in handler.php
public function render($request, Exception $e)
{
if ($e instanceof TokenMismatchException) {
// redirect to form an example of how i handle mine
return redirect($request->fullUrl())->with(
'csrf_error',
"Opps! Seems you couldn't submit form for a longtime. Please try again"
);
}
/*if ($e instanceof CustomException) {
return response()->view('errors.404', [], 500);
}*/
if ($e instanceof SymfonyComponentHttpKernelExceptionNotFoundHttpException)
return response(view('error.404'), 404);
return parent::render($request, $e);
}
If I enter wrong URL in browser, it returns a blank page. I have
'debug' => env('APP_DEBUG', true),
in app.php.
Can anyone help me how to show a 404 page if route is not found? Thank you.
asked Aug 25, 2015 at 4:06
scottscott
3,10219 gold badges51 silver badges88 bronze badges
7
I recieved 500 errors instead of 404 errors. I solved the problem like this:
In the app/Exceptions/Handler.php file, there is a render function.
Replace the function with this function:
public function render($request, Exception $e)
{
if ($this->isHttpException($e)) {
switch ($e->getStatusCode()) {
// not authorized
case '403':
return Response::view('errors.403',array(),403);
break;
// not found
case '404':
return Response::view('errors.404',array(),404);
break;
// internal error
case '500':
return Response::view('errors.500',array(),500);
break;
default:
return $this->renderHttpException($e);
break;
}
} else {
return parent::render($request, $e);
}
}
You can then use views that you save in views/errors/404.blade.php, and so on.
w3spi
4,2909 gold badges46 silver badges80 bronze badges
answered Jan 14, 2016 at 14:11
PimPim
5,5964 gold badges19 silver badges27 bronze badges
0
> The abort method will immediately raise an exception which will be rendered by the exception handler. Optionally, you may provide the response text:
abort(403, 'Unauthorized action.');
is your app_debug set to true? if that is the case, Laravel will throw the error with backtrace for debugging purposes, if you change the value to false, Laravel will show the default 404 page in the errors folder. That being said you can choose to use abort at any time you want. at the controller level or at the route level, it is totally up to you.
ie
Route::get('/page/not/found',function($closure){
// second parameter is optional.
abort(404,'Page not found');
abort(403);
});
answered Aug 25, 2015 at 4:20
mdamiamdamia
4,4071 gold badge24 silver badges23 bronze badges
6
@tester.Your problem has already been solved, try the command below in composer:
php artisan view:clear
Then try once more with an unknown URL. Because I have also faced the same error before.
M’Baku
3203 silver badges18 bronze badges
answered Aug 25, 2015 at 5:55
0
There is no need for you to check the error type and manually render the 404 view. Laravel already knows to render the view with the HTTP error code that was thrown (404 = resources/views/errors/404.blade.php). Get rid of the extra check and it should work fine.
public function render($request, Exception $e)
{
if ($e instanceof TokenMismatchException) {
// redirect to form an example of how i handle mine
return redirect($request->fullUrl())->with(
'csrf_error',
"Opps! Seems you couldn't submit form for a longtime. Please try again"
);
}
return parent::render($request, $e);
}
answered Aug 25, 2015 at 5:03
patricuspatricus
58.8k15 gold badges143 silver badges145 bronze badges
1
I use the following in app/Exceptions/Handler.php (Laravel 5.2):
/**
* Render an exception into an HTTP response.
*
* @param IlluminateHttpRequest $request
* @param Exception $e
* @return IlluminateHttpResponse
*/
public function render($request, Exception $e)
{
if ($e instanceof ReflectionException OR $e instanceof SymfonyComponentHttpKernelExceptionNotFoundHttpException) //Si la ruta no existe, mostar view 404.
return response(view('errors.404'), 404);
return parent::render($request, $e);
}
And it looks like this:img
answered Jan 22, 2017 at 17:24
In apache you could be able to put this code in .htaccess file at your main directory and make sure that change AllowOverride Directive to all in httpd confg file
ErrorDocument 404 thepathto404.blade.php
answered Aug 25, 2015 at 4:57
Mohammed ElhagMohammed Elhag
4,2821 gold badge9 silver badges18 bronze badges
3
In config folder app.php change the following code
'debug' => env('APP_DEBUG', true),
In App->Exception->Handler.php Replace Render Function With Below Code
public function render($request, Exception $exception)
{
if ($exception instanceof ModelNotFoundException)
{
return response()->view('errors.404', [], 404);
}
if ($exception instanceof ErrorException) {
return response()->view('errors.500', [], 500);
}
else {
return parent::render($request, $exception);
}
return parent::render($request, $exception);
}
answered Mar 5, 2021 at 18:17
If you want to redirect to the 404 page if the route is not found..
Route::fallback(function () {
return view('404');
});
answered Nov 7, 2022 at 6:46
Не смог найти в сети примера как в такой роут добавить исключение, чтобы если ID не найден выдавало 404 ошибку.
Доки читал, но так и не понял как мне нужно изменить мой роут.
Сейчас у меня роут такой
Route::get('/post/{id}', 'PostModelController@getOne')->where('id', 'd+');
Мой контроллер
<?php
namespace AppHttpControllers;
use AppHttpControllersController;
use IlluminateHttpRequest;
use AppPost;
class PostModelController extends Controller
{
public function post($order = 'date', $dir = '')
{
if($dir=='desc'){
$posts = Post::all()->sortByDesc($order);
}else{
$posts = Post::all()->sortBy($order);
}
return view('test.post', ['posts' => $posts]);
}
public function getOne(Request $request, $id)
{
$post = Post::find($id)->findOrFail($id);
return view('test.postOne', ['post' => $post]);
}
}
сейчас если ввести неверный id — ошибка
Error
Call to a member function findOrFail() on null
Обычно веб-разработчики не следят за ошибками. Если что-то идет не так, то мы частенько видим дефолтный текст ошибки Laravel: «Whoops, something went wrong» или, что еще хуже, код исключения, который посетителю совсем ни к чему. Поэтому я решил написать пошаговую статью о том, как элегантно обрабатывать ошибки и показывать посетителю правильную информацию о произошедшем.
Примечание: в этой статье также будет показан пример создания собственной службы с внедрением зависимости и обработка исключений, создаваемых службой.
Подготовка: Задача поиска пользователя
Итак, у нас есть очень простой пример — форма для поиска пользователя по его идентификатору.
У нас есть два маршрута:
Route::get('/users', 'UserController@index')->name('users.index'); Route::post('/users/search', 'UserController@search')->name('users.search');
И контроллер с двумя методами:
class UserController extends Controller { public function index() { return view('users.index'); } public function search(Request $request) { $user = User::find($request->input('user_id')); return view('users.search', compact('user')); } }
В файле resources/views/users/index.blade.php находится сама форма:
<form action="{{ route('users.search') }}" method="POST"> @csrf <div class="form-group"> <input id="user_id" class="form-control" name="user_id" type="text" value="{{ old('user_id') }}" placeholder="User ID"> </div> <input class="btn btn-info" type="submit" value="Search"> </form>
Если мы ищем существующего пользователя и он найден, мы видим такой результат:
Всё это находится в resources/views/users/search.blade.php:
<h3 class="page-title text-center">User found: {{ $user->name }}</h3> <b>Email</b>: {{ $user->email }} <br> <b>Registered on</b>: {{ $user->created_at }}
Но это в идеальном случае, а что, если пользователь не найден?
Обработка исключений
Давайте вернемся в реальный мир. Мы не проверяем существование пользователя, в контроллере мы только делаем:
$user = User::find($request->input('user_id'));
И, если пользователь не найден, то получим это:
Или, понятное дело, мы можем задать APP_DEBUG=false в файле .env, и тогда браузер просто покажет пустую страницу с «Whoops, looks like something went wrong». Но для нашего посетителя все это совершенно бесполезно.
Еще одно быстрое исправление, которое мы можем сделать, это использовать User::findOrFail() вместо просто find() — тогда, если пользователь не найден, Laravel покажет страницу 404 с текстом «Sorry, the page you are looking for could not be found». Но это дефолтная страница ошибки для всего сайта. Для посетителя она, опять таки, бесполезна.
Поэтому нам нужно отлавливать ошибки, обрабатывать их и перенаправлять обратно в форму с действительно понятным сообщением об ошибке.
Нам нужно знать тип исключения и имя класса, которое оно будет возвращать. В случае с findOrFail() будет выдано Eloquent исключение ModelNotFoundException, поэтому нам нужно сделать так:
public function search(Request $request) { try { $user = User::findOrFail($request->input('user_id')); } catch (ModelNotFoundException $exception) { return back()->withError($exception->getMessage())->withInput(); } return view('users.search', compact('user')); }
Теперь давайте покажем ошибку в Blade:
<h3 class="page-title text-center">Search for user by ID</h3> @if (session('error')) <div class="alert alert-danger">{{ session('error') }}</div> @endif <form action="{{ route('users.search') }}" method="POST">...</form>
Результат:
Отлично, мы отобразили ошибку! Но это еще не идеал, верно? Вместо $exception->getMessage() нам нужно показать наше собственное сообщение:
return back()->withError('User not found by ID ' . $request->input('user_id'))->withInput();
Финальный вариант!
Перемещаем обработку ошибок в Сервис
На данный момент мы взяли очень простой пример одного действия в контроллере — поиск пользователя. В реальном приложении все гораздо сложнее — обычно контроллер вызывает какой-нибудь внешний сервис или метод пакета, которые могут завершиться с какими-угодно ошибками.
Давайте создадим свой собственный сервис, который, по сути, делает то же самое, но будет генерировать исключение, поэтому контроллеру даже не потребуется знать текст сообщения.
Давайте переместим нашу логику в app/Services/UserService.php:
namespace AppServices; use AppUser; use IlluminateDatabaseEloquentModelNotFoundException; class UserService { public function search($user_id) { $user = User::find($user_id); if (!$user) { throw new ModelNotFoundException('User not found by ID ' . $user_id); } return $user; } }
А в контроллере нам нужно вызвать этот сервис. Для начала мы внедрим его в метод __construct():
use AppServicesUserService; class UserController extends Controller { private $userService; public function __construct(UserService $userService) { $this->userService = $userService; } // ...
Если вы не знакомы с внедрением зависимостей и как работает контейнер Laravel IOC, то вот официальная документация.
А вот так теперь выглядит наш метод search():
public function search(Request $request) { try { $user = $this->userService->search($request->input('user_id')); } catch (ModelNotFoundException $exception) { return back()->withError($exception->getMessage())->withInput(); } return view('users.search', compact('user')); }
Обратите внимание, мы снова можем использовать $exception->getMessage(), вся проверка ошибок и логика сообщений происходит внутри службы. Мы удалили это из контроллера, он не должен заниматься этим.
Следующий Шаг: Создание собственного класса исключений
Последняя глава этой статьи посвящена улучшению архитектуры. Это когда ваш сервис генерирует свое собственное исключение, связанное с этой конкретной ошибкой, и, в зависимости от ошибки, может быть несколько классов исключений. Хорошим примером такой архитектуры является библиотека Stripe, ее использование выглядит так:
try { // Use Stripe's library to make requests... } catch(StripeErrorCard $e) { // Since it's a decline, StripeErrorCard will be caught $body = $e->getJsonBody(); $err = $body['error']; print('Status is:' . $e->getHttpStatus() . "n"); print('Type is:' . $err['type'] . "n"); print('Code is:' . $err['code'] . "n"); // param is '' in this case print('Param is:' . $err['param'] . "n"); print('Message is:' . $err['message'] . "n"); } catch (StripeErrorRateLimit $e) { // Too many requests made to the API too quickly } catch (StripeErrorInvalidRequest $e) { // Invalid parameters were supplied to Stripe's API } catch (StripeErrorAuthentication $e) { // Authentication with Stripe's API failed // (maybe you changed API keys recently) } catch (StripeErrorApiConnection $e) { // Network communication with Stripe failed } catch (StripeErrorBase $e) { // Display a very generic error to the user, and maybe send // yourself an email } catch (Exception $e) { // Something else happened, completely unrelated to Stripe }
Итак, как мы можем создать наш собственный класс исключений? Очень просто — с помощью команды Artisan:
php artisan make:exception UserNotFoundException
Сгенерируется app/Exceptions/UserNotFoundException.php:
namespace AppExceptions; use Exception; class UserNotFoundException extends Exception { // }
Здесь же ничего нет, верно? Давайте заполним наше исключение логикой.
В этом классе может быть два метода:
- report() — используется, если вы хотите сделать дополнительную запись в журнал — отправить сообщение об ошибке в BugSnag, на почту, в Slack и т. д.
- render() — используется, если вы хотите сделать редирект с ошибкой или вернуть HTTP-ответ (например, свой собственный файл Blade ) непосредственно из класса Exception
Итак, для этого примера мы cделаем метод report():
namespace AppExceptions; use Exception; class UserNotFoundException extends Exception { /** * Report or log an exception. * * @return void */ public function report() { Log::debug('User not found'); } }
А вот так мы вызываем это исключение из контроллера:
public function search(Request $request) { try { $user = $this->userService->search($request->input('user_id')); } catch (UserNotFoundException $exception) { report($exception); return back()->withError($exception->getMessage())->withInput(); } return view('users.search', compact('user')); }
Итак, это всё, что я хотел рассказать вам об обработке исключений и использовании Сервисов.
Я понимаю, что мой пример очень упрощен, и другие люди могут использовать исключения иными способами, но я надеюсь, что в этой статье дан основой обзор исключений и причин, по которым вы должны их использовать.
Подробнее об исключениях и обработке ошибок: официальная документация Laravel.
Автор: Povilas Korop
Перевод: Алексей Широков
Задать вопросы по урокам можно на нашем форуме.