Laravel使用自定义异常类

今夜月明人尽望,不知秋思落谁家

自定义异常类

app\Exceptions\ExportException.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php

namespace App\Exceptions;

use Throwable;

class ExportException extends \Exception
{

protected $message;

public function __construct(string $message = "", int $code = 0, Throwable $previous = null)
{
parent::__construct($message, $code, $previous);

$this->message = $message;
}

public function report()
{
// 这里自定义发生异常发生时要额外做的事情
// 比如发邮件通知管理员
//
}


public function render()
{
// 这里需要给浏览器或者API返回必要的通知信息
// 可以是json 结构, 一般是针对API调用的
// 也可以渲染一个网页, 一般是针对浏览器访问的页面
// 也可以直接重定向到其他网页

return response()->json(['status' => 200, 'message' => $this->message], 503);
}
}

说明:
如果在文件 app\Exceptions\Handler.php 中申明了以下代码, 针对该异常类的report方法将不会在被执行;

1
2
3
4
5
6
7
8
9
10
11
12
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that are not reported.
*
* @var array
*/
protected $dontReport = [

ExportException::class
];

抛出并捕获异常

首先尝试抛出异常:

1
2
3
4
5
try {
$res = 1/0;
} catch (\App\Exceptions\ExportException $e) {
throw new \App\Exceptions\ExportException($e->getMessage());
}

上面这段代码并不会在我们自定义的ExportException中捕获, 而会在全局异常中捕获,原因我们打印出这个异常类型看看
app\Exceptions\Handler.php

1
2
3
4
5
6
public function render($request, Exception $exception)
{
dd(get_class($exception)); // ErrorException

return parent::render($request, $exception);
}

该异常类型属于ErrorException, 并不是我们自定的ExportException, 所以并不会被我们捕获;
我们可以这样, 既然我们已经知道这个异常类似是ErrorException, 那么

1
2
3
4
5
try {
$res = 1/0;
} catch ( ErrorException $e) {
throw new \App\Exceptions\ExportException($e->getMessage());
}

当然也可以直接这样

1
2
3
4
5
try {
$res = 1/0;
} catch ( Exception $e) {
throw new \App\Exceptions\ExportException($e->getMessage());
}

这样就可以用我们自定义的异常类来捕捉从而进一步处理了

1
2
3
4
{
"status": 200,
"message": "Division by zero"
}

关于异常的理解

  • 由程序员设计不足所导致的错误,需要用异常来捕捉和处理
  • 程序在运行过程中, 有可能会发生一些不可预知的错误, 无法用if…else这样的语句来处理的时候.
  • 函数无法满足调用方的期望的时候使用异常, 比如用户要请求一个API获得最新的商品信息, 但是查询结果为空的时候

所以我们自定义的异常可以是在不符合实现给定的情况下抛出的, 而不是真正程序在运行过程中发生了错误, 在Laravel的源码中我们可以找到很多一样的例子
vendor\laravel\framework\src\Illuminate\Foundation\Auth\AuthenticatesUsers.php

1
2
3
4
5
6
protected function sendFailedLoginResponse(Request $request)
{
throw ValidationException::withMessages([
$this->username() => [trans('auth.failed')],
]);
}

vendor\guzzlehttp\psr7\src\functions.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function str(MessageInterface $message)
{
if ($message instanceof RequestInterface) {
$msg = trim($message->getMethod() . ' '
. $message->getRequestTarget())
. ' HTTP/' . $message->getProtocolVersion();
if (!$message->hasHeader('host')) {
$msg .= "\r\nHost: " . $message->getUri()->getHost();
}
} elseif ($message instanceof ResponseInterface) {
$msg = 'HTTP/' . $message->getProtocolVersion() . ' '
. $message->getStatusCode() . ' '
. $message->getReasonPhrase();
} else {
throw new \InvalidArgumentException('Unknown message type');
}

foreach ($message->getHeaders() as $name => $values) {
$msg .= "\r\n{$name}: " . implode(', ', $values);
}

return "{$msg}\r\n\r\n" . $message->getBody();
}