Кто-нибудь знает, как лучше всего обрабатывать ошибки в Laravel, есть ли какие-то правила или что-то, чему нужно следовать?
В настоящее время я делаю это:
public function store(Request $request)
{
$plate = Plate::create($request->all());
if ($plate) {
return $this->response($this->plateTransformer->transform($plate));
} else {
// Error handling ?
// Error 400 bad request
$this->setStatusCode(400);
return $this->responseWithError("Store failed.");
}
}
И setStatusCode и responseWithError исходят от отца моего контроллера:
public function setStatusCode($statusCode)
{
$this->statusCode = $statusCode;
return $this;
}
public function responseWithError ($message )
{
return $this->response([
'error' => [
'message' => $message,
'status_code' => $this->getStatusCode()
]
]);
}
Но является ли это хорошим способом обработки ошибок API, я вижу другой способ обработки ошибок в Интернете, что лучше?
Спасибо.






Попробуйте, я использовал это в своем проекте (приложение / Исключения / Handler.php)
public function render($request, Exception $exception)
{
if ($request->wantsJson()) { //add Accept: application/json in request
return $this->handleApiException($request, $exception);
} else {
$retval = parent::render($request, $exception);
}
return $retval;
}
Теперь обработать исключение Api
private function handleApiException($request, Exception $exception)
{
$exception = $this->prepareException($exception);
if ($exception instanceof \Illuminate\Http\Exception\HttpResponseException) {
$exception = $exception->getResponse();
}
if ($exception instanceof \Illuminate\Auth\AuthenticationException) {
$exception = $this->unauthenticated($request, $exception);
}
if ($exception instanceof \Illuminate\Validation\ValidationException) {
$exception = $this->convertValidationExceptionToResponse($exception, $request);
}
return $this->customApiResponse($exception);
}
После этого пользовательского ответа обработчика Api
private function customApiResponse($exception)
{
if (method_exists($exception, 'getStatusCode')) {
$statusCode = $exception->getStatusCode();
} else {
$statusCode = 500;
}
$response = [];
switch ($statusCode) {
case 401:
$response['message'] = 'Unauthorized';
break;
case 403:
$response['message'] = 'Forbidden';
break;
case 404:
$response['message'] = 'Not Found';
break;
case 405:
$response['message'] = 'Method Not Allowed';
break;
case 422:
$response['message'] = $exception->original['message'];
$response['errors'] = $exception->original['errors'];
break;
default:
$response['message'] = ($statusCode == 500) ? 'Whoops, looks like something went wrong' : $exception->getMessage();
break;
}
if (config('app.debug')) {
$response['trace'] = $exception->getTrace();
$response['code'] = $exception->getCode();
}
$response['status'] = $statusCode;
return response()->json($response, $statusCode);
}
Всегда добавляйте Accept: application/json в свой запрос api или json.
Спасибо за отличный ответ! Не все потребители добавляют заголовок Accept, поэтому вместо $request->wantsJson() я проверяю $request->expectsJson() || $request->isJson().
Нет причин реализовывать то, что Laravel уже обрабатывает по умолчанию, проверьте мой ответ
Это то что я хочу
@rkj Я делаю то же самое. Можете ли вы рассказать, как справиться с синтаксической ошибкой или другой ошибкой?
Вы можете использовать $response['message'] = Symfony\Component\HttpFoundation\Response::$statusTexts[$statusCode] вместо длинного переключателя.
На мой взгляд, я бы не усложнил задачу.
Вернуть ответ с кодом ошибки HTTP и настраиваемым сообщением.
return response()->json(['error' => 'You need to add a card first'], 500);
Или, если вы хотите выдать обнаруженную ошибку, вы можете сделать:
try {
// some code
} catch (Exception $e) {
return response()->json(['error' => $e->getMessage()], 500);
}
Вы даже можете использовать это для отправки успешных ответов:
return response()->json(['activeSubscription' => $this->getActiveSubscription()], 200);
Таким образом, независимо от того, какой сервис использует ваш API, он может ожидать получения одинаковых ответов на одни и те же запросы.
Вы также можете увидеть, насколько гибким вы можете сделать это, передав код состояния HTTP.
Laravel уже по умолчанию может управлять ответами json.
Без настройки метода рендеринга в app \ Handler.php вы можете просто выбросить Symfony \ Component \ HttpKernel \ Exception \ HttpException, обработчик по умолчанию распознает, содержит ли заголовок запроса Принять: приложение / json, и соответственно напечатает сообщение об ошибке json.
Если включен режим отладки, он также будет выводить трассировку стека в формате json.
Вот краткий пример:
<?php
...
use Symfony\Component\HttpKernel\Exception\HttpException;
class ApiController
{
public function myAction(Request $request)
{
try {
// My code...
} catch (\Exception $e) {
throw new HttpException(500, $e->getMessage());
}
return $myObject;
}
}
Вот ответ laravel с отключенной отладкой
{
"message": "My custom error"
}
А вот ответ с отладкой на
{
"message": "My custom error",
"exception": "Symfony\\Component\\HttpKernel\\Exception\\HttpException",
"file": "D:\\www\\myproject\\app\\Http\\Controllers\\ApiController.php",
"line": 24,
"trace": [
{
"file": "D:\\www\\myproject\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\ControllerDispatcher.php",
"line": 48,
"function": "myAction",
"class": "App\\Http\\Controllers\\ApiController",
"type": "->"
},
{
"file": "D:\\www\\myproject\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Route.php",
"line": 212,
"function": "dispatch",
"class": "Illuminate\\Routing\\ControllerDispatcher",
"type": "->"
},
...
]
}
Используя HttpException, вызов вернет код статуса http по вашему выбору (в данном случае внутренняя ошибка сервера 500).
Не уверен, почему это не принятый ответ. «Моя пользовательская ошибка» выше заменяет $ e-> getMessage ()
Именно то, что я искал. Спасибо
Это работает, только если вы используете только API. Однако у моего приложения также есть API и нормальные ответы. Поэтому мне нужно 2 разных способа их обработки - даже если я вызываю один и тот же код. Поэтому я не выбрасываю 2 разных типа исключений. Или ваш код тоже так делает, а я этого не понял?
Если заголовок вашего запроса содержит Accept: application/json, он ответит с ошибкой json, если вы выполняете обычный запрос, он ответит страницей с ошибкой html, показывая детали исключения, если вы включите отладку.
Я думаю, что было бы лучше изменить существующее поведение, реализованное в приложение / Исключения / Handler.php, чем переопределять его.
Вы можете изменить JSONResponse, возвращаемый parent::render($request, $exception);, и добавить / удалить данные.
Пример реализации:
приложение / Исключения / Handler.php
use Illuminate\Support\Arr;
// ... existing code
public function render($request, Exception $exception)
{
if ($request->is('api/*')) {
$jsonResponse = parent::render($request, $exception);
return $this->processApiException($jsonResponse);
}
return parent::render($request, $exception);
}
protected function processApiException($originalResponse)
{
if ($originalResponse instanceof JsonResponse){
$data = $originalResponse->getData(true);
$data['status'] = $originalResponse->getStatusCode();
$data['errors'] = [Arr::get($data, 'exception', 'Something went wrong!')];
$data['message'] = Arr::get($data, 'message', '');
$originalResponse->setData($data);
}
return $originalResponse;
}
Используя некоторый код из лучшего ответа @RKJ, я обработал ошибки следующим образом:
Откройте класс "Освещение \ Основание \ Исключения \ Обработчик" и найдите метод с именем "convertExceptionToArray". Этот метод преобразует исключение HTTP в массив, который будет отображаться в качестве ответа. В этом методе я только что изменил небольшой фрагмент кода, который не повлияет на слабую связь.
Так что замените метод convertExceptionToArray на этот
protected function convertExceptionToArray(Exception $e, $response=false)
{
return config('app.debug') ? [
'message' => $e->getMessage(),
'exception' => get_class($e),
'file' => $e->getFile(),
'line' => $e->getLine(),
'trace' => collect($e->getTrace())->map(function ($trace) {
return Arr::except($trace, ['args']);
})->all(),
] : [
'message' => $this->isHttpException($e) ? ($response ? $response['message']: $e->getMessage()) : 'Server Error',
];
}
Теперь перейдите к классу Приложение \ Исключения \ Обработчик и вставьте приведенный ниже код чуть выше метода оказывать:
public function convertExceptionToArray(Exception $e, $response=false){
if (!config('app.debug')){
$statusCode=$e->getStatusCode();
switch ($statusCode) {
case 401:
$response['message'] = 'Unauthorized';
break;
case 403:
$response['message'] = 'Forbidden';
break;
case 404:
$response['message'] = 'Resource Not Found';
break;
case 405:
$response['message'] = 'Method Not Allowed';
break;
case 422:
$response['message'] = 'Request unable to be processed';
break;
default:
$response['message'] = ($statusCode == 500) ? 'Whoops, looks like something went wrong' : $e->getMessage();
break;
}
}
return parent::convertExceptionToArray($e,$response);
}
По сути, мы переопределили метод convertExceptionToArray, подготовили ответное сообщение и вызвали родительский метод, передав ответ в качестве аргумента.
Примечание: это решение не будет работать для Ошибки аутентификации / проверки, но в большинстве случаев обе эти ошибки хорошо обрабатываются Laravel с помощью надлежащих удобочитаемых ответных сообщений.
Вы можете использовать $response['message'] = Symfony\Component\HttpFoundation\Response::$statusTexts[$statusCode] вместо длинного переключателя.
В вашем handler.php Это должно работать для обработки исключения 404.
public function render($request, Throwable $exception ){
if ($exception instanceof ModelNotFoundException) {
return response()->json([
'error' => 'Data not found'
], 404);
}
return parent::render($request, $exception);
}
Для меня лучший способ - использовать конкретное исключение для ответа API.
Если вы используете Laravel версии> 5.5, вы можете создать собственное исключение с методами report() и render(). Используйте команду:
php artisan make:exception AjaxResponseException
Он создаст AjaxResponseException.php по адресу:app/Exceptions/
После этого наполните его своей логикой. Например:
/**
* Report the exception.
*
* @return void
*/
public function report()
{
\Debugbar::log($this->message);
}
/**
* Render the exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @return JsonResponse|Response
*/
public function render($request)
{
return response()->json(['error' => $this->message], $this->code);
}
Теперь вы можете использовать его в своем ...Controller с функциональностью try/catch.
Например по-вашему:
public function store(Request $request)
{
try{
$plate = Plate::create($request->all());
if ($plate) {
return $this->response($this->plateTransformer->transform($plate));
}
throw new AjaxResponseException("Plate wasn't created!", 404);
}catch (AjaxResponseException $e) {
throw new AjaxResponseException($e->getMessage(), $e->getCode());
}
}
Этого достаточно, чтобы сделать ваш код более простым для чтения, красивым и полезным.
С наилучшими пожеланиями!
Можете сослаться на аналогичный вопрос stackoverflow.com/questions/42958875/…