evil-corp/cakephp-awsapigateway

CakePHP 的 AwsApiGateway 插件

安装次数: 4,799

依赖: 2

建议者: 0

安全: 0

类型:cakephp-plugin

v2.3.3 2020-08-31 19:36 UTC

This package is auto-updated.

Last update: 2024-09-29 05:02:21 UTC


README

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

变更日志

日期:2020-08-31

  • 为 CakePHP 3.8.* 代码弃用更改

日期: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)
  • 包括用于 API 中使用的模型的 FlattenedFieldsTrait,这简化了本地表与 API 定义之间的映射。
    • 此 trait 简化了本地表与 API 定义之间的映射。
    • 这允许完全分离 API 的定义与表的内结构
    • 还负责将验证错误格式化为与 API 定义的格式一致
  • 包括 UnprocessableEntityException 异常,用于处理 API 接收到的格式和验证错误的数据。
  • 包括异常渲染器 ApiExceptionRenderer,以便正确处理 API 抛出的异常

安装

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

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

注意:请确保您有对存储库的访问/部署的正确权限。

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

  • 向 API 控制器添加组件:$this->loadComponent('EvilCorp/AwsApiGateway.ApiRequest');
  • 向需要它们的表添加 FlattenedFields trait:use EvilCorp\AwsApiGateway\Traits\FlattenedFieldsTrait;
  • app.php 中设置以使用异常渲染器,将 Error.exceptionRenderer 设置为 EvilCorp\AwsApiGateway\Error\ApiExceptionRenderer

结构

EvilCorp\AwsApiGateway\Controller\Component\ApiRequestComponent

  • public $_apiRoute = null
    • 内部变量,用于生成 Link 头。有关更多信息,请参阅 getLinkHeaders
  • public beforeFilter(Event $event)
    • 在此回调中,该组件负责:
      • 检查 X-Amzn-Apigateway-Api-Id 标头是否存在且与配置匹配
      • 确保 Content-Type 标头对于 PUT、POST 和 PATCH 类型的请求是 application/json
      • 确保头部 Acceptapplication/json
  • public beforeRender(Event $event)
    • 在此回调中,该组件负责:
      • 配置Response以包含必要的CORS头部
      • 如果使用过,在Response的paging数组中设置Paginator变量。
      • 如果存在,设置用于分页的Link头部。
      • 确保所有viewVars都已序列化。
    • 在序列化响应之前,也负责清理viewVar _apiRoute
  • protected getLinkHeaders($paging, $controller)
    • 用于生成Link头部。
    • 如果需要,可以从Controller中设置变量_apiRoute以覆盖自动生成URL。当生成的Link头部与期望的路径不一致时使用。例如
        $this->set('_apiRoute', [
            'prefix'                => 'Api/v1',
            'controller'            => 'News',
            'action'                => 'index',
            '_method'               => 'GET',
            'plugin'                => false,
            'owners_association_id' => $this->request->getParam('owners_association_id'),
            '?'                     => $this->request->getQueryParams(),
        ]);
      

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

EvilCorp\AwsApiGateway\Error\ApiExceptionRenderer

  • public UnprocessableEntity($exception)
    • 此异常渲染器为UnprocessableEntityException类型的异常添加了支持。
    • 负责在Response中设置相应的变量。
    • 如果有验证错误,则将其设置在响应对象的'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'
                ],
            ]
        ]
      
      

EvilCorp\AwsApiGateway\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,将每个条目单独传递到$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 中将所有操作封装在一个事务中。
    • 此方法在 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)中保存的其余配置相同。

可用的配置:

'AwsApiGateway' => [
    /*
    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,
]

测试

//TODO