zepgram / module-rest
使用 Guzzle 库实现依赖注入模式的 API REST 调用的技术模块
2.0.2
2024-05-06 10:54 UTC
Requires
- php: ^8.1
- guzzlehttp/guzzle: ^7.0
- magento/framework: ^103.0.4
- zepgram/module-base: ~0.0.1
- zepgram/module-json-schema: ~0.1.0
README
概述
Zepgram Rest 是一个技术模块,旨在简化 Magento 2 项目的 REST API 集成开发。利用 Guzzle HTTP 客户端进行依赖注入,该模块提供了一系列强大的功能,旨在减少样板代码,提高性能,并增强调试能力。通过集中管理 REST API 交互并利用 Magento 的内置系统,Zepgram Rest 简化了开发者的实现过程。
功能
Zepgram Rest 提供了几个关键功能,以帮助 Magento 开发者在创建和管理 RESTful 服务时提高效率
- 避免代码重复:通过简单的 di.xml 配置来最小化重复代码。只需创建一个类即可实现 REST API 集成,从而简化开发过程。
- 集中配置:将所有 REST Web 服务配置集中管理,确保一致性和易于维护。
- 内置注册和缓存:利用 Magento 的原生缓存机制和专用注册表来提升 API 的性能和安全性。此功能有助于高效管理数据检索和存储,降低服务器负载。
- 通用日志记录器:通过全面的日志记录系统使调试变得轻松。启用调试模式以记录有关 API 调用的详细信息,包括参数、请求和响应,从而便于故障排除。
- 数据序列化:声明您的请求和结果是否应进行 JSON 序列化。这种灵活性避免了需要多个序列化器实现,轻松满足各种 API 要求。
安装
composer require zepgram/module-rest
bin/magento module:enable Zepgram_Rest
bin/magento setup:upgrade
使用 ApiPool 指南
- 为您的服务创建一个 RequestAdapter 类,该类扩展了抽象类
Zepgram\Rest\Model\RequestAdapter
,此类代表您的服务合同适配器- public const SERVICE_ENDPOINT: 定义服务端点
- dispatch(DataObject $rawData): 初始化您将适配以请求 Web 服务的数据
- getBody(): 实现请求体
- getHeaders(): 实现头部
- getUri(): 实现端点 URI(用于处理动态值)
- getCacheKey(): 实现特定请求的缓存键(您必须定义一个唯一键)
- 创建一个 system.xml 和一个 config.xml,并具有专门的 configName
- section:
rest_api
- group_id:
$configName
- 字段:
base_uri
timeout
is_debug
cache_ttl
- section:
- 在 di.xml 中声明您的服务,通过实现
Zepgram\Rest\Service\ApiProvider
作为虚拟类,您可以通过以下链接进行配置 ApiProviderConfig - 在
Zepgram\Rest\Service\ApiPoolInterface
中声明您的 RequestAdapter 和 ApiProvider- 在
apiProviders[]
中添加一个新条目- 键 是您的自定义 RequestAdapter 完整命名空间
- 值 是您的 ApiProvider 作为虚拟类
- 在
- 在将消耗您的 API 的类中注入 ApiPoolInterface,并使用
$this->apiPool->execute(RequestAdapter::class, $rawData)
,其中- RequestAdapter::class 代表在
apiProviders[]
中声明的请求适配器 - $rawData 是一个动态数据数组,将在
dispatch()
方法中分发。
- RequestAdapter::class 代表在
基本指南实现
您不仅可以在 Zepgram\Rest\Service\ApiPoolInterface
中声明您的类,还可以在专用类中直接注入您的 ApiProvider。
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <!-- rest api --> <virtualType name="CustomApiProvider" type="Zepgram\Rest\Service\ApiProvider"> <arguments> <argument name="requestAdapter" xsi:type="object">Zepgram\Sales\Rest\FoxtrotOrderRequestAdapter</argument> <argument name="configName" xsi:type="string">foxtrot</argument> </arguments> </virtualType> <type name="My\Custom\Model\ConsumerExample"> <arguments> <argument name="apiProvider" xsi:type="object">CustomApiProvider</argument> </arguments> </type> </config>
<?php declare(strict_types=1); namespace My\Custom\Model\Api; use Zepgram\Rest\Exception\InternalException; use Zepgram\Rest\Exception\ExternalException; use Zepgram\Rest\Service\ApiPoolInterface; use Zepgram\Rest\Service\ApiProviderInterface; use Zepgram\Sales\Api\OrderRepositoryInterface; class ConsumerExample { public function __construct( private OrderRepositoryInterface $orderRepository, private ApiProviderInterface $apiProvider ) {} /** * @param int $orderId * @return array */ public function execute(int $orderId): array { // get raw data $order = $this->orderRepository->get($orderId); // send request $result = $this->apiProvider->execute(['order' => $order]); return $result; } }
配置
存储配置
如果您没有声明特定的配置,请求将回退到默认配置。要覆盖默认配置,您必须遵循以下系统配置模式:
rest_api/%configName%/base_uri
XML 配置
您可以通过创建一个虚拟类并按照以下配置定制其注入以满足您的需求,使用 Zepgram\Rest\Service\ApiProvider
配置您的服务。
实现
以下是一个简单的实现示例,使用名为 Foxtrot 的服务,将订单对象作为 rawData。
FoxtrotOrderRequestAdapter.php
<?php declare(strict_types=1); namespace Zepgram\Sales\Rest; use Magento\Framework\DataObject; use Magento\Sales\Api\Data\OrderInterface; use Zepgram\Rest\Model\RequestAdapter; class FoxtrotOrderRequestAdapter extends RequestAdapter { /** @var string */ public const SERVICE_ENDPOINT = 'v1/order/'; /** @var OrderInterface */ private $order; /** * {@inheritDoc} */ public function dispatch(DataObject $rawData): void { $this->order = $rawData->getOrder(); } /** * {@inheritDoc} */ public function getBody(): array { return [ 'orderId' => $this->order->getEntityId(), 'customer' => $this->order->getCustomerEmail(), 'orderTotal' => $this->order->getGrandTotal(), 'version' => '1.0', ]; } /** * {@inheritDoc} */ public function getCacheKey(): ?string { return $this->order->getEntityId(); } }
system.xml
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> <section id="rest_api"> <group id="foxtrot" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Foxtrot</label> <field id="base_uri" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Base URI</label> </field> <field id="timeout" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Timeout</label> </field> <field id="cache_ttl" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Cache TTL</label> </field> <field id="is_debug" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Enable Debug</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> </group> </section> </system> </config>
config.xml
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> <default> <rest_api> <foxtrot> <base_uri>https://foxtrot.service.io</base_uri> <timeout>30</timeout> <cache_ttl>7200</cache_ttl> <is_debug>1</is_debug> </foxtrot> </rest_api> </default> </config>
di.xml
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <!-- rest api --> <virtualType name="FoxtrotOrderApiProvider" type="Zepgram\Rest\Service\ApiProvider"> <arguments> <argument name="requestAdapter" xsi:type="object">Zepgram\Sales\Rest\FoxtrotOrderRequestAdapter</argument> <argument name="configName" xsi:type="string">foxtrot</argument> </arguments> </virtualType> <type name="Zepgram\Rest\Service\ApiPoolInterface"> <arguments> <argument name="apiProviders" xsi:type="array"> <item name="Zepgram\Sales\Rest\FoxtrotOrderRequestAdapter" xsi:type="object">FoxtrotOrderApiProvider</item> </argument> </arguments> </type> </config>
OrderDataExample.php
<?php declare(strict_types=1); namespace Zepgram\Sales\Model; use Zepgram\Rest\Exception\InternalException; use Zepgram\Rest\Exception\ExternalException; use Zepgram\Rest\Service\ApiPoolInterface; use Zepgram\Sales\Api\OrderRepositoryInterface; use Zepgram\Sales\Rest\FoxtrotOrderRequestAdapter; class OrderDataExample { public function __construct( private OrderRepositoryInterface $orderRepository, private ApiPoolInterface $apiPool ) {} /** * @param int $orderId * @throws MyAweSomeTechnicalException * @throws MyAwesomeBusinessException */ public function execute(int $orderId): void { try { // get raw data $order = $this->orderRepository->get($orderId); // send request $result = $this->apiPool->execute(FoxtrotOrderRequestAdapter::class, ['order' => $order]); // handle result $order->setFoxtrotData($result); $this->orderRepository->save($order); } catch (InternalException $e) { $context['context_error'] = 'Magento request is wrong, foxtrot order service could not handle it' // service rejected request for business reason: do something (log, throw, errorMessage..) throw MyAwesomeBusinessException(__('Bad request error'), $e); } catch (ExternalException $e) { $context['context_error'] = 'We could not reach foxtrot order service' // service is unavailable due to technical reason: do something (log, throw, errorMessage..) $this->logger->error($e, $context); throw MyAwesomeTechnicalException(__('Foxtrot server error'), $e); } } }
问题 & 改进
如果在安装或使用过程中遇到问题,请在此 github 仓库中报告。
如果您有改进此模块的好主意,请随时贡献。