Vaadin24+SpringBoot3/SpringSecurity6下POST请求参数丢失求助
问题背景
从Vaadin 14升级至24,同步完成Spring Boot 2→3、Spring Security 5→6的升级后,带有@AnonymousAllowed注解的路由接收x-www-form-urlencoded格式的POST请求时出现参数丢失问题。日志显示POST请求已到达应用,payload中包含payRequest参数及对应JSON结构,但后续请求自动转为无参数的GET请求,最终Vaadin视图无法获取到payRequest参数。该功能在Vaadin 14中可正常运行。
核心原因
Vaadin 20+版本的路由机制发生变更:当POST请求直接访问Vaadin路由时,框架会先完成POST请求的基础处理,随后触发UI初始化的GET重定向(即日志中出现的GET /?v-r=init&location=payments-list%2Fexternal-api&query=),这个重定向过程默认不会携带原POST请求的参数。而Vaadin 14不存在该自动重定向逻辑,POST参数可直接传递到视图。
解决方案
方案1:通过Spring MVC端点中转POST参数
创建独立的Spring MVC控制器接收POST请求,将参数存入Session后重定向到Vaadin路由,最后在视图中从Session取出参数:
@Controller @RequestMapping("/payments-list") public class PaymentPostController { @PostMapping("/external-api") public String handlePost(@RequestParam("payRequest") String payRequest, HttpSession session) { session.setAttribute("payRequest", payRequest); return "redirect:/payments-list/external-api"; } }
在Vaadin视图的afterNavigation方法中读取参数:
@Override public void afterNavigation(AfterNavigationEvent event) { String payRequest = (String) VaadinSession.getCurrent().getAttribute("payRequest"); if (payRequest != null) { // 执行业务逻辑处理参数 VaadinSession.getCurrent().removeAttribute("payRequest"); // 用完清理Session,避免重复读取 } }
方案2:自定义Vaadin RequestHandler处理POST参数
自定义RequestHandler拦截POST到目标路由的请求,将参数存入请求属性,供后续UI初始化使用:
@Component public class PostParameterForwardHandler extends AbstractRequestHandler { @Override public boolean handleRequest(VaadinRequest request, VaadinResponse response) throws IOException { if ("POST".equals(request.getMethod()) && request.getPathInfo().contains("/payments-list/external-api")) { String payRequest = request.getParameter("payRequest"); if (payRequest != null) { request.setAttribute("payRequest", payRequest); } } return false; // 放行让后续Vaadin处理逻辑继续执行 } }
在Vaadin视图中读取参数:
@Override public void afterNavigation(AfterNavigationEvent event) { String payRequest = (String) VaadinRequest.getCurrent().getAttribute("payRequest"); // 执行业务逻辑 }
方案3:调整Spring Security配置确保参数传递
检查Spring Security 6的过滤器链配置,确保POST请求参数不会被过滤或拦截:
@Configuration public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .requestMatchers("/payments-list/external-api").permitAll() .anyRequest().authenticated() ) // 如果外部请求无法携带CSRF令牌,可忽略该端点的CSRF校验 .csrf(csrf -> csrf.ignoringRequestMatchers("/payments-list/external-api")); return http.build(); } }
验证建议
- 在中转端点或自定义RequestHandler中添加日志,确认参数被正确接收
- 在Vaadin视图中打印Session或请求属性,验证参数可被正常读取
内容的提问来源于stack exchange,提问作者JohnM

