ministryofjustice/zend-rest-module

此包已被弃用,不再维护。没有建议的替代包。

一个用于创建RESTful Web服务的Zend模块。

2.0.0 2018-06-04 14:57 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允许您使用注释XMLYML来配置如何将对象从原始数据序列化和反序列化。

虽然您可以使用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/narwhalGET请求将返回一个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,
        ]
    ]
]

祝您玩得开心!