最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Laravel 异常与错误处理机制

    正文概述 转载于:掘金(Rocket)   2021-06-03   534

    Hello,我是Rocket

    这是我参与更文挑战的第2天,活动详情查看:更文挑战

    • Laravel框架是处理错误和异常的机制是什么
    • 工作中如何自定义异常响应
    • 如何修改异常响应页面
    • 接下来带着大家去深入理解

    1、什么时候注册自定义的异常和错误处理程序

    \Illuminate\Foundation\Bootstrap\HandleExceptions 注册异常错误处理程序

    public function bootstrap(Application $app)
    {
            $this->app = $app;
            error_reporting(-1);
            set_error_handler([$this, 'handleError']);//注册错误处理程序
            set_exception_handler([$this, 'handleException']);//注册异常处理程序
            register_shutdown_function([$this, 'handleShutdown']);//会在php中止时执行的函数
            if (! $app->environment('testing')) {
                ini_set('display_errors', 'Off');
            }
    }
    
    

    把符合的error转换成异常类

    public function handleError($level, $message, $file = '', $line = 0, $context = [])
    {
            if (error_reporting() & $level) {
                throw new ErrorException($message, 0, $level, $file, $line);
            }
    }
    

    异常处理 报告+输出页面

    public function handleException($e)
    {
            if (! $e instanceof Exception) {
                //非异常类要转换成FatalThrowableError 
                $e = new FatalThrowableError($e);
            }
    
            try {
                
                $this->getExceptionHandler()->report($e);
            } catch (Exception $e) {
                //
            }
    
            if ($this->app->runningInConsole()) {
                $this->renderForConsole($e);
            } else {
                $this->renderHttpResponse($e);
            }
    }
    
    
    protected function renderForConsole(Exception $e)
    {
            $this->getExceptionHandler()->renderForConsole(new ConsoleOutput, $e);
    }
        
    protected function renderHttpResponse(Exception $e)
    {
            $this->getExceptionHandler()->render($this->app['request'], $e)->send();
    }
    

    监听php脚本中断如果有错误并且错误类型包含就转换从FatalErrorException并且处理异常

    public function handleShutdown()
    {
            if (! is_null($error = error_get_last()) && $this->isFatal($error['type'])) {
                $this->handleException($this->fatalExceptionFromError($error, 0));
            }
    }
    
    protected function isFatal($type)
    {
            return in_array($type, [E_COMPILE_ERROR, E_CORE_ERROR, E_ERROR, E_PARSE]);
    }
    
    protected function fatalExceptionFromError(array $error, $traceOffset = null)
    {
            return new FatalErrorException(
                $error['message'], $error['type'], 0, $error['file'], $error['line'], $traceOffset
           );
    }
    

    获取异常处理程序

    protected function getExceptionHandler()
    {
        //获得异常处理类   app/Exceptions/Handler.php
            return $this->app->make(ExceptionHandler::class);
    }
    

    app/Exceptions/Handler.php 框架扩展的异常类

    class Handler extends ExceptionHandler
    {
    
        public function report(Exception $exception)
        {
            parent::report($exception);
        }
        
        public function render($request, Exception $exception)
        {
            return parent::render($request, $exception);
        }
        
    }
    

    Illuminate\Foundation\Exceptions\Handler 框架基本异常类
    实现了接口 Illuminate\Contracts\Debug\ExceptionHandler

    public function report(Exception $e)
    {
         //是否报告  在dontReport和internalDontReport里的就可以忽略异常
        if ($this->shouldntReport($e)) {
            return;
        }
        
        //如果异常类有自定义报告方法则自定义执行
        if (method_exists($e, 'report')) {
            return $e->report();
        }
    
        try {
            $logger = $this->container->make(LoggerInterface::class);
        } catch (Exception $ex) {
            throw $e;
        }
    
        //记录日志(可以考虑专门抽一个错误日志)
        $logger->error(
            $e->getMessage(),
            array_merge($this->context(), ['exception' => $e]
        ));
    }
    
    
    protected function shouldntReport(Exception $e)
    {
        $dontReport = array_merge($this->dontReport, $this->internalDontReport);
    
        return ! is_null(Arr::first($dontReport, function ($type) use ($e) {
            return $e instanceof $type;
        }));
    }
    
    protected function context()
    {
            try {
                return array_filter([
                    'userId' => Auth::id(),
                    'email' => Auth::user() ? Auth::user()->email : null,
                ]);
            } catch (Throwable $e) {
                return [];
            }
    }
    

    错误输出

    public function render($request, Exception $e)
    {
            //异常类如果存在render就输出自定义的
            if (method_exists($e, 'render') && $response = $e->render($request)) {
                return Router::toResponse($request, $response);
            } elseif ($e instanceof Responsable) {//如果实现了Responsable接口就直接输出toResponse方法
                return $e->toResponse($request);
            }
            //解析异常并转换
            $e = $this->prepareException($e);
    
            if ($e instanceof HttpResponseException) {
                return $e->getResponse();
            } elseif ($e instanceof AuthenticationException) {
                //未登录
                return $this->unauthenticated($request, $e);
            } elseif ($e instanceof ValidationException) {
                //验证异常
                return $this->convertValidationExceptionToResponse($e, $request);
            }
    
            return $request->expectsJson()
                            ? $this->prepareJsonResponse($request, $e)
                            : $this->prepareResponse($request, $e);
    }
    
    
     protected function prepareException(Exception $e)
    {
            if ($e instanceof ModelNotFoundException) {
                $e = new NotFoundHttpException($e->getMessage(), $e);
            } elseif ($e instanceof AuthorizationException) {
                $e = new AccessDeniedHttpException($e->getMessage(), $e);
            } elseif ($e instanceof TokenMismatchException) {//csrf token
                $e = new HttpException(419, $e->getMessage(), $e);
            }
    
            return $e;
    }
    

    prepareJsonResponse

    
    //返回json响应体    
    protected function prepareJsonResponse($request, Exception $e)
      {
            return new JsonResponse(
                $this->convertExceptionToArray($e),
                $this->isHttpException($e) ? $e->getStatusCode() : 500,
                $this->isHttpException($e) ? $e->getHeaders() : [],
                JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
            );
    }
    
    
    protected function convertExceptionToArray(Exception $e)
        {
            return config('app.debug') ? [
                'message' => $e->getMessage(),
                'exception' => get_class($e),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => collect($e->getTrace())->map(function ($trace) {
                    return Arr::except($trace, ['args']);
                })->all(),
            ] : [
                'message' => $this->isHttpException($e) ? $e->getMessage() : 'Server Error',
            ];
        }
    

    prepareResponse

    
    //返回响应体
    protected function prepareResponse($request, Exception $e)
    {
             
            if (! $this->isHttpException($e) && config('app.debug')) {
                //这块是我们本地调试的时候错误页面
                return $this->toIlluminateResponse($this->convertExceptionToResponse($e), $e);
            }
    
            if (! $this->isHttpException($e)) {
                $e = new HttpException(500, $e->getMessage());
            }
    
            return $this->toIlluminateResponse(
                $this->renderHttpException($e), $e
            );
    }
    
    
    //主要是debug = true的时候
    protected function convertExceptionToResponse(Exception $e)
    {
            return SymfonyResponse::create(
                $this->renderExceptionContent($e),
                $this->isHttpException($e) ? $e->getStatusCode() : 500,
                $this->isHttpException($e) ? $e->getHeaders() : []
            );
    }
    
    protected function renderExceptionContent(Exception $e)
    {
            try {
                //debug为true的时候返回
                return config('app.debug') && class_exists(Whoops::class)
                            ? $this->renderExceptionWithWhoops($e)
                            : $this->renderExceptionWithSymfony($e, config('app.debug'));
            } catch (Exception $e) {
                return $this->renderExceptionWithSymfony($e, config('app.debug'));
            }
    }
    
    
    //如果是HttpException异常或者非调试环境
    protected function renderHttpException(HttpException $e)
        {
            //注册错误页面
            $this->registerErrorViewPaths();
    
            //如果存在对应的错误码的错误页就直接视图输出
            if (view()->exists($view = "errors::{$e->getStatusCode()}")) {
                return response()->view($view, [
                    'errors' => new ViewErrorBag,
                    'exception' => $e,
                ], $e->getStatusCode(), $e->getHeaders());
            }
    
            return $this->convertExceptionToResponse($e);
        }
    
    
    

    2、流程图

    Laravel  异常与错误处理机制

    3、如何忽略异常

    4、将错误写入日志

    5、自定义异常响应

    6、修改错误页面

    7、可以自定义方法 在app/Exceptions/Handler.php

    protected function unauthenticated($request, AuthenticationException $exception)
    {
            你的逻辑
    }
    
    protected function convertValidationExceptionToResponse(ValidationException $e, $request)
    {
            你的逻辑
    }
    
     protected function registerErrorViewPaths()
    {   
           你的逻辑这块要看下视图处理
    }
    

    8、如何优雅的线上调试(不用去调错误日志并且筛选)

    
    设置类成员
    
    protected $debug=false;
    
    public function __construct(Container $container)
    {
            $this->container = $container;
            //调试
            $this->debug = isset($_GET['debug'])? $_GET['debug'] == 'h5debug':config('app.debug');
    
    }
    
    
    protected function prepareResponse($request, Exception $e)
    {
             
            if (! $this->isHttpException($e) && $this->debug) {
                //这块是我们本地调试的时候错误页面
                return $this->toIlluminateResponse($this->convertExceptionToResponse($e), $e);
            }
    
            if (! $this->isHttpException($e)) {
                $e = new HttpException(500, $e->getMessage());
            }
    
            return $this->toIlluminateResponse(
                $this->renderHttpException($e), $e
            );
    }
    
    
    
    protected function renderExceptionContent(Exception $e)
    {
            try {
                //debug为true的时候返回
                return $this->debug && class_exists(Whoops::class)
                            ? $this->renderExceptionWithWhoops($e)
                            : $this->renderExceptionWithSymfony($e, config('app.debug'));
            } catch (Exception $e) {
                return $this->renderExceptionWithSymfony($e, config('app.debug'));
            }
    }
    
    
    
    //json请求返回
    protected function convertExceptionToArray(Exception $e)
    {
            return $this->debug? [
                'message' => $e->getMessage(),
                'exception' => get_class($e),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => collect($e->getTrace())->map(function ($trace) {
                    return Arr::except($trace, ['args']);
                })->all(),
            ] : [
                'message' => $this->isHttpException($e) ? $e->getMessage() : 'Server Error',
            ];
    }
    
    
    

    结尾


    起源地下载网 » Laravel 异常与错误处理机制

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元