Spring 验证器未检测到错误

Spring validator does not detect errors

此代码正在创建一个 hmtl 表单,您可以在其中 select 一些 chekcboxes 并设计一个 Taco。我正在尝试执行模型字段验证,但它不起作用。例如,如果名称的输入字段框为空或者根本没有 select 复选框,则此代码应该 return 出错。控制器中的错误变量虽然从不捕获任何错误。可能会发生什么。此代码是行动书中 Spring 中的复制粘贴示例,所以我不确定为什么它不起作用。

型号

@Data
public class Taco {

 @NotNull
 @Size(min=5, message = "Name must be at least 5 characters long")
 private String name;
 @Size(min=1, message = "You must choose at least 1 ingredient")
 private List<String> ingredients;
}

控制器

@Slf4j
@Controller
@RequestMapping("/design")
public class DesignTacoController {

@GetMapping
public String showDesignFromModel(Model model){
    List<Ingredient> ingredients = Arrays.asList(
            new Ingredient("FLTO","Flour Tortilla", WRAP),
            new Ingredient("COTO","Corn Tortilla", WRAP),
            new Ingredient("GRBF","Ground Beef", PROTEIN),
            new Ingredient("CARN","Carnitas", PROTEIN),
            new Ingredient("TMTP","Diced Tomatoes", VEGGIES),
            new Ingredient("LETC","Lettuce", VEGGIES),
            new Ingredient("CHED","Cheddar", CHEESE),
            new Ingredient("JACK","Monterrey Jack", CHEESE),
            new Ingredient("SLSA","Salsa", SAUCE),
            new Ingredient("SRCR","Sour Cream", SAUCE)
    );


    Type[] types = Ingredient.Type.values();
    for (Type type : types){
        model.addAttribute(type.toString().toLowerCase(),
                ingredients.stream()
                        .filter(e -> e.getType() == type)
                        .collect(Collectors.toList()));

    }
    model.addAttribute("design",new Taco());
    return "design";
}

@PostMapping
public String processDesign(@Valid Taco design , Errors errors){
    if(errors.hasErrors()){
        System.out.println("Many errors " + errors);
        return "design";
    }
    System.out.println("Process design: " + design);
    System.out.println("No errors " + errors);
    log.info("Process design: " + design);
    return "redirect:/orders/current";
}
}

查看

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
     xmlns:th="http://www.thymeleaf.org">

<head>
   <title>Taco Cloud</title>
   <link rel="stylesheet" th:href="@{/styles.css}" />
</head>

<body>
<h1>Design your taco!</h1>
<img th:src="@{/images/TacoCloud.png}" alt="Taco Cloud Logo"/>
<form method="POST" th:object="${design}">
    <div class="grid">
        <div class="ingredient-group" id="wraps">
           <h3>Designate your wrap:</h3>
           <div th:each="ingredient : ${wrap}">
               <input name="ingredients" type="checkbox" th:value="${ingredient.id}"/>
               <span th:text="${ingredient.name}">INGREDIENT</span><br/>
           </div>
        </div>
        <div class="ingredient-group" id="proteins">
            <h3>Pick your protein:</h3>
            <div th:each="ingredient : ${protein}">
                <input name="ingredients" type="checkbox" th:value="${ingredient.id}"/>
                <span th:text="${ingredient.name}">INGREDIENT</span><br/>
            </div>
        </div>
        <div class="ingredient-group" id="cheeses">
            <h3>Choose your cheese:</h3>
            <div th:each="ingredient : ${cheese}">
                <input name="ingredients" type="checkbox" th:value="${ingredient.id}"/>
                <span th:text="${ingredient.name}">INGREDIENT</span><br/>
            </div>
        </div>
        <div class="ingredient-group" id="veggies">
            <h3>Determine your veggies:</h3>
            <div th:each="ingredient : ${veggies}">
                <input name="ingredients" type="checkbox" th:value="${ingredient.id}"/>
                <span th:text="${ingredient.name}">INGREDIENT</span><br/>
            </div>
        </div>
        <div class="ingredient-group" id="sauces">
            <h3>Select your sauce:</h3>
            <div th:each="ingredient : ${sauce}">
                <input name="ingredients" type="checkbox" th:value="${ingredient.id}"/>
                <span th:text="${ingredient.name}">INGREDIENT</span><br/>
            </div>
        </div>
    </div>
    <div>
        <h3>Name your taco creation:</h3>
        <input type="text" th:field="*{name}"/>
        <span th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Error</span>
        <br/>
        <button>Submit your taco</button>
    </div>
</form>
</body>


</html>

当验证不起作用时,通常有一件事情是不对的。类路径上没有 javax.validation API 的实现。仅从 javax.validationvalidation-api 添加依赖项不会执行任何操作,因为那只是 API 而不是实际实现。

您还需要添加一个实现,例如 hibernate-validator

现在,在 Spring Boot 的早期版本(2.3 之前)中,添加 spring-boot-starter-web 作为依赖项时会自动包含验证。然而,在较新的版本 (2.3+) 中,这是 removed。您现在需要明确包含 spring-boot-starter-validation 启动器依赖项。这包括 API 和一个实现。

话虽这么说,也可能是你在类路径上有 Jakarta 验证 API 而不是 Java 验证 API 以及它的实现(比如 hibernate-validator 版本 7 或更高版本)。 Spring 或 Spring Boot(尚)不支持 Jakarata 验证 API(或大多数 JakartaEE API)。所以即使你在你的类路径上有它也不会工作,你需要 Java 验证 API 一个。

我也被这个问题折磨了一段时间
原因:
如果您不 select 任何 CheckBox,成分=null,@Size min 不验证 null result.So,我们需要组合 @NotNull。

@NotNull(message = "You must choose at least 1 ingredient")
@Size(min = 1, message = "You must choose at least 1 ingredient")
private List<String> ingredients;