You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.16 06:54:06