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.

Я собираюсь сделать это самостоятельно, принимая во внимание ответ @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); } 

В вашем приложении у вас должно быть 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); } 

Основываясь на функции рендеринга обработчика @ 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); } 

Я изменил несколько реализаций, найденных здесь для работы над 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); } 

Используя код @ 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); }