Spring Boot 和 Thymeleaf 在字段错误时返回 400 错误

Posted

技术标签:

【中文标题】Spring Boot 和 Thymeleaf 在字段错误时返回 400 错误【英文标题】:Spring Boot and Thymeleaf are returning a 400 error on field errors 【发布时间】:2020-11-16 02:45:42 【问题描述】:

我将 Spring Boot 2.3.1 和 Thymeleaf 用于一个简单的注册应用程序,因此用户可以创建 Member 模型并将其保存到数据库中。我正在使用javax.validation 框架来确保字段不为空,并且在模型有效时它可以正常工作,但是缺少字段的无效模型会将我踢出,并在我的默认error.html 页面上显示 400 错误代码,而不是将用户带回注册表单,我可以在其中显示字段级错误(例如,“需要用户名”)。

模型很简单,看起来像这样:

@Entity
@Table(name="member")
public class Member 

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    @Email
    @NotEmpty
    private String username;

    @NotEmpty
    @Column(name="password", nullable = false)
    private String password;
   
    // ... getters and setters

控制器如下所示:

@PostMapping("/register")
public ModelAndView registerMember(HttpServletRequest request, HttpServletResponse response,
                                       @ModelAttribute @Valid Member member, ModelMap model, BindingResult result)
   log.info("Attempting to register a user");

   // some other error checking not related to javax.validation ...

   memberService.save(member);

   return new ModelAndView("/success", model);

最后,(简化的)HTML/Thymeleaf 表单看起来像这样(register.html):

       <form th:action="@/register" th:object="$member" method="post">

             <input type="text" class="form-control" placeholder="Email" id="username" th:field="*username">

             <input type="password" placeholder="Enter your Password"  id="password" class="form-control" th:field="*password">

             <input type="submit" class="btn btn-primary btn-block btn-login">
        </form>

在表单提交时使用usernamepassword 的值一切正常,但如果我省略其中任何一个,例如username,我会得到以下WARN 日志语句:

Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 4 errors
Field error in object 'member' on field 'username': rejected value []; codes [NotEmpty.member.username,NotEmpty.username,NotEmpty.java.lang.String,NotEmpty]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [member.username,username]; arguments []; default message [username]]; default message [must not be empty]

但随后服务器返回 400 错误并呈现 error.html 页面而不是返回到表单,同样重要的是我在控制器中的 registerMember() 方法根本没有被调用。它完全通过了我的逻辑,所以我什至无法使用 BindingResult 手动检查错误。

如果我在我的模型上省略了@Valid 注释,比如@ModelAttribute Member member,那么我的方法会被调用,但没有使用任何javax.validation,而BindingResult.getErrorCount()0,因此无法使用它(我必须手动验证自己)。

知道如何让@Valid 完成其工作返回/register 端点以显示字段级错误吗?

【问题讨论】:

【参考方案1】:

您可以通过以下方式解决此问题。

    您需要从更改方法签名

public ModelAndView registerMember(HttpServletRequest请求,HttpServletResponse响应,@ModelAttribute @Valid Member成员,ModelMap模型,BindingResult结果)

到这里

public ModelAndView registerMember(HttpServletRequest request, HttpServletResponse response,@ModelAttribute @Valid Member member, BindingResult result, ModelMap model) 

因为绑定结果应该立即到@Valid 对象。

在此之后,您可以出错并发送如下所需的表格

if (bindingResult.hasErrors()) 
     return "register";

    您可以控制建议以处理异常

@RestControllerAdvice @EnableWebMvc

公共类 ExceptionAdvice

@ExceptionHandler(MethodArgumentNotValidException.class)
public Response handleInvaldException (MethodArgumentNotValidException ex, Response response)     
      Map < String, String > errors = new HashMap < > ();
        ex.getBindingResult().getAllErrors().forEach((err) - > 
        String fieldName = ((FieldError) err).getField();
        String errorMessage = err.getDefaultMessage();
        errors.put(fieldName, errorMessage);
    );
    response.setData(errors);
   retr

【讨论】:

您建议在Member 旁边重新定位BindingResult 参数解决了问题,现在它进入了我的逻辑,并报告了错误。谢谢!这与使用required 进行前端表单验证的@3asyPe 建议相结合,完成了客户端和服务器端检查。 你好@vishal-pawar 文档中是否提到有效注释必须后跟BindingResult?可以提供链接吗【参考方案2】:

在实际应用中,表单验证发生在前端。我强烈建议您阅读有关默认 html 和 js 表单验证的信息。例如,@NotNull 验证只是您的 html 标记中的 required 属性。此属性不允许您在字段为空时提交表单。可以使用简单的 JS 或 jQuery 来执行更复杂的验证。

<input type="text" class="form-control" placeholder="Email" id="username" th:field="*username" required>

在后端,@NotNull 等注释用于重新检查您没有将不正确的数据保存到数据库中。但是将这些注释用作唯一检查是不自然的。

【讨论】:

虽然我接受了另一个答案,因为它处理了@Valid 问题,但您建议还添加浏览器端required 验证非常有帮助,并且会使表单更加用户友好。谢谢!

以上是关于Spring Boot 和 Thymeleaf 在字段错误时返回 400 错误的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot整合Thymeleaf

Spring Boot 和 Thymeleaf:链接列表

Spring Boot Thymeleaf

Spring Boot 和 Thymeleaf 在字段错误时返回 400 错误

使用 Jpa 和 Thymeleaf 在 Spring Boot 中重定向页面

Spring Boot (十五): Spring Boot + Jpa + Thymeleaf 增删改查示例