Shopware前台AJAX提交额外新闻通讯订阅表单时触发AccessDeniedHttpException的问题求助
Shopware前台AJAX提交额外新闻通讯订阅表单时触发AccessDeniedHttpException的问题求助
看起来你遇到的这个问题核心原因是Shopware的PageController不允许通过AJAX请求访问,结合你的错误信息和代码,我来一步步帮你排查解决:
1. 问题根源分析
错误信息“PageController can't be requested via XmlHttpRequest.”明确指向两个核心问题:
- 你的请求处理控制器很可能继承了
Shopware\Storefront\Controller\PageController,而PageController是Shopware专门用于渲染完整页面的控制器类,它内置的XmlHttpRequestListener会直接拦截所有AJAX请求并抛出该异常。 - 你的表单同时使用了Shopware内置的
data-form-auto-submit自动提交逻辑,又手动添加了submit事件监听,这会导致请求处理逻辑冲突,可能丢失AJAX请求所需的必要头信息。
2. 具体解决方案
方案一:修正控制器的父类
确保你的控制器继承自StorefrontController(这是Shopware处理Storefront各类请求包括AJAX的正确基类),而不是PageController:
use Shopware\Storefront\Controller\StorefrontController; class AdditionalNewsletterController extends StorefrontController { // 你的请求处理方法... }
方案二:移除冲突的自定义JS监听
你的表单已经通过data-form-auto-submit="true"和data-form-auto-submit-options配置了Shopware内置的AJAX自动提交逻辑,不需要再手动编写submit事件监听代码。直接删除index.js中相关的监听逻辑即可,Shopware会自动处理AJAX请求,包括携带CSRF令牌和X-Requested-With头。
方案三:完善表单的CSRF令牌
在你的Twig表单中,添加Shopware官方的CSRF令牌生成标签,确保请求通过CSRF验证:
{% sw_extends '@Storefront/storefront/page/account/newsletter.html.twig' %} {% block page_account_overview_newsletter_content_form %} {{ parent() }} <div class="js-additional-option-wrapper"> <form name="additionalOptionForm" method="post" action="{{ path('frontend.account.additionalOption') }}" data-form-auto-submit="true" data-form-auto-submit-options='{"useAjax": true, "ajaxContainerSelector": ".js-additional-option-wrapper"}'> {# 添加Shopware官方CSRF令牌 #} {{ sw_csrf('frontend.account.additionalOption') }} {# 你的表单内容示例 #} <div class="form-group"> <label class="form-check-label"> <input type="checkbox" class="form-check-input" name="additionalOption" value="1" {% if customer.extensions.additionalNewsletterSubscribed %}checked{% endif %}> 订阅专属行业动态通讯 </label> </div> </form> </div> {% endblock %}
方案四:调整控制器的响应类型
因为是AJAX请求,控制器方法应该返回JsonResponse或者渲染HTML片段,而不是完整页面响应:
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; use Shopware\Storefront\Controller\StorefrontController; use Shopware\Core\System\SalesChannel\SalesChannelContext; use Shopware\Core\System\Customer\CustomerEntity; use Shopware\Core\Framework\DataAbstractionLayer\RepositoryInterface; use Symfony\Contracts\Translation\TranslatorInterface; class AdditionalNewsletterController extends StorefrontController { private $customerRepository; private $translator; public function __construct(RepositoryInterface $customerRepository, TranslatorInterface $translator) { $this->customerRepository = $customerRepository; $this->translator = $translator; } #[Route(path: '/account/newsletter/additional-option', name: 'frontend.account.additionalOption', methods: ['POST'], defaults: ['_loginRequired' => true, '_storefront' => true])] public function handleAdditionalOptionSubmission(Request $request, SalesChannelContext $context, CustomerEntity $customer): JsonResponse { $isSubscribed = $request->request->getBoolean('additionalOption'); // 这里处理你的业务逻辑,比如更新客户的扩展字段 $this->customerRepository->update([ [ 'id' => $customer->getId(), 'customFields' => [ 'additional_newsletter_subscribed' => $isSubscribed ] ] ], $context->getContext()); // 返回JSON响应 return new JsonResponse([ 'success' => true, 'message' => $this->translator->trans('additionalNewsletter.updatedSuccessfully') ]); } }
3. 验证测试
完成以上调整后:
- 清除Shopware缓存(执行
bin/console cache:clear命令) - 登录前台账号,测试复选框的自动提交功能
- 在浏览器开发者工具中检查请求头是否携带
X-Requested-With: XMLHttpRequest,以及CSRF令牌是否正确发送
如果仍有问题,可以进一步检查请求的响应状态码和返回内容,定位是否存在其他配置或逻辑问题。
备注:内容来源于stack exchange,提问作者Auhona




