Symfony5+ApiPlatform项目抛自定义403异常返回HTML而非JSON问题
问题原因
- 你抛出异常的时机位于Symfony安全组件的认证流程中,此时ApiPlatform默认的异常序列化逻辑尚未接管异常处理,Symfony核心异常处理器会根据请求默认格式返回HTML错误页
- LexikJWTAuthenticationBundle提供的默认登录端点未被ApiPlatform路由系统接管,异常不会自动被转换为Hydra规范的JSON格式
- 你的自定义异常未被纳入ApiPlatform的异常映射配置,无法触发对应的JSON序列化逻辑
- 额外注意:你当前构造异常时对消息做了
json_encode()处理,会导致最终返回的错误描述携带多余转义引号,直接传入字符串即可。
解决方案
方案1:使用自定义异常监听器(适配全场景抛出的自定义异常)
监听kernel.exception事件,捕获MyAccessDeniedException后手动构造符合要求的Hydra格式JSON响应:
<?php namespace App\EventListener; use App\Exception\MyAccessDeniedException; use Symfony\Component\EventDispatcher\Attribute\AsEventListener; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpKernel\Event\ExceptionEvent; use Symfony\Component\HttpKernel\KernelEvents; #[AsEventListener(event: KernelEvents::EXCEPTION)] class AccessDeniedExceptionListener { public function __invoke(ExceptionEvent $event): void { $exception = $event->getThrowable(); if (!$exception instanceof MyAccessDeniedException) { return; } // 仅对期望JSON响应的请求返回JSON格式 $request = $event->getRequest(); if (!in_array('application/json', $request->getAcceptableContentTypes()) && !in_array('application/ld+json', $request->getAcceptableContentTypes())) { return; } $responseData = [ '@context' => '/contexts/Error', '@type' => 'hydra:Error', 'hydra:title' => 'An error occurred', 'hydra:description' => $exception->getMessage(), // 调试环境可添加trace字段,生产环境建议关闭 'trace' => $exception->getTrace() ]; $event->setResponse(new JsonResponse($responseData, 403)); } }
如果未开启服务自动配置,需要在services.yaml中注册该监听器。
方案2:使用ApiPlatform异常映射+用户检查器(更符合规范的实现)
第一步:替换现有LoginListener逻辑,改用Symfony原生用户检查器实现状态校验,这是Symfony推荐的用户状态校验方案:
<?php namespace App\Security; use App\Entity\User; use App\Exception\MyAccessDeniedException; use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\User\UserInterface; class UserChecker implements UserCheckerInterface { public function checkPreAuth(UserInterface $user): void { } public function checkPostAuth(UserInterface $user): void { if (!$user instanceof User) { return; } if ($user->someCondition) { throw new MyAccessDeniedException('Ce compte n\'est plus actif.'); } } }
第二步:在security.yaml中注册用户检查器:
security: firewalls: main: # 其他配置保持不变 user_checker: App\Security\UserChecker
第三步:在config/packages/api_platform.yaml中配置异常映射,让ApiPlatform自动序列化该异常为Hydra格式:
api_platform: exception_to_status: App\Exception\MyAccessDeniedException: 403
同时确保前端请求登录接口时,Accept头设置为application/ld+json或者application/json。
提示:如果配置后登录接口仍返回HTML,可在JWT登录路由配置中添加
defaults: { _api_respond: true }参数,强制ApiPlatform接管该端点的响应渲染:# config/routes.yaml api_login: path: /api/login methods: ['POST'] defaults: _api_respond: true _controller: lexik_jwt_authentication.controller.authentication::authenticate
内容的提问来源于stack exchange,提问作者baronbrn
相关产品推荐
相关产品推荐

