Laravel 5: 当请求需要时处理异常 JSON
Laravel 5: Handle exceptions when request wants JSON
我正在 Laravel 5 通过 AJAX 上传文件。除了一件事,我几乎所有的东西都在工作。
当我尝试上传太大的文件(大于 upload_max_filesize
和 post_max_size
时,我抛出了 TokenMismatchException。
然而,这是意料之中的,因为我知道如果超过这些限制,我的输入将为空。空输入,意味着没有收到 _token
因此为什么负责验证 CSRF 令牌的中间件大惊小怪。
然而,我的问题不是抛出此异常,而是它的呈现方式。当此异常被 Laravel 捕获时,它会为通用 Whoops 页面吐出 HTML(由于我处于调试模式,因此有大量堆栈跟踪)。
处理此异常的最佳方法是什么,以便在 AJAX 上返回 JSON(或在请求 JSON 时),同时保持默认行为?
编辑: 无论抛出异常如何,这似乎都会发生。我刚刚尝试通过 AJAX(数据类型:JSON)向不存在的 'page' 发出请求,试图获得 404,同样的事情发生了 - [=返回 31=],没有任何 JSON 友好。
在您的应用程序中,您应该有 app/Http/Middleware/VerifyCsrfToken.php
。在该文件中,您可以处理中间件的运行方式。因此,您可以检查请求是否为 ajax 并按您喜欢的方式处理。
或者,可能更好的解决方案是将异常处理程序编辑为 return 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);
}
考虑到@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);
}
基于@Jonathon 的处理程序呈现函数,我将修改条件以排除 ValidationException 实例。
// If the request wants JSON + exception is not ValidationException
if ($request->wantsJson() && ( ! $exception instanceof ValidationException))
Laravel 5 returns 验证错误 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);
}
使用@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);
}
我更改了此处找到的几个实现以在 Laravel 5.3 上运行。
主要区别在于我的 return 正确的 HTTP 状态文本
在 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);
}
您可以像这样轻松捕捉 err.response:
axios.post().then().catch(function(err){
console.log(err.response); //is what you want
};
我的方式:
// App\Exceptions\Handler.php
public function render($request, Throwable $e) {
if($request->is('api/*')) {
// Setting Accept header to 'application/json', the parent::render
// automatically transform your request to json format.
$request->headers->set('Accept', 'application/json');
}
return parent::render($request, $e);
}
在 Laravel 8.x 中,你可以做到
app/Http/Exceptions/Handler.php
public function render($request, Throwable $exception)
{
if ($request->wantsJson()) {
return parent::prepareJsonResponse($request, $exception);
}
return parent::render($request, $exception);
}
如果你想总是 return JSON 对于所有例外情况,只需始终调用 parent::prepareJsonResponse
并删除 parent::render
.
当 JSON 使用 APP_DEBUG=true
呈现时,您将获得完整的错误报告和堆栈跟踪。当 APP_DEBUG=false
时,您会收到一条通用消息,以免意外暴露应用程序详细信息。
我正在 Laravel 5 通过 AJAX 上传文件。除了一件事,我几乎所有的东西都在工作。
当我尝试上传太大的文件(大于 upload_max_filesize
和 post_max_size
时,我抛出了 TokenMismatchException。
然而,这是意料之中的,因为我知道如果超过这些限制,我的输入将为空。空输入,意味着没有收到 _token
因此为什么负责验证 CSRF 令牌的中间件大惊小怪。
然而,我的问题不是抛出此异常,而是它的呈现方式。当此异常被 Laravel 捕获时,它会为通用 Whoops 页面吐出 HTML(由于我处于调试模式,因此有大量堆栈跟踪)。
处理此异常的最佳方法是什么,以便在 AJAX 上返回 JSON(或在请求 JSON 时),同时保持默认行为?
编辑: 无论抛出异常如何,这似乎都会发生。我刚刚尝试通过 AJAX(数据类型:JSON)向不存在的 'page' 发出请求,试图获得 404,同样的事情发生了 - [=返回 31=],没有任何 JSON 友好。
在您的应用程序中,您应该有 app/Http/Middleware/VerifyCsrfToken.php
。在该文件中,您可以处理中间件的运行方式。因此,您可以检查请求是否为 ajax 并按您喜欢的方式处理。
或者,可能更好的解决方案是将异常处理程序编辑为 return 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);
}
考虑到@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);
}
基于@Jonathon 的处理程序呈现函数,我将修改条件以排除 ValidationException 实例。
// If the request wants JSON + exception is not ValidationException
if ($request->wantsJson() && ( ! $exception instanceof ValidationException))
Laravel 5 returns 验证错误 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);
}
使用@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);
}
我更改了此处找到的几个实现以在 Laravel 5.3 上运行。 主要区别在于我的 return 正确的 HTTP 状态文本
在 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);
}
您可以像这样轻松捕捉 err.response:
axios.post().then().catch(function(err){
console.log(err.response); //is what you want
};
我的方式:
// App\Exceptions\Handler.php
public function render($request, Throwable $e) {
if($request->is('api/*')) {
// Setting Accept header to 'application/json', the parent::render
// automatically transform your request to json format.
$request->headers->set('Accept', 'application/json');
}
return parent::render($request, $e);
}
在 Laravel 8.x 中,你可以做到
app/Http/Exceptions/Handler.php
public function render($request, Throwable $exception)
{
if ($request->wantsJson()) {
return parent::prepareJsonResponse($request, $exception);
}
return parent::render($request, $exception);
}
如果你想总是 return JSON 对于所有例外情况,只需始终调用 parent::prepareJsonResponse
并删除 parent::render
.
当 JSON 使用 APP_DEBUG=true
呈现时,您将获得完整的错误报告和堆栈跟踪。当 APP_DEBUG=false
时,您会收到一条通用消息,以免意外暴露应用程序详细信息。