Laravel 5: обрабатывать исключения, когда запрос хочет JSON
Я делаю загрузку файлов через AJAX на Laravel 5. У меня почти все работает, кроме одного.
Когда я пытаюсь загрузить файл, который слишком велик (больше, чем upload_max_filesize
и post_max_size
я получаю брошенное TokenMismatchException.
Этого следует ожидать, потому что я знаю, что мой ввод будет пустым, если эти лимиты будут превышены. Пустой вход, означает, что нет _token
поэтому почему промежуточное программное обеспечение, ответственное за проверку токенов CSRF, поднимает суету.
Однако моя проблема заключается не в том, что это исключение выбрасывается, а в том, как это делается. Когда это исключение попадает на Laravel, он выплескивает HTML для общей страницы Whoops (с загрузкой трассировки стека, так как я в режиме отладки).
Какой лучший способ справиться с этим исключением, чтобы JSON возвращался через AJAX (или когда запрашивался JSON), в то же время сохраняя поведение по умолчанию?
Изменить: похоже, это происходит независимо от того, что было сделано. Я только что попробовал сделать запрос через AJAX (Datatype: JSON) на "страницу", которая не существует в попытке получить 404, и происходит то же самое - возвращается HTML, нет JSON.
Ответы
Ответ 1
Я собираюсь сделать это самостоятельно, принимая во внимание ответ @Wader и комментарии от @Tyler Crompton:
приложение/исключения /handler.php
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $e
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $e)
{
// If the request wants JSON (AJAX doesn't always want JSON)
if ($request->wantsJson()) {
// Define the response
$response = [
'errors' => 'Sorry, something went wrong.'
];
// If the app is in debug mode
if (config('app.debug')) {
// Add the exception class name, message and stack trace to response
$response['exception'] = get_class($e); // Reflection might be better here
$response['message'] = $e->getMessage();
$response['trace'] = $e->getTrace();
}
// Default response of 400
$status = 400;
// If this exception is an instance of HttpException
if ($this->isHttpException($e)) {
// Grab the HTTP status code from the Exception
$status = $e->getStatusCode();
}
// Return a JSON response with the response array and status code
return response()->json($response, $status);
}
// Default to the parent class' implementation of handler
return parent::render($request, $e);
}
Ответ 2
В вашем приложении у вас должно быть app/Http/Middleware/VerifyCsrfToken.php
. В этом файле вы можете справиться с тем, как работает промежуточное программное обеспечение. Таким образом, вы можете проверить, является ли запрос ajax и обрабатывать то, что вам нравится.
Альтернативно и, вероятно, лучшее решение, было бы отредактировать обработчик исключений, чтобы вернуть json. См. app/exceptions/Handler.php
, что-то вроде ниже будет начальным местом
public function render($request, Exception $e)
{
if ($request->ajax() || $request->wantsJson())
{
$json = [
'success' => false,
'error' => [
'code' => $e->getCode(),
'message' => $e->getMessage(),
],
];
return response()->json($json, 400);
}
return parent::render($request, $e);
}
Ответ 3
Основываясь на функции рендеринга обработчика @Jonathon, я бы просто изменил условия, чтобы исключить экземпляры ValidationException.
// If the request wants JSON + exception is not ValidationException
if ($request->wantsJson() && ( ! $exception instanceof ValidationException))
Laravel 5 возвращает ошибки проверки в JSON, если это необходимо.
Полный метод в App/Exceptions/Handler.php:
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $exception
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
// If the request wants JSON + exception is not ValidationException
if ($request->wantsJson() && ( ! $exception instanceof ValidationException))
{
// Define the response
$response = [
'errors' => 'Sorry, something went wrong.'
];
// If the app is in debug mode
if (config('app.debug'))
{
// Add the exception class name, message and stack trace to response
$response['exception'] = get_class($exception); // Reflection might be better here
$response['message'] = $exception->getMessage();
$response['trace'] = $exception->getTrace();
}
// Default response of 400
$status = 400;
// If this exception is an instance of HttpException
if ($this->isHttpException($exception))
{
// Grab the HTTP status code from the Exception
$status = $exception->getCode();
}
// Return a JSON response with the response array and status code
return response()->json($response, $status);
}
return parent::render($request, $exception);
}
Ответ 4
Я изменил несколько реализаций, найденных здесь для работы над Laravel 5.3. Основное различие заключается в том, что my вернет правильные тексты HTTP-статуса
В вашей функции render() в приложении \Exceptions\Handler.php добавьте этот фрагмент вверху:
if ($request->wantsJson()) {
return $this->renderExceptionAsJson($request, $exception);
}
Содержание renderExceptionAsJson:
/**
* Render an exception into a JSON response
*
* @param $request
* @param Exception $exception
* @return SymfonyResponse
*/
protected function renderExceptionAsJson($request, Exception $exception)
{
// Currently converts AuthorizationException to 403 HttpException
// and ModelNotFoundException to 404 NotFoundHttpException
$exception = $this->prepareException($exception);
// Default response
$response = [
'error' => 'Sorry, something went wrong.'
];
// Add debug info if app is in debug mode
if (config('app.debug')) {
// Add the exception class name, message and stack trace to response
$response['exception'] = get_class($exception); // Reflection might be better here
$response['message'] = $exception->getMessage();
$response['trace'] = $exception->getTrace();
}
$status = 400;
// Build correct status codes and status texts
switch ($exception) {
case $exception instanceof ValidationException:
return $this->convertValidationExceptionToResponse($exception, $request);
case $exception instanceof AuthenticationException:
$status = 401;
$response['error'] = Response::$statusTexts[$status];
break;
case $this->isHttpException($exception):
$status = $exception->getStatusCode();
$response['error'] = Response::$statusTexts[$status];
break;
default:
break;
}
return response()->json($response, $status);
}
Ответ 5
Используя код @Jonathon, здесь быстрое исправление для Laravel/Lumen 5.3 :)
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $e
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $e)
{
// If the request wants JSON (AJAX doesn't always want JSON)
if ($request->wantsJson())
{
// Define the response
$response = [
'errors' => 'Sorry, something went wrong.'
];
// If the app is in debug mode
if (config('app.debug'))
{
// Add the exception class name, message and stack trace to response
$response['exception'] = get_class($e); // Reflection might be better here
$response['message'] = $e->getMessage();
$response['trace'] = $e->getTrace();
}
// Default response of 400
$status = 400;
// If this exception is an instance of HttpException
if ($e instanceof HttpException)
{
// Grab the HTTP status code from the Exception
$status = $e->getStatusCode();
}
// Return a JSON response with the response array and status code
return response()->json($response, $status);
}
// Default to the parent class' implementation of handler
return parent::render($request, $e);
}
Ответ 6
Быстрая заметка об этом... В Laravel 5.5 исключение называется "$ exception", а не "$ e" в методе.