关于Laravl自定义异常处理

2024-01-11  本文已影响0人  liurongming

一、版本介绍

PS C:\phpstudy_pro\WWW\api.demo.test> php artisan --version                  
Laravel Framework 10.40.0
PS C:\phpstudy_pro\WWW\api.demo.test> 

二、希望情况

{
    "code": 200,
    "message": "请求成功",
    "data": {
        "token": "125|tZmR9SOUq946dwxdRM7hlrvvGDOnnmeUaA63MYiv3519f747"
    }
}
image.png image.png
{
    "message": "Device name 不能为空。",
    "errors": {
        "device_name": [
            "Device name 不能为空。"
        ]
    }
}
{
    "message": "The GET method is not supported for route api/user/token. Supported methods: POST.",
    "exception": "Symfony\\Component\\HttpKernel\\Exception\\MethodNotAllowedHttpException",
    "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\AbstractRouteCollection.php",
    "line": 122,
    "trace": [
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\AbstractRouteCollection.php",
            "line": 107,
            "function": "requestMethodNotAllowed",
            "class": "Illuminate\\Routing\\AbstractRouteCollection",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\AbstractRouteCollection.php",
            "line": 41,
            "function": "getRouteForMethods",
            "class": "Illuminate\\Routing\\AbstractRouteCollection",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\RouteCollection.php",
            "line": 162,
            "function": "handleMatchedRoute",
            "class": "Illuminate\\Routing\\AbstractRouteCollection",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
            "line": 761,
            "function": "match",
            "class": "Illuminate\\Routing\\RouteCollection",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
            "line": 748,
            "function": "findRoute",
            "class": "Illuminate\\Routing\\Router",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
            "line": 737,
            "function": "dispatchToRoute",
            "class": "Illuminate\\Routing\\Router",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
            "line": 200,
            "function": "dispatch",
            "class": "Illuminate\\Routing\\Router",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 144,
            "function": "Illuminate\\Foundation\\Http\\{closure}",
            "class": "Illuminate\\Foundation\\Http\\Kernel",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php",
            "line": 21,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull.php",
            "line": 31,
            "function": "handle",
            "class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 183,
            "function": "handle",
            "class": "Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php",
            "line": 21,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TrimStrings.php",
            "line": 40,
            "function": "handle",
            "class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 183,
            "function": "handle",
            "class": "Illuminate\\Foundation\\Http\\Middleware\\TrimStrings",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize.php",
            "line": 27,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 183,
            "function": "handle",
            "class": "Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance.php",
            "line": 99,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 183,
            "function": "handle",
            "class": "Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Http\\Middleware\\HandleCors.php",
            "line": 62,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 183,
            "function": "handle",
            "class": "Illuminate\\Http\\Middleware\\HandleCors",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Http\\Middleware\\TrustProxies.php",
            "line": 39,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 183,
            "function": "handle",
            "class": "Illuminate\\Http\\Middleware\\TrustProxies",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 119,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
            "line": 175,
            "function": "then",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
            "line": 144,
            "function": "sendRequestThroughRouter",
            "class": "Illuminate\\Foundation\\Http\\Kernel",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\public\\index.php",
            "line": 51,
            "function": "handle",
            "class": "Illuminate\\Foundation\\Http\\Kernel",
            "type": "->"
        }
    ]
}

三、实现处理

PS C:\phpstudy_pro\WWW\api.demo.test> php artisan make:exception ApiException

   INFO  Exception [C:\phpstudy_pro\WWW\api.demo.test\app\Exceptions\ApiException.php] created successfully.  

修改ApiException.php

<?php

namespace App\Exceptions;

use Exception;

class ApiException extends Exception
{
    public function __construct(string $message = "", int $code = 422, ?Throwable $previous = null)
    {
        parent::__construct($message, $code, $previous);
    }

    public function render()
    {
        return response()->json([
            'code' => $this->code ?? 422,
            'message' => $this->message,
        ]);
    }
}

