Spring环境下Modal验证失效:页面刷新导致弹窗消失
问题解决:登录注册弹窗提交后重载丢失错误提示的问题
问题背景
为博客网站开发登录和注册功能时,点击登录/注册按钮会触发页面重载,导致弹窗关闭,无法在弹窗内展示「字符过少」这类错误提示信息。尝试过AJAX但未成功,希望错误信息能显示在弹窗对应输入项下方。
现有代码的核心问题
- 登录接口:无论成功失败都返回
redirect:/home,重定向会丢失Model中的错误信息,页面重载后弹窗也不会重新打开。 - 注册接口:校验失败时返回
index页面,虽然尝试添加showModal属性,但页面重载后无法自动唤起注册弹窗,且原有表单输入会丢失。 - 前端表单:默认的同步提交会导致页面刷新,弹窗必然关闭,无法保留上下文展示错误。
解决方案:前后端配合实现AJAX异步提交
第一步:修改后端接口,返回JSON格式的校验结果
登录接口修改
@PostMapping("/login") @ResponseBody public Map<String, Object> login(@RequestParam String username, @RequestParam String password, HttpServletRequest request) { Map<String, Object> result = new HashMap<>(); Authentication authentication; try { authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(username, password) ); SecurityContextHolder.getContext().setAuthentication(authentication); result.put("success", true); result.put("message", "登录成功"); } catch (BadCredentialsException e) { result.put("success", false); result.put("error", "用户名或密码无效"); } return result; }
说明:直接处理登录逻辑,返回JSON结果,避免重定向;如果使用Spring Security默认登录流程,也可以自定义失败处理器返回JSON。
注册接口修改
@PostMapping("/register") @ResponseBody public Map<String, Object> registration(@ModelAttribute("userForm") User userForm, BindingResult bindingResult) { Map<String, Object> result = new HashMap<>(); userValidator.validate(userForm, bindingResult); if (bindingResult.hasErrors()) { // 收集所有字段的错误信息 Map<String, String> errors = new HashMap<>(); bindingResult.getFieldErrors().forEach(error -> { errors.put(error.getField(), error.getDefaultMessage()); }); result.put("success", false); result.put("fieldErrors", errors); return result; } userService.save(userForm); result.put("success", true); result.put("message", "注册成功"); return result; }
说明:校验失败时,将每个字段的错误信息以键值对形式返回,前端可根据字段名对应到输入框下方显示。
补充:配置国际化错误消息(对应Validation中的错误码)
在messages.properties中添加错误提示文本:
NotEmpty.userForm.username=用户名不能为空 Size.userForm.username=用户名长度需在2-32位之间 Duplicate.userForm.username=该用户名已被注册 NotEmpty.userForm.password=密码不能为空 Size.userForm.password=密码长度需在2-32位之间 Diff.userForm.passwordConfirm=两次输入的密码不一致
第二步:前端修改为AJAX异步提交表单
先添加CSRF元标签(页面头部)
<meta name="_csrf" th:content="${_csrf.token}"/> <meta name="_csrf_parameter" th:content="${_csrf.parameterName}"/>
登录弹窗JS处理
<script> $("#loginForm").submit(function(e) { e.preventDefault(); // 阻止默认表单提交 const formData = new FormData(this); // 携带CSRF令牌 formData.append($("meta[name='_csrf_parameter']").attr("content"), $("meta[name='_csrf']").attr("content")); $.ajax({ url: $(this).attr("action"), type: "POST", data: formData, processData: false, contentType: false, success: function(res) { if (res.success) { window.location.href = "/home"; // 登录成功后跳转首页 } else { // 显示登录错误信息 $("#loginModal .has-error").text(res.error).show(); } }, error: function() { $("#loginModal .has-error").text("服务器错误,请稍后重试").show(); } }); }); </script>
注册弹窗JS处理
<script> $("#registrationForm").submit(function(e) { e.preventDefault(); const formData = new FormData(this); formData.append($("meta[name='_csrf_parameter']").attr("content"), $("meta[name='_csrf']").attr("content")); $.ajax({ url: $(this).attr("action"), type: "POST", data: formData, processData: false, contentType: false, success: function(res) { if (res.success) { $("#registerModal").modal("hide"); // 自动唤起登录弹窗提示用户登录 $("#loginModal").modal("show"); } else { // 清空之前的错误提示 $("#registerModal .has-error").text("").hide(); // 遍历字段错误,显示对应位置 Object.keys(res.fieldErrors).forEach(field => { const errorText = res.fieldErrors[field]; $("#registerModal [th\\:field='*{" + field + "}']").next(".has-error").text(errorText).show(); }); } }, error: function() { $("#registerModal .has-error").first().text("服务器错误,请稍后重试").show(); } }); }); </script>
第三步:调整前端错误提示元素的默认状态
将弹窗内的错误提示元素默认设置为隐藏,避免页面加载时显示空标签:
<!-- 登录弹窗错误提示 --> <p class="has-error" th:text="${error}" style="color: red; display: none;"></p> <!-- 注册弹窗错误提示 --> <p class="has-error" th:if="${#fields.hasErrors('username')}" th:errors="*{username}" style="color: red; display: none;"></p> <p class="has-error" th:if="${#fields.hasErrors('password')}" th:errors="*{password}" style="color: red; display: none;"></p> <span class="has-error" th:if="${#fields.hasErrors('passwordConfirm')}" th:errors="*{passwordConfirm}" style="color: red; display: none;"></span>
关键说明
- AJAX阻止默认提交:通过
e.preventDefault()避免页面重载,弹窗得以保留,保证错误提示在原上下文展示。 - 后端返回结构化JSON:登录返回全局错误,注册返回字段级错误,前端可精准对应到输入框下方显示。
- CSRF令牌处理:Spring Security默认启用CSRF校验,AJAX提交必须携带令牌,否则请求会被拒绝。
内容的提问来源于stack exchange,提问作者jCodeXtreme
相关产品推荐
相关产品推荐

