helmich/flow-resttools

此包已被废弃,不再维护。没有建议的替代包。
对于此包的最新版本(dev-master),没有可用的许可信息。

用于使用TYPO3 Flow创建RESTful Web服务的实用包

安装: 88

依赖者: 1

建议者: 0

安全性: 0

星标: 5

关注者: 5

分支: 2

开放问题: 0

类型:typo3-flow-package

dev-master 2016-01-26 15:00 UTC

This package is auto-updated.

Last update: 2020-01-27 18:47:45 UTC


README

作者

Martin Helmich typo3@martin-helmich.de

概述

此包包含一组辅助类,用于实现使用TYPO3 Flow的RESTful Web服务。

它特别处理以下问题

  • 控制器配置
  • 序列化领域对象
  • 请求体处理
  • 异常处理

控制器配置

此包提供了一个RestController,您可以在实现控制器时使用它。请注意,此类**不**扩展Flow自己的RestController类。

RestController类包含一个默认视图配置,使控制器能够支持JSON、YAML、XML和MSGPACK表示形式(目前仅支持输出。输入仍由Flow的默认MediaConverter处理,它仅支持JSON和XML)。敬请期待更多内容。

序列化领域对象

此包包含将领域对象转换为表示的API。与TYPO3 Flow的JsonView不同,它依赖于根据对象获取器方法返回的值自动构建表示,此包要求为每个领域模型提供显式的规范化类。我更喜欢这种设计,因为它提供了更多关于对象表示形式生成的控制。

通常,对象序列化分为两个步骤

  • 规范化:将领域对象转换为普通PHP数组。此步骤是领域特定的。这意味着您必须为要展示的每个领域对象指定一个规范化器类。这些类必须实现NormalizerInterface(见)并返回一个标量PHP类型--通常是(嵌套的)数组。

  • 序列化:将规范化步骤生成的标量PHP类型转换为字符串表示形式。此步骤不是领域特定的。目前,有JSON、YAML和MessagePack的规范化器。

请求体处理

动机

关于Flow,最让我烦恼的一件事是它对请求体的处理有限。只有当您将资源包装在Flow可以将其映射到请求参数的包装对象中时,反序列化JSON体才会工作。

例如,考虑以下控制器操作

public function testAction(Product $product) {
  // ...
}

为了成功映射$product参数,您的JSON请求体还需要一个"product"属性

{
  "product": {
    # ...
  }
}

解决方案

您可以使用注解 Rest\BodyParam 来表示一个控制器动作参数,该参数应从请求体中填充。

<?php
namespace My\Example\RestApi\Controller;

use Helmich\RestTools\Annotations as Rest;

class TestController {
  /**
   * @Rest\BodyParam("$product", allowProperties={"name", "price"})
   */
  public function testAction(Product $product) {
    // ...
  }
}

allowProperties 键也预先配置了属性映射器,允许映射一定集合的属性(这意味着您不需要在 initialize 方法中显式启用此功能)。

或者,您可以使用 allowAllProperties 键,并将其设置为 true 以允许映射所有属性(请谨慎使用,因为这可能会带来安全风险)。

/**
 * @Rest\BodyParam("$product", allowAllProperties=TRUE)
 */
public function testAction(Product $product) {
  // ...
}

异常处理

此包使用它自己的异常处理程序覆盖了 Flow 的默认异常处理程序。错误处理程序(ProductionRestExceptionHandlerDevelopmentRestExceptionHandler)将未捕获的异常作为 JSON 文档呈现,并尝试根据异常类型猜测适当的 HTTP 响应代码(例如,当属性映射发生错误时,将抛出 400 状态码)。

完整示例

考虑一个简单的域对象 My\Example\Domain\Model\Product,它具有 namequantity 属性。

首先,实现一个规范器,用于将这些类的实例转换为标量值。

<?php
namespace My\Example\RestApi\Normalizer;

use Helmich\RestTools\Rest\Normalizer\NormalizerInterface;
use My\Example\Domain\Model\Product;
use TYPO3\Flow\Annotations as Flow;
use TYPO3\Flow\Persistence\PersistenceManagerInterface;

class ProductNormalizer implements NormalizerInterface
{
  /**
   * @var PersistenceManagerInterface
   * @Flow\Inject
   */
  protected $persistenceManager;

  public function objectToScalar($object)
  {
    if ($object instanceof Product) {
      return [
        'id'              => $this->persistenceManager->getIdentifierByObject($object),
        'name'            => $object->getName(),
        'amount_in_stock' => $object->getQuantity()
      ];
    }
  }
}

然后,在控制器中,您可以将此规范器连接到您的实体类。

<?php
namespace My\Example\Controller;

use My\Example\Domain\Model\Product;
use My\Example\Domain\Repository\ProductRepository;
use My\Example\RestApi\Normalizer\ManufacturerNormalizer;
use Helmich\RestTools\Annotations as Rest;
use Helmich\RestTools\Mvc\Controller\RestController;
use Helmich\RestTools\Mvc\View\SerializingViewInterface;
use TYPO3\Flow\Annotations as Flow;
use TYPO3\Flow\Mvc\View\ViewInterface;

class ProductController extends RestController
{
  /**
   * @var ProductRepository
   * @Flow\Inject
   */
  protected $productRepository;

  public function initializeView(ViewInterface $view)
  {
    if ($view instanceof SerializingViewInterface) {
      $view->registerNormalizerForClass(Product::class, new new ProductNormalizer());
    }
  }

  public function listAction()
  {
    if ($view instanceof SerializingViewInterface) {
      $this->view->setRootElement('products');
    }
    $this->view->assign('products', $this->productRepository->findAll());
  }

  /**
   * @Rest\BodyParam("$product", allowAllProperties=TRUE)
   */
  public function createAction(Product $product)
  {
    $this->productRepository->add($product);
  }
}