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

P_i*_*l55 0 java validation spring thymeleaf spring-boot

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

该模型很简单,如下所示:

@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
Run Code Online (Sandbox Code Playgroud)

控制器看起来像这样:

@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);
}
Run Code Online (Sandbox Code Playgroud)

最后,(简化的)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>
Run Code Online (Sandbox Code Playgroud)

在表单上提交一个值 forusername并且password一切正常,但是如果我省略其中任何一个,就像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]
Run Code Online (Sandbox Code Playgroud)

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

如果我省略@Valid模型上的注释,那么我的方法就会被调用,但没有使用@ModelAttribute Member member任何一个,并且is ,从而破坏了使用它的全部意义(我必须手动验证自己)。javax.validationBindingResult.getErrorCount()0

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

Vis*_*war 6

您可以通过以下方法解决这个问题。

  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) 
Run Code Online (Sandbox Code Playgroud)

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

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

if (bindingResult.hasErrors()) {
     return "register";
}
Run Code Online (Sandbox Code Playgroud)
  1. 您可以通过控制器建议来处理异常

@RestControllerAdvice @EnableWebMvc

公共类异常建议{

@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
}
Run Code Online (Sandbox Code Playgroud)

}