ccasanovas/cakeapigateway

CakePHP 的 ApiGatewaySDK 插件

安装: 3

依赖: 1

建议者: 0

安全: 0

星标: 0

关注者: 1

分支: 0

开放问题: 0

类型:cakephp-plugin

1.0.5 2023-02-04 23:50 UTC

This package is auto-updated.

Last update: 2024-09-05 03:25:56 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 Headers(https://tools.ietf.org/html/rfc5988
  • 包含 FlattenedFieldsTrait trait,用于 API 中使用的模型,以简化与 API 的兼容性方法和操作创建。
    • 此 Trait 简化了本地表与 API 定义之间的映射。
    • 这允许完全分离 API 定义与表内部结构
    • 它还负责格式化验证错误,以确保与 API 定义的格式相匹配。
  • 包含 UnprocessableEntityException 异常,用于处理 API 收到的格式和验证错误的数据。
  • 包含异常渲染器 ApiExceptionRenderer,以便正确处理 API 触发的异常

安装

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

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

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

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

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

结构

Ccasanovas\ApiGatewaySDK\Controller\Component\ApiRequestComponent

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

Ccasanovas\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()
      ])

Ccasanovas\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格式,而不是像CakePHP那样的格式。
    • 对于每个字段,都有一个包含错误的数组。每个错误都是一个包含其 codemessage 的数组。将CakePHP的验证错误转换为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'
              ],
          ]
      ]

Ccasanovas\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 方法的一个特性是它允许映射关联实体的字段,例如,此映射用于一个与 BankAccounts 通过 hasMany 关联的实体,并且想要通过别名 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的内部事务中。
    • 此方法在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,
]