powersystem/cakeapigateway

CakePHP 的 ApiGatewaySDK 插件

安装: 8

依赖项: 1

建议者: 0

安全: 0

星标: 0

关注者: 1

分支: 0

公开问题: 0

类型:cakephp-plugin

1.0.05 2023-02-08 18:38 UTC

This package is not auto-updated.

Last update: 2024-10-03 02:18:21 UTC


README

此插件包含用于创建和部署 AWS ApiGateway 应用程序的实用工具。

变更日志

日期:2022-08-31

  • 将 CakePHP 3.8.* 的过时代码更新到 4.3.*

日期:2019-04-15

  • 为 ApiRequestComponent 添加了新选项,以启用允许的 Content-Types 的自定义

日期:2018-10-10

  • 重写了整个 README,以指明最新的更改
  • 此插件不再与旧版本兼容(旧于 v0.1.0 标签)

文档

功能

  • 包括用于 API 控制器的 ApiRequestComponent 组件,具有以下功能:
    • 确保请求以 JSON 格式正确格式化
    • 验证请求是否来自配置的 ApiGateway
    • 为 APIGateway 启用 CORS
    • 如果使用 PagintorComponent 在控制器中处理数据,则正确格式化分页变量
    • 如果响应包含分页,则添加 Link 头(https://tools.ietf.org/html/rfc5988
  • 包括用于 API 中使用的模型的 FlattenedFieldsTrait 特性,这有助于创建与 API 兼容的方法和操作。
    • 此特性简化了本地表和 API 定义之间的映射。
    • 这允许完全分离 API 定义和表的内部结构。
    • 它还负责格式化验证错误,以与 API 定义的格式相匹配。
  • 包括 UnprocessableEntityException 异常,用于处理 API 接收到的格式和验证错误。
  • 包括异常渲染器 ApiExceptionRenderer,以正确处理 API 触发的异常

安装

要使用 composer 获取此插件,需要在 composer.json 中添加以下内容:

  1. "require" 对象添加插件:"PowerSystem/cake-apigateway": "dev-master"
  2. "repositories" 数组添加对象:{"type": "vcs", "url": "git@bitbucket.org:PowerSystem/cake-apigateway.git"}
  3. 运行 composer update

注意:确保在存储库中具有正确的访问/部署权限。

一旦在存储库中安装了插件,就可以

  • 将其添加到 API 控制器的组件中:$this->loadComponent('PowerSystem/ApiGatewaySDK.ApiRequest');
  • 将 FlattenedFields 特性添加到需要它的表:use PowerSystem\ApiGatewaySDK\Traits\FlattenedFieldsTrait;
  • app.php 中配置应用程序以使用异常渲染器,将 Error.exceptionRenderer 设置为 PowerSystem\ApiGatewaySDK\Error\ApiExceptionRenderer

结构

PowerSystem\ApiGatewaySDK\Controller\Component\ApiRequestComponent

  • public $_apiRoute = null
    • 用于生成 Link 标头的内部变量。有关更多信息,请参阅 getLinkHeaders
  • public beforeFilter(Event $event)
    • 在此回调中,该组件负责
      • 检查是否存在且与配置匹配的 X-Amzn-Apigateway-Api-Id 标头
      • 确保对于 PUT、POST 和 PATCH 类型的请求,标头 Content-Typeapplication/json
      • 确保标头 Acceptapplication/json
  • public beforeRender(Event $event)
    • 在此回调中,该组件负责
      • 配置响应以包含必要的 CORS 标头
      • 如果使用了分页器,则在响应的 paging 数组中设置分页变量。
      • 如果存在,设置分页的 Link 头部。
      • 确保所有 viewVars 都已序列化。
    • 在序列化响应之前,还需要清理 viewVar _apiRoute

PowerSystem\ApiGatewaySDK\Error\UnprocessableEntityException

  • public __construct($message = null, $code = 422)
    • 此异常用于显示 API 请求中的验证错误。
    • 使用错误代码 422,等同于 HTTP 异常 422 Unprocessable Entity
    • 允许通过传入数组的方式在 $message 中设置验证错误。
      throw new UnprocessableEntityException([
          'message' => 'Error de validación!',
          'errors' => $entity->getErrors()
      ])

PowerSystem\ApiGatewaySDK\Error\ApiExceptionRenderer

  • public UnprocessableEntity($exception)
    • 此异常渲染器增加了对 UnprocessableEntityException 类型异常的支持。
    • 负责设置响应中相应的变量。
    • 如果有验证错误,则将其设置在响应对象的 'errors' 变量中。
      throw new UnprocessableEntityException([
          'message' => __('Data Validation Failed'),
          'errors' => $entity->getErrors()
      ]);
      {
        "message": "Data Validation Failed",
        "errors": {
          "email": [{
            "code": "Empty",
            "message": "This field cannot be left empty"
          }]
        },
        "url": "\/api\/v1\/me\/email",
        "code": 422
      }
  • protected formatErrors($entity_errors)
    • 此方法用于将验证错误格式化为更通用的 API 格式,而不是 Cake-like 格式。
    • 对于每个字段,都有一个包含错误的数组。每个错误都是一个包含其 codemessage 的数组。将 Cake 的验证错误转换为 API 标准。
      $errors = [
          'email' => [
              '_empty' => 'This field cannot be left empty',
              'email'  => 'This field must be a valid email address'
          ]
      ];
      
      pr($this->formatErrors($errors));
      //resultado:
      [
          'email' => [
              [
                  'code' => 'Empty',
                  'message' => 'This field cannot be left empty'
              ],
              [
                  'code' => 'Email',
                  'message' => 'This field must be a valid email address'
              ],
          ]
      ]

PowerSystem\ApiGatewaySDK\Traits\FlattenedFieldsTrait

  • public flattenedFieldsMaps()
    • 返回一个包含用于使用此 Trait 的方法的字段映射的数组。**重要的是在表中重写此方法,以便返回满足 API 结构所需的必要映射**。
    • 为了能够使用 getFlattenedEntitysetFlattenedEntity,数组必须定义 'get' 和 'set' 映射。
    • 映射定义为关联数组(clave => valor),其中 clave 对应于结果的字段名,而 valor 对应于输入数据中的字段名。例如
      [
           'get' => [
              'identificador' => 'id',
              'nombre' => 'name'
           ],
           'set' => [
              'id' => 'identificador',
              'name' => 'nombre'
          ]
      ]
    • 查看 mapFlattenedFields 以获取更多信息。
  • protected getFlattenedFieldsMap($map_name, $flip = false)
    • 此方法用于从 flattenedFieldsMaps 内部获取映射。
    • 它还允许在需要时反转映射。
  • protected mapFlattenedFields($entity, $map, $callback)
    • 此方法处理一个实体或数组 $entity,使用 $map,将每个条目单独传递给 $callback 进行处理。
    • 最常见的使用方法是结合 Hash::get,例如
      //dentro de la Table
      $entity = $this->get($id);
      $map    = $this->getFlattenedFieldsMap('get');
      $result = $this->mapFlattenedFields($entity, $map, function($field, $entity){
          return Hash::get($entity, $field);
      });
    • 使用 Hash::get 允许我们以点表示法在映射中定义字段,例如
      [
          'index' => [
              'id'                => 'id',
              'title'             => 'title',
              'category'          => 'news_category.name',
              'thumbnail'         => 'news_images.0.thumbnail',
              'created_at'        => 'created_at',
              'created_by'        => 'creator.full_name',
              'modified_at'       => 'modified_at',
              'modified_by'       => 'modifier.full_name',
          ]
      ]
    • mapFlattenedFields 方法的一个特性是它允许映射关联实体的字段,例如,此映射用于一个通过 hasManyBankAccounts 关联的实体,并希望在别名 cuentas 中公开它们。
      [
          'get' => [
              'id' => 'id',
              'titulo' => 'title',
              'cuentas' => [
                  'entities' => 'bank_accounts',
                  'map' => [
                      'banco'  => 'bank.name',
                      'tipo'   => 'bank_account_type.title',
                      'numero' => 'account_number',
                      'cbu'    => 'account_cbu',
                  ]
              ]
          ]
      ]
  • protected getFlattenedEntity($where = [], $contain = [])
    • 此方法是一个基本包装器,用于演示如何使用映射来处理单个实体。
    • 对于简单情况或作为创建自己的复杂方法的指南,请使用它。
    • 该方法使用在 flattenedFieldsMaps 中定义的 get 映射。
    • 该方法被定义为受保护的,因为预期您将在自己的方法中使用它,而不是直接从控制器中调用它。
    • 参数是透明的,与 CakePHP 3 中创建查询使用的参数等效。
    • 参数 $where 定义查询的条件,将直接传递给 find 而不进行修改。
    • 参数 $contain 定义查询的关联。
    • 基本用法示例:
      //dentro de la Table
      $entity = $this->get($id);
      $mapped_entity = $this->getFlattenedEntity(['id' => $id]);
  • protected setFlattenedEntity(Callable $method, $error_map = null)
    • getFlattenedEntity 不同,此方法不是一个基本的保存包装器,而是用于与您自己的保存方法结合使用。
    • 这通常是由于在API定义中对数据进行简化会导致保存过程更加复杂,且变化多端,难以轻易封装到本插件中。
    • 这种灵活性允许例如,通过单个API端点同时保存到多个表格,所有操作都在setFlattenedEntity内部的transaction中完成。
    • 此方法在try/catch中执行 Callable $method 提供的方法,确保验证或保存过程中的错误保持相同的映射格式。
    • 重要的是Callable使用saveOrFail而不是save,因为catch基于由saveOrFail发出的保存异常来正确格式化验证错误。
    • 在映射验证错误时,方法将使用提供的 $error_map 中的映射,否则将回退到 'set' 映射。
    • 一个典型的实现示例是
      public function setPaymentNotification($flattened_data, $api_user_id)
      {
          //obtiene un error map custom1
          $error_map = $this->getFlattenedFieldsMap('errors');
      
          //llama al método setFlattenedEntity
          return $this->setFlattenedEntity(function() use ($flattened_data, $api_user_id){
      
              /*  encierra el proceso en una transacción para poder guardar en varias tablas y
                  si alguna falla, revertir todos los cambios.
              */
              return $this->getConnection()->transactional(
              function ($connection) use ($flattened_data, $api_user_id){
      
                  //mapeamos con set
                  $fields_map = $this->getFlattenedFieldsMap('set');
                  $data = $this->mapFlattenedFields($flattened_data, $fields_map,
                  function($field, $payment_notification){
                      return Hash::get($payment_notification, $field);
                  });
      
                  //creamos entidad
                  $payment_notification = $this->newEntity($data, [
                      'associated' => ['FunctionalUnits']
                  ]);
      
                  /*  acá también se podrían guardar datos en otras tablas,
                      por ejemplo tablas asociadas a esta entidad.
                  */
      
                  //setteamos algunos campos manualmente
                  $payment_notification->api_user_id = $api_user_id;
      
                  /*  finalmente usamos saveOrFail en todas nuestras operaciones para que
                      el catch ataje las excepciones si el guardado falla.
                  */
                  return $this->saveOrFail($payment_notification, [
                      'associated' => ['FunctionalUnits']
                  ]);
      
              });
          }, $error_map);
      }

配置变量

配置变量与其它配置一样保存在应用配置数组中(默认为 config/app.php)。

可用的配置有

'ApiGatewaySDK' => [
    /*
    Esto es para que las peticiones requieren el api_id que agrega APIGateway a las requests.
    Al setearlo como requerido, las peticiones que no vengan a través de APIGateway no serán aceptadas.
    */
    'api_id'                => 'rlrr9c1dk8',
    'require_api_id_header' => true,
]