AJAX on Laravel 5.を介してファイルのアップロードを行っています。
大きすぎるファイルをアップロードしようとすると(upload_max_filesize
およびpost_max_size
TokenMismatchExceptionがスローされます。
ただし、これらの制限を超えると入力が空になることがわかっているため、これは予想されることです。入力が空の場合、_token
が受信されるため、CSRFトークンの検証を担当するミドルウェアが大騒ぎしている理由。
ただし、私の問題は、この例外がスローされることではなく、レンダリングされる方法です。この例外がLaravel=によってキャッチされている場合、一般的なWhoopsページのHTMLを吐き出します(デバッグモードになっているため、スタックトレースが大量に発生します)。
JSONがAJAX(またはJSONが要求されたとき)で返され、それ以外の場合はデフォルトの動作を維持するように、この例外を処理する最良の方法は何ですか?
編集:これは、スローされた例外に関係なく発生するようです。 404を取得しようとして存在しない「ページ」に対してAJAX(データ型:JSON)を介してリクエストを作成しようとしましたが、同じことが起こります-HTMLが返されます、JSONフレンドリーなものはありません。
@Waderからの回答と@Tyler Cromptonからのコメントを考慮に入れて、自分でこのショットを撮影します。
app/Exceptions/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。
App\Exceptions\Handler.phpのrender()関数で、このスニペットを先頭に追加します。
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);
}