Я делаю загрузку файлов через 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); }