aeris/zend-rest-module

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

v1.1.4 2015-10-07 19:46 UTC

This package is not auto-updated.

Last update: 2024-09-24 01:49:15 UTC


README

Aeris的Zend Framework 2模块,可帮助您创建RESTful Web服务。

安装

ZendRestModule可以通过composer安装。

php composer.phar require aeris/zend-rest-module

然后在 /config/application.config.php'modules' 配置中添加 'Aeris\ZendRestModule'

特性

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 响应中看到动物 id 和名称,但在 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响应。

示例

本例配置了在Animal Web服务中发生的错误时的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,
        ]
    ]
]

祝您玩得开心!