测试效果:


image.png
{
    "code": 422,
    "message": "用户名或密码不正确!"
}
image.png

一般情况,都可以使用throw new ApiException('用户名或密码不正确!');的形式进行处理。但是系统的ValidationException就不能处理了,那么能不能统一一下呢?当然可以。重写 public function render($request, Throwable $e)即可。

<?php

namespace App\Exceptions;

use Illuminate\Auth\AuthenticationException;
use Illuminate\Contracts\Support\Responsable;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Routing\Router;
use Illuminate\Validation\ValidationException;
use Throwable;

class Handler extends ExceptionHandler
{
    /**
     * The list of the inputs that are never flashed to the session on validation exceptions.
     *
     * @var array<int, string>
     */
    protected $dontFlash = [
        'current_password',
        'password',
        'password_confirmation',
    ];

    /**
     * Register the exception handling callbacks for the application.
     */
    public function register(): void
    {
        $this->reportable(function (Throwable $e) {
            //
        });
    }

    /**
     * 重写render实现
     * @param $request
     * @param Throwable $e
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|mixed|\Symfony\Component\HttpFoundation\Response
     * @throws ApiException
     * @throws \ReflectionException
     */
    public function render($request, Throwable $e)
    {
        if ($e instanceof ValidationException) {// 处理验证异常信息
            throw new ApiException($e->getMessage(), $e->status);
        } else {// 保持原来的实现
            $e = $this->mapException($e);

            if (method_exists($e, 'render') && $response = $e->render($request)) {
                return Router::toResponse($request, $response);
            }

            if ($e instanceof Responsable) {
                return $e->toResponse($request);
            }

            $e = $this->prepareException($e);

            if ($response = $this->renderViaCallbacks($request, $e)) {
                return $response;
            }

            return match (true) {
                $e instanceof HttpResponseException => $e->getResponse(),
                $e instanceof AuthenticationException => $this->unauthenticated($request, $e),
                $e instanceof ValidationException => $this->convertValidationExceptionToResponse($e, $request),
                default => $this->renderExceptionResponse($request, $e),
            };
        }
    }
}

实现效果:
有处理部分


image.png

不处理部分:


image.png

1.新增ApiException.php

php artisan make:exception ApiException

并实现如下:

<?php

namespace App\Exceptions;

use Exception;

class ApiException extends Exception
{
    public function __construct(string $message = "", int $code = 422, ?Throwable $previous = null)
    {
        parent::__construct($message, $code, $previous);
    }

    public function render()
    {
        return response()->json([
            'code' => $this->code ?? 422,
            'message' => $this->message,
        ]);
    }
}

2.修改Exceptions 下的Handler.php

 /**
     * 重写render实现
     * @param $request
     * @param Throwable $e
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|mixed|\Symfony\Component\HttpFoundation\Response
     * @throws ApiException
     * @throws \ReflectionException
     */
    public function render($request, Throwable $e)
    {
        if ($request->ajax() && $e instanceof ValidationException) {// 处理验证异常信息
            throw new ApiException($e->getMessage(), $e->status);
        } else {// 保持原来的实现
            $e = $this->mapException($e);

            if (method_exists($e, 'render') && $response = $e->render($request)) {
                return Router::toResponse($request, $response);
            }

            if ($e instanceof Responsable) {
                return $e->toResponse($request);
            }

            $e = $this->prepareException($e);

            if ($response = $this->renderViaCallbacks($request, $e)) {
                return $response;
            }

            return match (true) {
                $e instanceof HttpResponseException => $e->getResponse(),
                $e instanceof AuthenticationException => $this->unauthenticated($request, $e),
                $e instanceof ValidationException => $this->convertValidationExceptionToResponse($e, $request),
                default => $this->renderExceptionResponse($request, $e),
            };
        }
    }

至此,处理完毕。

上一篇 下一篇

猜你喜欢

热点阅读