如何在不刷新页面、不重定向的情况下从Thymeleaf视图触发Spring MVC控制器方法
问题的核心在于你当前用的是传统同步表单提交,浏览器会在提交后自动跳转到请求的URL,这就是页面刷新/跳转的原因。要实现只执行业务逻辑、不刷新页面的效果,我们需要用异步AJAX请求来替代表单提交,之后再通过JavaScript手动处理DOM元素的移除。
下面是具体的实现步骤:
1. 修改HTML按钮结构
首先,我们不需要传统的<form>标签(因为会触发同步提交),直接给按钮添加标识来存储itemId,同时给对应的item行添加标记,方便后续移除:
<!-- 'block item' button code--> <div class="row item-row" data-item-id="${item.itemId}"> <button type="button" class="btn btn-warning block-item-btn" data-item-id="${item.itemId}">block item</button> </div>
这里用data-item-id属性存储物品ID,给按钮添加统一的类名block-item-btn,给父容器添加item-row类,方便后续定位要移除的元素。
2. 编写异步请求的JavaScript代码
使用现代的fetch API发送异步GET请求,监听按钮点击事件,请求成功后移除对应的item行:
// 页面加载完成后绑定事件 document.addEventListener('DOMContentLoaded', function() { // 给所有"block item"按钮绑定点击事件 document.querySelectorAll('.block-item-btn').forEach(button => { button.addEventListener('click', async function() { const itemId = this.dataset.itemId; try { // 发送异步GET请求到控制器 const response = await fetch(`/items/block/${itemId}`, { method: 'GET', headers: { 'Accept': 'application/json' // 可选,标识接受JSON响应 } }); if (response.ok) { // 请求成功:移除对应的item行 const targetRow = document.querySelector(`.item-row[data-item-id="${itemId}"]`); if (targetRow) { targetRow.remove(); } } else { // 请求失败:提示用户 alert('Failed to block the item. Please try again.'); } } catch (error) { // 捕获网络错误 console.error('Error blocking item:', error); alert('An unexpected error occurred.'); } }); }); });
3. 控制器代码(可选优化)
你当前的控制器代码已经可以正常工作,因为@ResponseStatus(HttpStatus.OK)配合void返回值会告诉Spring MVC返回200状态码,不需要额外处理。如果想更明确地返回响应,可以改成返回ResponseEntity<Void>:
@GetMapping("/items/block/{itemId}") public ResponseEntity<Void> blockItem(@PathVariable String itemId) { itemService.blockItem(itemId); return ResponseEntity.ok().build(); }
两种写法都能满足需求,选你觉得更清晰的即可。
4. 注意:CSRF防护处理
如果你的Spring Boot应用启用了CSRF防护(默认是启用的),需要在请求中携带CSRF令牌,否则会被拦截。可以在HTML的<head>中添加以下meta标签:
<meta name="_csrf" content="${_csrf.token}"/> <meta name="_csrf_header" content="${_csrf.headerName}"/>
然后修改JavaScript中的fetch请求,在headers里添加CSRF令牌:
// 获取CSRF令牌和头部名称 const csrfToken = document.querySelector('meta[name="_csrf"]').content; const csrfHeader = document.querySelector('meta[name="_csrf_header"]').content; // 修改fetch的headers部分 headers: { 'Accept': 'application/json', [csrfHeader]: csrfToken }
这样修改后,点击"block item"按钮时,浏览器会在后台发送异步请求,控制器执行业务逻辑,页面不会刷新,请求成功后对应的item行会被自动移除,完全符合你的需求。
内容的提问来源于stack exchange,提问作者DimaSan

