Laravel中的异常处理相关问题怎么理解,是怎样的
Admin 2022-07-07 群英技术资讯 723 次浏览
这篇文章给大家分享的是Laravel中的异常处理相关问题怎么理解,是怎样的。小编觉得挺实用的,因此分享给大家做个参考,文中的介绍得很详细,而要易于理解和学习,有需要的朋友可以参考,接下来就跟随小编一起了解看看吧。前言
异常处理是编程中十分重要但也最容易被人忽视的语言特性,它为开发者提供了处理程序运行时错误的机制,对于程序设计来说正确的异常处理能够防止泄露程序自身细节给用户,给开发者提供完整的错误回溯堆栈,同时也能提高程序的健壮性。
这篇文章我们来简单梳理一下Laravel中提供的异常处理能力,然后讲一些在开发中使用异常处理的实践,如何使用自定义异常、如何扩展Laravel的异常处理能力。
下面话不多说了,来一起看看详细的介绍吧
注册异常Handler
这里又要回到我们说过很多次的Kernel处理请求前的bootstrap阶段,在bootstrap阶段的Illuminate\Foundation\Bootstrap\HandleExceptions 部分中Laravel设置了系统异常处理行为并注册了全局的异常处理器:
class 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']);
if (! $app->environment('testing')) {
ini_set('display_errors', 'Off');
}
}
public function handleError($level, $message, $file = '', $line = 0, $context = [])
{
if (error_reporting() & $level) {
throw new ErrorException($message, 0, $level, $file, $line);
}
}
}
set_exception_handler([$this, 'handleException'])将HandleExceptions的handleException方法注册为程序的全局处理器方法:
public function handleException($e)
{
if (! $e instanceof Exception) {
$e = new FatalThrowableError($e);
}
$this->getExceptionHandler()->report($e);
if ($this->app->runningInConsole()) {
$this->renderForConsole($e);
} else {
$this->renderHttpResponse($e);
}
}
protected function getExceptionHandler()
{
return $this->app->make(ExceptionHandler::class);
}
// 渲染CLI请求的异常响应
protected function renderForConsole(Exception $e)
{
$this->getExceptionHandler()->renderForConsole(new ConsoleOutput, $e);
}
// 渲染HTTP请求的异常响应
protected function renderHttpResponse(Exception $e)
{
$this->getExceptionHandler()->render($this->app['request'], $e)->send();
}
在处理器里主要通过ExceptionHandler的report方法上报异常、这里是记录异常到storage/laravel.log文件中,然后根据请求类型渲染异常的响应生成输出给到客户端。这里的ExceptionHandler就是\App\Exceptions\Handler类的实例,它是在项目最开始注册到服务容器中的:
// bootstrap/app.php /* |-------------------------------------------------------------------------- | Create The Application |-------------------------------------------------------------------------- */ $app = new Illuminate\Foundation\Application( realpath(__DIR__.'/../') ); /* |-------------------------------------------------------------------------- | Bind Important Interfaces |-------------------------------------------------------------------------- */ ...... $app->singleton( Illuminate\Contracts\Debug\ExceptionHandler::class, App\Exceptions\Handler::class );
这里再顺便说一下set_error_handler函数,它的作用是注册错误处理器函数,因为在一些年代久远的代码或者类库中大多是采用PHP那件函数trigger_error函数来抛出错误的,异常处理器只能处理Exception不能处理Error,所以为了能够兼容老类库通常都会使用set_error_handler注册全局的错误处理器方法,在方法中捕获到错误后将错误转化成异常再重新抛出,这样项目中所有的代码没有被正确执行时都能抛出异常实例了。
/**
* Convert PHP errors to ErrorException instances.
*
* @param int $level
* @param string $message
* @param string $file
* @param int $line
* @param array $context
* @return void
*
* @throws \ErrorException
*/
public function handleError($level, $message, $file = '', $line = 0, $context = [])
{
if (error_reporting() & $level) {
throw new ErrorException($message, 0, $level, $file, $line);
}
}
常用的Laravel异常实例
Laravel中针对常见的程序异常情况抛出了相应的异常实例,这让开发者能够捕获这些运行时异常并根据自己的需要来做后续处理(比如:在catch中调用另外一个补救方法、记录异常到日志文件、发送报警邮件、短信)
在这里我列一些开发中常遇到异常,并说明他们是在什么情况下被抛出的,平时编码中一定要注意在程序里捕获这些异常做好异常处理才能让程序更健壮。
扩展Laravel的异常处理器
上面说了Laravel把\App\Exceptions\Handler 注册成功了全局的异常处理器,代码中没有被catch到的异常,最后都会被\App\Exceptions\Handler捕获到,处理器先上报异常记录到日志文件里然后渲染异常响应再发送响应给客户端。但是自带的异常处理器的方法并不好用,很多时候我们想把异常上报到邮件或者是错误日志系统中,下面的例子是将异常上报到Sentry系统中,Sentry是一个错误收集服务非常好用:
public function report(Exception $exception)
{
if (app()->bound('sentry') && $this->shouldReport($exception)) {
app('sentry')->captureException($exception);
}
parent::report($exception);
}
还有默认的渲染方法在表单验证时生成响应的JSON格式往往跟我们项目里统一的JOSN格式不一样这就需要我们自定义渲染方法的行为。
public function render($request, Exception $exception)
{
//如果客户端预期的是JSON响应, 在API请求未通过Validator验证抛出ValidationException后
//这里来定制返回给客户端的响应.
if ($exception instanceof ValidationException && $request->expectsJson()) {
return $this->error(422, $exception->errors());
}
if ($exception instanceof ModelNotFoundException && $request->expectsJson()) {
//捕获路由模型绑定在数据库中找不到模型后抛出的NotFoundHttpException
return $this->error(424, 'resource not found.');
}
if ($exception instanceof AuthorizationException) {
//捕获不符合权限时抛出的 AuthorizationException
return $this->error(403, "Permission does not exist.");
}
return parent::render($request, $exception);
}
自定义后,在请求未通过FormValidator验证时会抛出ValidationException, 之后异常处理器捕获到异常后会把错误提示格式化为项目统一的JSON响应格式并输出给客户端。这样在我们的控制器中就完全省略了判断表单验证是否通过如果不通过再输出错误响应给客户端的逻辑了,将这部分逻辑交给了统一的异常处理器来执行能让控制器方法瘦身不少。
使用自定义异常
这部分内容其实不是针对Laravel框架自定义异常,在任何项目中都可以应用我这里说的自定义异常。
我见过很多人在Repository或者Service类的方法中会根据不同错误返回不同的数组,里面包含着响应的错误码和错误信息,这么做当然是可以满足开发需求的,但是并不能记录发生异常时的应用的运行时上下文,发生错误时没办法记录到上下文信息就非常不利于开发者进行问题定位。
下面的是一个自定义的异常类
namespace App\Exceptions\;
use RuntimeException;
use Throwable;
class UserManageException extends RuntimeException
{
/**
* The primitive arguments that triggered this exception
*
* @var array
*/
public $primitives;
/**
* QueueManageException constructor.
* @param array $primitives
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct(array $primitives, $message = "", $code = 0, Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
$this->primitives = $primitives;
}
/**
* get the primitive arguments that triggered this exception
*/
public function getPrimitives()
{
return $this->primitives;
}
}
定义完异常类我们就能在代码逻辑中抛出异常实例了
class UserRepository
{
public function updateUserFavorites(User $user, $favoriteData)
{
......
if (!$executionOne) {
throw new UserManageException(func_get_args(), 'Update user favorites error', '501');
}
......
if (!$executionTwo) {
throw new UserManageException(func_get_args(), 'Another Error', '502');
}
return true;
}
}
class UserController extends ...
{
public function updateFavorites(User $user, Request $request)
{
.......
$favoriteData = $request->input('favorites');
try {
$this->userRepo->updateUserFavorites($user, $favoritesData);
} catch (UserManageException $ex) {
.......
}
}
}
除了上面Repository列出的情况更多的时候我们是在捕获到上面列举的通用异常后在catch代码块中抛出与业务相关的更细化的异常实例方便开发者定位问题,我们将上面的updateUserFavorites 按照这种策略修改一下
public function updateUserFavorites(User $user, $favoriteData)
{
try {
// database execution
// database execution
} catch (QueryException $queryException) {
throw new UserManageException(func_get_args(), 'Error Message', '501' , $queryException);
}
return true;
}
在上面定义UserMangeException类的时候第四个参数$previous是一个实现了Throwable接口类实例,在这种情景下我们因为捕获到了QueryException的异常实例而抛出了UserManagerException的实例,然后通过这个参数将QueryException实例传递给PHP异常的堆栈,这提供给我们回溯整个异常的能力来获取更多上下文信息,而不是仅仅只是当前抛出的异常实例的上下文信息, 在错误收集系统可以使用类似下面的代码来获取所有异常的信息。
while($e instanceof \Exception) {
echo $e->getMessage();
$e = $e->getPrevious();
}
异常处理是PHP非常重要但又容易让开发者忽略的功能,这篇文章简单解释了Laravel内部异常处理的机制以及扩展Laravel异常处理的方式方法。更多的篇幅着重分享了一些异常处理的编程实践,这些正是我希望每个读者都能看明白并实践下去的一些编程习惯,包括之前分享的Interface的应用也是一样。
总结
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
Trait 是单继承语言(如 PHP )中的一种代码复用机制。这篇文章主要介绍了在 PHP 和 Laravel 中使用 Traits的方法,需要的朋友可以参考下
这篇说的PHP处理高分辨率图片出现问题,就是说,我们能使用的内存最大是8M,但是处理这个图片还需要额外的41bytes,就会导致内存不足,这是一个很严重的错误。在对图片进行操作前需要将图片的所有信息读入内存中,同时还会使用另一部分内存同于处理计算并缓存输出,所以内存大小的使用还是和图片的大小有关。
对于PHP学习,PHP和MySQL是结合使用的,因此我们是需要掌握php连接mysql的方法的。我们在PHP连接MySQL数据库的时候,可以用mysql_connect()与mysqli_connect()这两个函数来检测是否连接成功,但是很多人不清楚两者的区别,下面我们通过实例来了解一下mysql_connect()与mysqli_connect()的区别。
这篇文章主要介绍了PHP实现的权重算法,可用于游戏根据权限来随机物品,涉及php数组遍历、随机数处理及数值运算相关操作技巧,需要的朋友可以参考下
这篇文章主要介绍了php操作redis数据库常见方法,结合实例形式总结分析了PHP操作redis数据库的基本安装、连接、字符串、哈希表、列表、集合等相关操作技巧,需要的朋友可以参考下
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008