php Laravel 5:当请求需要 JSON 时处理异常

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28944097/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 00:09:09  来源:igfitidea点击:

Laravel 5: Handle exceptions when request wants JSON

phpajaxexceptionlaravelcsrf

提问by Jonathon

I'm doing file uploads via AJAX on Laravel 5. I've got pretty much everything working except one thing.

我正在 Laravel 5 上通过 AJAX 上传文件。除了一件事,我几乎所有东西都在工作。

When I try to upload a file that is too big (Bigger than upload_max_filesizeand post_max_sizeI get a TokenMismatchException thrown.

当我尝试上传一个太大的文件时(大于upload_max_filesize并且post_max_size我得到一个 TokenMismatchException 抛出。

This is to be expected however, because I know that my input will be empty if these limits are being exceeded. Empty input, means no _tokenis received hence why the middleware responsible for verifying CSRF tokens is kicking up a fuss.

然而,这是意料之中的,因为我知道如果超出这些限制,我的输入将是空的。空输入,意味着没有_token收到,因此为什么负责验证 CSRF 令牌的中间件大惊小怪。

My issue however is not that this exception is being thrown, it is how it is being rendered. When this exception is being caught by Laravel it's spitting out the HTML for the generic Whoops page (With a load of stack tracing since I'm in debug mode).

然而,我的问题不是抛出这个异常,而是它是如何呈现的。当这个异常被 Laravel 捕获时,它会为通用的 Whoops 页面吐出 HTML(因为我处于调试模式,所以有大量的堆栈跟踪)。

What's the best way to handle this exception so that JSON is returned over AJAX (Or when JSON is requested) while keeping the default behaviour otherwise?

处理此异常以便通过 AJAX 返回 JSON(或在请求 JSON 时)同时保持默认行为的最佳方法是什么?



Edit:This seems to happen regardless of the exception thrown. I've just tried making a request via AJAX (Datatype: JSON) to a 'page' that doesn't exist in an attempt to get a 404 and the same thing happens - HTML is returned, nothing JSON friendly.

编辑:无论抛出的异常如何,这似乎都会发生。我刚刚尝试通过 AJAX(数据类型:JSON)向一个不存在的“页面”发出请求,试图获得 404 并且发生了同样的事情 - 返回了 HTML,没有任何 JSON 友好的内容。

回答by Jonathon

I'm going to take a shot at this one myself taking into account the answer given by @Wader and the comments from @Tyler Crompton:

考虑到@Wader 给出的答案和@Tyler Crompton 的评论,我将亲自尝试一下:

app/Exceptions/Handler.php

应用程序/异常/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);
}

回答by Wader

In your application you should have app/Http/Middleware/VerifyCsrfToken.php. In that file you can handle how the middleware runs. So you could check if the request is ajax and handle that how you like.

在您的应用程序中,您应该有app/Http/Middleware/VerifyCsrfToken.php. 在该文件中,您可以处理中间件的运行方式。因此,您可以检查请求是否为 ajax 并按照您的喜好进行处理。

Alternativly, and probably a better solution, would be to edit the exception handler to return json. See app/exceptions/Handler.php, something like the below would be a starting place

或者,可能是更好的解决方案是编辑异常处理程序以返回 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);
}

回答by Liore Shai

Building on @Jonathon's handler render function, I would just modify the conditions to exclude ValidationException instances.

基于@Jonathon 的处理程序渲染函数,我只需修改条件以排除 ValidationException 实例。

// If the request wants JSON + exception is not ValidationException
if ($request->wantsJson() && ( ! $exception instanceof ValidationException))

Laravel 5 returns validation errors in JSON already if appropriate.

如果合适,Laravel 5 已经在 J​​SON 中返回验证错误。

The full method in App/Exceptions/Handler.php:

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);
}

回答by Joe Alamo

I have altered several implementations found here to work on Laravel 5.3. The main difference is that mine will return the correct HTTP status texts

我已经改变了在这里找到的几个实现来在 Laravel 5.3 上工作。主要区别在于我的将返回正确的 HTTP 状态文本

In your render() function in app\Exceptions\Handler.php add this snippet to the top:

在 app\Exceptions\Handler.php 中的 render() 函数中,将此代码段添加到顶部:

    if ($request->wantsJson()) {
        return $this->renderExceptionAsJson($request, $exception);
    }

Contents of renderExceptionAsJson:

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);
}

回答by Mostafa

you can easily catch err.response like this:

你可以很容易地捕捉到这样的 err.response:

axios.post().then().catch(function(err){


 console.log(err.response);  //is what you want

};

回答by juliochina

Using @Jonathon's code, here's a quick fix for Laravel/Lumen 5.3 :)

使用@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);
}

回答by Wally

Quick note on this... In Laravel 5.5 the exception is named "$exception" and not "$e" in the method.

关于这一点的快速说明...在 Laravel 5.5 中,异常在方法中被命名为“$exception”而不是“$e”。