API Platform 2.3.5中如何拦截GraphQL请求并实现日志/邮件功能?
我完全懂你在API Platform 2.3.5里遇到的GraphQL拦截难题——想在update类型的mutation执行时做日志记录或发邮件,又不想改动框架核心代码,官方文档关于GraphQL的内容还少得可怜,确实挺挠头的。
给你分享几个不用碰核心代码的可行方案,按精准度和易用性排序:
方案1:用装饰器包装默认的Item Mutation Resolver
这是最直接精准的方式,因为我们可以直接接管GraphQL mutation的执行流程,同时保留原有的逻辑。
步骤1:创建自定义Resolver类
这个类会包装框架默认的ItemMutationResolver,在执行完原始更新逻辑后插入我们的自定义操作:
// src/Resolver/CustomItemMutationResolver.php namespace App\Resolver; use ApiPlatform\Core\GraphQl\Resolver\ItemMutationResolverInterface; use Psr\Log\LoggerInterface; use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Mime\Email; class CustomItemMutationResolver implements ItemMutationResolverInterface { private $originalResolver; private $logger; private $mailer; public function __construct( ItemMutationResolverInterface $originalResolver, LoggerInterface $logger, MailerInterface $mailer ) { $this->originalResolver = $originalResolver; $this->logger = $logger; $this->mailer = $mailer; } public function resolve($item, array $context) { // 先执行框架原生的更新逻辑 $updatedItem = $this->originalResolver->resolve($item, $context); // 只针对update类型的mutation执行自定义逻辑 if ('update' === $context['info']->fieldName) { // 记录更新日志 $this->logger->info(sprintf( 'GraphQL更新了实体:%s,ID:%d', get_class($updatedItem), $updatedItem->getId() )); // 发送通知邮件(示例代码,根据你的需求调整) $email = (new Email()) ->from('system@yourdomain.com') ->to('admin@yourdomain.com') ->subject('实体已更新') ->text(sprintf('实体%s(ID:%d)通过GraphQL完成更新', get_class($updatedItem), $updatedItem->getId())); $this->mailer->send($email); } return $updatedItem; } }
步骤2:配置装饰器
在config/services.yaml里把我们的自定义Resolver注册为默认Resolver的装饰器,这样所有GraphQL的item mutation都会经过我们的逻辑:
services: App\Resolver\CustomItemMutationResolver: decorates: api_platform.graphql.resolver.item_mutation arguments: $originalResolver: '@.inner' # 引用被装饰的原始服务 $logger: '@logger' $mailer: '@mailer'
如果只想针对某一个实体生效,也可以直接在实体的@ApiResource注解里指定这个Resolver:
/** * @ApiResource( * graphql={ * "update"={ * "resolver"="App\Resolver\CustomItemMutationResolver" * } * } * ) */ class YourEntity { // ... 实体代码 }
方案2:利用Doctrine生命周期事件
如果你的更新操作最终会触发Doctrine的持久化,那么可以监听Doctrine的postUpdate事件,同时判断当前请求是否来自GraphQL:
// src/EventListener/GraphQLEntityUpdateListener.php namespace App\EventListener; use Doctrine\ORM\Event\PostUpdateEventArgs; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\RequestStack; class GraphQLEntityUpdateListener { private $logger; private $requestStack; public function __construct(LoggerInterface $logger, RequestStack $requestStack) { $this->logger = $logger; $this->requestStack = $requestStack; } public function postUpdate(PostUpdateEventArgs $args) { $request = $this->requestStack->getCurrentRequest(); // 只处理GraphQL请求(默认路径是/graphql) if (!$request || '/graphql' !== $request->getPathInfo()) { return; } $entity = $args->getObject(); // 可以针对特定实体类型做处理 if ($entity instanceof YourEntity) { $this->logger->info(sprintf( 'GraphQL更新了实体:%s,ID:%d', get_class($entity), $entity->getId() )); // 邮件发送逻辑... } } }
然后在services.yaml里注册监听器:
services: App\EventListener\GraphQLEntityUpdateListener: tags: - { name: doctrine.orm.entity_listener, event: postUpdate }
这个方案的好处是不用改动GraphQL相关代码,但缺点是无法精准区分是GraphQL的update mutation还是其他操作触发的实体更新。
方案对比
- 方案1最推荐:完全针对GraphQL mutation场景,逻辑清晰,扩展性强,能精准控制哪些mutation需要处理。
- 方案2适合快速实现,但通用性强,需要额外判断请求来源和实体类型。
内容的提问来源于stack exchange,提问作者Povilas Gintutis
相关产品推荐
相关产品推荐

