ministryofjustice / zend-rest-module
一个用于创建RESTful Web服务的Zend模块。
Requires
- php: >=7.1
- doctrine/common: ~2.5
- jms/serializer: ^1.10
- zendframework/zendframework: ^2.3
Requires (Dev)
- aeris/guzzle-http-mock: ^1.0.0
- mockery/mockery: ^0.9
- phpunit/phpunit: ^7.2
This package is not auto-updated.
Last update: 2022-05-28 17:11:12 UTC
README
一个Zend Framework 2模块,可以帮助您创建RESTful Web服务。
安装
可以通过composer安装ZendRestModule。
php composer.phar require aeris/zend-rest-module
然后将'Aeris\ZendRestModule'
添加到/config/application.config.php
中的'modules'
配置中。
特性
ZendRestModule提供了一些特性,以简化创建RESTful Web服务的过程。
- 实体序列化
- RESTful异常处理
- 测试助手
序列化器
ZendRestModule使用流行的JMS Serializer来序列化和反序列化应用程序模型。
JMS Serializer允许您使用注释、XML或YML来配置如何将对象从原始数据序列化和反序列化。
虽然您可以使用JMS serializer独立使用,但ZendRestModule引入了一些行为,使得在Zend API的上下文中序列化和反序列化变得更加容易。
下面是一个没有使用ZendRestModule的RESTful控制器示例
自动实体序列化
class AnimalRestController extends AbstractRestfulController { public function get($id) { // Grab the animal entity from the database $animal = $this ->animalRepository ->find($id); // Serialize the animal into a JSON string $jsonString = $this ->serviceManager ->get('the_jms_serializer_service_I_configured_on_my_own') ->serialize($animal, 'json'); // But JsonModel expects an array, so we need // deserialize the JSON string back into a php array. $serializedData = json_decode($jsonString); return new JsonModel($serializedData); } }
使用ZendRestModule,您只需简单地返回原始模型即可
class AnimalRestController extends AbstractRestfulController { public function get($id) { return $this ->animalRepository ->find($id); } }
ZendRestModule将拦截返回值,将模型转换为SerializedJsonModel
,然后根据您的JMS Serializer配置来序列化数据。
将数据反序列化到现有实体
默认情况下,JMS Serializer允许您将原始数据反序列化为实体对象。
class AnimalRestController extends AbstractRestfulController { public function create($data) { // Deserialize the raw JSON data into // a MyApp\Entity\Animal object $animal = $this->serviceManager ->get('Aeris\ZendRestModule\Serializer') ->deserialize($data, 'MyApp\Entity\Animal'); $this->saveToDb($animal); return $animal; } }
ZendRestModule包含一个JMS对象构造器扩展,允许您将数据反序列化到现有实体。这在PUT请求中非常有用,其中请求数据可能不是一个完全定义的实体。
class AnimalRestController extends AbstractRestfulController { public function update($id, $data) { $animal = $this->entityManager ->getRepo('MyApp\Entity\Animal') ->find($id); // Update the existing animal from the // PUT data $this->serviceManager ->get('Aeris\ZendRestModule\Serializer') ->deserialize($data, $animal); $this->saveToDb($animal); return $animal; } }
请参阅这篇文章,了解这是如何以及为什么可以工作的。
序列化组
JMS Serializer允许您为实体配置序列化组。这对于设置不同请求返回的数据很有用。例如,我可能只想在getList
响应中看到动物的症状和名称,但在get
响应中看到更多详细信息。
use JMS\Serializer\Annotation as JMS; class Animal { /** * @JMS\Groups({"animalSummary", "animalDetails"}) * @var int */ public $id; /** * @JMS\Groups({"animalSummary", "animalDetails"}) * @var string */ public $name; /** * @JMS\Groups({"animalDetails"}) * @var string */ public $species; /** * @JMS\Groups({"animalDetails"}) * @var string */ public $color; /** * @JMS\Groups({"dates"}) * @var */ public $birthDate; }
Aeris\ZendRestModule\View\Annotation\Groups
注释允许您为每个控制器操作配置要使用的序列化组。
use Aeris\ZendRestModule\View\Annotation as View;
class AnimalRestController extends AbstractRestfulController {
/**
* @View\Groups({"animalDetails", "dates"})
*/
public function get($id) {
return $this->entityManager->find($id);
}
/**
* @View\Groups({"animalSummary"})
*/
public function getList() {
return $this->entityManager->findAll();
}
}
您还可以在zend_rest
配置中配置序列化组。
[ 'controllers' => [ 'invokables' => [ 'MyApp\Animals\AnimalRest' => '\MyApp\Animals\AnimalRestController', ] ], 'zend_rest' => [ 'controllers' => [ 'MyApp\Animals\AnimalRest' => [ 'serialization_groups' => [ 'get' => ['animalDetails', 'dates'], 'getList' => ['animalSummary'] ] ] ] ] ]
其他序列化组件
DateTimeTimestampHandler
在Unix时间戳和\DateTime
对象之间进行序列化和反序列化。
class Animal { /** * @JMS\Type("DateTimeTimestamp") * * @var \DateTime */ public $birthDate; }
现在序列化器将把出生日期时间戳反序列化为\DateTime对象,并将出生日期序列化为时间戳。
序列化器配置参考
[
'zend_rest' => [
'serializer' => [
// Implementations of \JMS\Serializer\Handler\SubscribingHandlerInterface
// See http://jmsyst.com/libs/serializer/master/handlers
'handlers' => [
// Registers the DateTimeTimestamp Handler by default,
'\Aeris\ZendRestModule\Service\Serializer\Handler\DateTimeTimestampHandler',
],
// Event subscribers
// implementing \JMS\Serializer\EventDispatcher\EventSubscriberInterface.
// May be services or fully qualified class names.
// See http://jmsyst.com/libs/serializer/master/event_system
'subscribers' => [
'MyApp\Serializer\Subscriber\MyAwesomeEventSubscriber'
],
// Event listeners,
// categorized by event name.
// See http://jmsyst.com/libs/serializer/master/event_system
'listeners' => [
'serializer.pre_serialize' => [
function(\JMS\Serializer\EventDispatcher\PreSerializeEvent $event) {
// some fantastic event handling logic
},
],
],
// An implementation of \JMS\Serializer\Naming\PropertyNamingStrategyInterface
// The '\Aeris\ZendRestModule\Service\Serializer\Naming\IdenticalPropertyNamingStrategy` (default)
// fixes a bug in the `\JMS\Serializer\Naming\IdenticalPropertyNamingStrategy`
// See https://github.com/schmittjoh/serializer/issues/334
'naming_strategy' => '\Aeris\ZendRestModule\Service\Serializer\Naming\IdenticalPropertyNamingStrategy',
// An implementation of \JMS\Serializer\Construction\ObjectConstructorInterface
// The 'Aeris\ZendRestModule\Serializer\Constructor\InitializedObjectConstructor' (default)
// allows data to be deserialized onto existing entities.
'object_constructor' => 'Aeris\ZendRestModule\Serializer\Constructor\InitializedObjectConstructor',
// Set to false to disable the @MaxDepth annotation.
// ZendRestModule sets this to true by default.
// Note, however, that the JMSSerializers sets this to false by default.
'enable_max_depth' => true,
]
],
]
RESTful异常处理
ZendRestModule会在MVC事件周期中捕获抛出的错误和异常,并将错误转换为JSON响应。
示例
此示例配置了在Animals Web Service中发生的错误的JSON输出。
// zend-rest.config.php 'zend_rest' => [ 'errors' => [ [ // The `error` can be a Zend\Mvc error string // or the class name of an Exception 'error' => \Zend\Mvc\Application::ERROR_ROUTER_NO_MATCH, // HTTP response code 'http_code' => 404, The `error.code` property of the JSON response object 'application_code' => 'invalid_request', The `error.details` property of the JSON response object 'details' => 'The requested endpoint or action is invalid and not supported.', ], [ 'error' => 'MyApp\Exception\RidiculousAnimalException', 'http_code' => 418, 'applicationCode' => 'ridiculous_animal_error', 'details' => 'The animal you have requested is too riduculous for our web service.', 'on_error' => function(RestErrorEvt $evt) { // This is a good place to log errors $exception = $evt->getError(); error_log($exception); // You can also modify the error view model $viewModel = $evt->getViewModel(); $errorObject = $viewModel->getVariable('error'); $errorObject['animal'] = $exception->getAnimal(); $viewModel->setVariable('error', $errorObject); } ], [ // You should always include a fallback '\Exception' handler. 'error' => '\Exception', 'http_code' => 500, 'application_code' => 'uh_oh', 'details' => 'whoops!', ] ] ]
class AnimalRestController extends AbstractRestfulController { public function get($id) { if ($id === 'narwhal') { throw new RidiculousAnimalException("narwhal"); } return $this->animalRepo->find($id); } }
对/animals/narwhal
的GET
请求将返回一个JSON对象,所以
HTTP Code 418 { "error": { "code": "ridiculous_animal_error", "details": "The animal you have requested is too riduculous for our web service.", "animal": "narwhal" } }
错误还将通过on_error
回调写入服务器日志。
同样,对/not/an/endpoint
的请求将返回一个404错误,带有invalid_request
JSON应用程序代码。
AbstractTestCase
\Aeris\ZendRestModuleTest\AbstractTestCase
是\Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase
的扩展。它提供了一些用于测试RESTful API的实用工具和许多新的断言。在使用ZendRestModule时,不需要使用此测试用例类。
配置参考
[ 'zend_rest' => [ // Required. 'cache_dir' => __DIR__ . '/cache' // Set to true to disable caching. 'debug' => false, // See "Exception Handling" documentation 'errors' => [ [ 'error' => \Zend\Mvc\Application::ERROR_ROUTER_NO_MATCH, 'http_code' => 404, 'application_code' => 'invalid_request', 'details' => 'The requested endpoint or action is invalid and not supported.', 'on_error' => function(RestErrorEvt $evt) { error_log("Someone requested an invalid endpoint."); } ] ], 'controllers' => [ // See "Serialization Groups" documentation. 'serialization_groups' => [ 'MyApp\Controller\UserRest' => [ 'get' => ['details', 'timestamps'], 'getList' => ['summary'] ] ] ], // See "Serializer" documentation 'serializer' => [ 'handlers' [ '\Aeris\ZendRestModule\Service\Serializer\Handler\DateTimeTimestampHandler', ], 'naming_strategy' => '\Aeris\ZendRestModule\Service\Serializer\Naming\IdenticalPropertyNamingStrategy', 'object_constructor' => 'Aeris\ZendRestModule\Serializer\Constructor\InitializedObjectConstructor', 'enable_max_depth' => true, ] ] ]
祝您玩得开心!