simker

Life is too short, just make it.


  • 首页

  • 归档

  • 分类

  • 标签

  • 关于

  • 音乐

  • 搜索

使用symfony构建restful应用 - restful异常处理

发表于 2019-09-19 更新于 2021-01-13 分类于 PHP 阅读次数: Disqus:
本文字数: 7.3k 阅读时长 ≈ 7 分钟

目标:

上篇文章我们构建了一个基础的 api 接口,本篇文章我们要构建自己的异常处理以及 restful 返回。

抛出异常

我们可以在控制器中主动抛出一个异常,像这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
// src/controll/Word
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

/**
* @Route("/hello")
*/
class Word
{
/**
* @Route("/word",methods={"GET"})
*/
public function index()
{
throw new \Exception('something was wrong!');
}
}

你可以用curl命令来(或者用postman)请求[yourdomain]/hello/word查看效果。

异常捕获

上面我们已经抛出了异常,接下来我们需要捕获这个异常并进行处理。

在symfony中,有这么一个组件:

HttpKernel 组件提供了一个接口,该接口规范了从请求开始并创建适当响应的过程. 无论该系统的架构多么多样,该组件都是任何应用程序或框架的核心。

这个组件对外暴露了一个事件:kernel.exception,可以让我们很轻松的实现自定义异常处理

在config/services.yaml下加入以下代码:

1
2
3
4
# design exception handler
App\EventListener\Exception:
tags:
- { name: kernel.event_listener, event: kernel.exception }

其中,App\EventListener\Exception是我们的异常处理类,通过tags来告知symfony他是kernel.exception事件的监听者。
下面我们来实现App\EventListener\Exception类:

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
<?php
namespace App\EventListener;

use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;

/**
* Class Exception
* @package App\EventListener
*/
class Exception
{
/**
* @param ExceptionEvent $event
* @return ExceptionEvent
*/
public function onKernelException(ExceptionEvent $event)
{
$event->setResponse($this->createJsonRespons($event));

return $event;
}

/**
* @param ExceptionEvent $event
* @return JsonResponse
*/
private function createJsonResponse (ExceptionEvent $event)
{
return JsonResponse::create([
'message' => $event->getException()->getMessage(),
'requestUrl' => $event->getRequest()->getPathInfo()
], $event->getException()->getStatusCode());
}
}

上面的代码中,我们捕获了抛出的异常,并且把异常信息以 json 数据形式返回。

自定义异常处理

上面的代码中我们能够通过onKernelException方法,能够捕获到异常,但是我们并不知道是框架自己的异常还是我们主动抛出的异常。为了区分以及构建更友好的restful结构,我们需要自定义异常。

我们在src目录下创建Exception\Base作为我们异常的基类。

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<?php
namespace App\Exception;

use Symfony\Component\HttpKernel\Exception\HttpException;

/**
* Class Base
* @package App\Exception
*/
class Base extends HttpException
{
/**
* 返回的http状态码
* @var int
*/
protected $status = 500;

/**
* 错误信息
* @var string
*/
protected $message = 'unknown';

/**
* 自定义错误码
* @var int
*/
protected $errorCode = 999;

/**
* 附加数据
* @var array
*/
protected $data = [];

/**
* 允许携带的附加数据key
* @var array
*/
protected $accessKey = ['data', 'errorCode', 'message'];

/**
* Base constructor.
* @param array $errorData
* @param int $statusCode
* @param \Throwable|null $previous
*/
public function __construct (array $errorData = [], int $statusCode = 0, \Throwable $previous = null)
{
foreach ($this->accessKey as $key => $value) {
if (array_key_exists($value, $errorData)) $this->$value = $errorData[$value];
}

if ($statusCode) $this->setStatus($statusCode);

parent::__construct($this->getStatus(), $this->getMessage(), $previous, [], $this->getStatus());
}

...getter or setter
}

以上的代码中,核心的代码即为构造方法。

结合我们的App\EventListener\Exception,我们把他的代码改为如下:

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<?php
namespace App\EventListener;

use App\Exception\Base;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;

/**
* Class Exception
* @package App\EventListener
*/class Exception
{
/**
* @var
*/
private $event;
/**
* @var int
*/
private $statusCode = 500;
/**
* @var int
*/
private $errorCode = 999;
/**
* @var string
*/
private $message = '服务器异常';
/**
* @var array
*/
private $data = [];

/**
* @param ExceptionEvent $event
* @return ExceptionEvent
*/
public function onKernelException(ExceptionEvent $event)
{
$this->setEvent($event);
$exception = $event->getException();
if ($exception instanceof Base) {
$this->setData($exception->getData());
$this->setMessage($exception->getMessage());
$this->setErrorCode($exception->getErrorCode());
$this->setStatusCode($exception->getStatus());

$event->setResponse($this->createJsonResponse());
}

return $event;
}

/**
* @return JsonResponse
*/
private function createJsonResponse ()
{
return JsonResponse::create([
'message' => $this->getMessage(),
'errorCode' => $this->getErrorCode(),
'data' => $this->getData(),
'requestUrl' => $this->getEvent()->getRequest()->getPathInfo()
], $this->getStatusCode());
}

...getter or setter
}

以上的代码我们捕获了所有的异常,并且如果是继承自App\Exception\Base异常的话能够很有好的返回 json 数据,其中包括了

  • status-http 状态码
  • message-异常消息
  • errorCode-自定义错误码
  • data-返回数据
  • requestUrl-当前请求接口

能够应付大部分场景,如果你觉得还不够的话,可以补充。

使用异常

我们已经有了自定义的异常,我们怎么来使用它呢?

我们首先定义一个参数异常类App\Exception\Parameter:

1
2
3
4
5
6
7
8
9
<?php
namespace App\Exception;

class Parameter extends Base
{
protected $status = 400;
protected $errorCode = 10000;
protected $message = "参数错误";
}

什么,才10行代码?没错,就是如此简单。

然后我们可以在控制器使用它了。

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
<?php
// src/controll/Word
namespace App\Controller;

use App\Exception\Parameter;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

/**
* @Route("/hello")
*/
class Word
{
/**
* @Route("/word",methods={"GET"})
*/
public function index()
{
throw new \Exception('something was wrong!');
}

/**
* @Route("/word",methods={"POST"})
*/
public function custom()
{
throw new Parameter();
}
}

为了避免重复,我们定义了另外一个路由hello\word,只不过请求方式换了POST而已。

产品环境下的 500 异常

如上,你已经自定义了异常处理了,但是细心的同学可能发现了,我们的抛出Exception的时候,还是是由框架渲染的,在产品环境下,我们不希望用户看到这些异常。

所以,我们只需要在产品环境下让框架的异常正常返回就可以了。为此我们需要拿到配置环境的值。
在config/services.yaml下加入以下代码:

1
2
3
4
5
6
# design exception handler
App\EventListener\Exception:
tags:
- { name: kernel.event_listener, event: kernel.exception }
+ arguments:
+ $debug: "%kernel.debug%"

这样我们拿到了配置debug的值。异常处理就容易了。

至此,完整的App\EventListener\Exception类:

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
<?php
namespace App\EventListener;

use App\Exception\Base;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;

/**
* Class Exception
* @package App\EventListener
*/
class Exception
{
/**
* @var
*/
private $debug;

/**
* @var
*/
private $event;
/**
* @var int
*/
private $statusCode = 500;
/**
* @var int
*/
private $errorCode = 999;
/**
* @var string
*/
private $message = '服务器异常';
/**
* @var array
*/
private $data = [];

/**
* ExceptionListener constructor.
* @param $debug
*/
public function __construct ($debug) {
$this->debug = $debug;
}

/**
* @param ExceptionEvent $event
* @return ExceptionEvent
*/
public function onKernelException(ExceptionEvent $event)
{
$this->setEvent($event);
$exception = $event->getException();
if ($exception instanceof Base) {
$this->setData($exception->getData());
$this->setMessage($exception->getMessage());
$this->setErrorCode($exception->getErrorCode());
$this->setStatusCode($exception->getStatus());

$event->setResponse($this->createJsonResponse());
return $event;
}

if ($this->debug) {
return $event;
}

// do something else ...
$event->setResponse($this->createJsonResponse());
return $event;
}

/**
* @return JsonResponse
*/
private function createJsonResponse ()
{
return JsonResponse::create([
'message' => $this->getMessage(),
'errorCode' => $this->getErrorCode(),
'data' => $this->getData(),
'requestUrl' => $this->getEvent()->getRequest()->getPathInfo()
], $this->getStatusCode());
}
}

如上,我们已经完成了最复杂的一层构建。接下来我们来实现数据的校验层吧。

enjoy and happy coding!

Cai xian 微信支付

微信支付

Cai xian 支付宝

支付宝

# restful # symfony
使用symfony构建restful应用 - 项目初始化
使用symfony构建restful应用 - 验证器
  • 文章目录
  • 站点概览
Cai xian

Cai xian

A super nice guy!
24 日志
12 分类
15 标签
  1. 1. 目标:
  2. 2. 抛出异常
  3. 3. 异常捕获
  4. 4. 自定义异常处理
  5. 5. 使用异常
  6. 6. 产品环境下的 500 异常
© 2019 – 2021 Cai xian | 70k | 1:04
由 Hexo 强力驱动 v3.9.0
|
主题 – NexT.Pisces v7.3.0
|