Spring - Thymeleaf:异常处理模板

Posted

技术标签:

【中文标题】Spring - Thymeleaf:异常处理模板【英文标题】:Spring - Thymeleaf: Exception processing template 【发布时间】:2019-10-11 21:10:57 【问题描述】:

我正在关注 Spring in Action 第 5 版一书,但我认为这是一个错误。

这是本书的 GitHub。我已经到达第 3 章 tacos-jdbc source of code

提交订单时突然出现错误:

并以这种方式在终端上:

2019-05-25 16:58:18.164 错误 11777 --- [nio-8080-exec-7] org.thymeleaf.TemplateEngine : [THYMELEAF][http-nio-8080-exec-7] 异常处理模板 “orderForm”:模板解析时出错(模板: "类路径资源 [templates/orderForm.html]")

org.thymeleaf.exceptions.TemplateInputException:发生错误 在模板解析期间(模板:“类路径资源 [模板/orderForm.html]")

订单控制器:

@Controller
@RequestMapping("/orders")
@SessionAttributes("order")
public class OrderController 

    private OrderRepository orderRepo;

    public OrderController(OrderRepository orderRepo) 
        this.orderRepo = orderRepo;
    

    @GetMapping("/current")
    public String orderForm() 
        return "orderForm";
    

    @PostMapping
    public String processOrder(@Valid Order order, Errors errors,
                               SessionStatus sessionStatus) 
        if (errors.hasErrors()) 
            return "orderForm";
        

        orderRepo.save(order);
        sessionStatus.setComplete();

        return "redirect:/";
    


和orderForm:

<!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>

<form method="POST" th:action="@/orders" th:object="$order">
    <h1>Order your taco creations!</h1>

    <img th:src="@/images/TacoCloud.png"/>
    <a th:href="@/design" id="another">Design another taco</a><br/>

    <div th:if="$#fields.hasErrors()">
        <span class="validationError">
        Please correct the problems below and resubmit.
        </span>
    </div>

    <h3>Deliver my taco masterpieces to...</h3>
    <label for="name">Name: </label>
    <input type="text" th:field="*name"/>
    <br/>

    <label for="street">Street address: </label>
    <input type="text" th:field="*street"/>
    <br/>

    <label for="city">City: </label>
    <input type="text" th:field="*city"/>
    <br/>

    <label for="state">State: </label>
    <input type="text" th:field="*state"/>
    <br/>

    <label for="zip">Zip code: </label>
    <input type="text" th:field="*zip"/>
    <br/>

    <label for="ccNumber">Credit Card #: </label>
    <input type="text" th:field="*ccNumber"/>
    <span class="validationError"
          th:if="$#fields.hasErrors('ccNumber')"
          th:errors="*ccNumber">CC Num Error</span>

    <label for="ccExpiration">Expiration: </label>
    <input type="text" th:field="*ccExpiration"/>
    <br/>

    <label for="ccCVV">CVV: </label>
    <input type="text" th:field="*ccCVV"/>
    <br/>

    <input type="submit" value="Submit order"/>
</form>


</body>
</html>

【问题讨论】:

【参考方案1】:

我认为您应该处理 orderForm.html 中所有输入中验证错误的显示,如下所示:(Source Git)

<!-- tag::allButValidation[] -->
<!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>

    <form method="POST" th:action="@/orders" th:object="$order">
      <h1>Order your taco creations!</h1>

      <img th:src="@/images/TacoCloud.png"/>
      <a th:href="@/design" id="another">Design another taco</a><br/>

      <div th:if="$#fields.hasErrors()">
        <span class="validationError">
        Please correct the problems below and resubmit.
        </span>
      </div>

      <h3>Deliver my taco masterpieces to...</h3>
      <label for="name">Name: </label>
      <input type="text" th:field="*name"/>
<!-- end::allButValidation[] -->
      <span class="validationError"
            th:if="$#fields.hasErrors('name')"
            th:errors="*name">Name Error</span>
<!-- tag::allButValidation[] -->
      <br/>

      <label for="street">Street address: </label>
      <input type="text" th:field="*street"/>
<!-- end::allButValidation[] -->
      <span class="validationError"
            th:if="$#fields.hasErrors('street')"
            th:errors="*street">Street Error</span>
<!-- tag::allButValidation[] -->
      <br/>

      <label for="city">City: </label>
      <input type="text" th:field="*city"/>
<!-- end::allButValidation[] -->
      <span class="validationError"
            th:if="$#fields.hasErrors('city')"
            th:errors="*city">City Error</span>
<!-- tag::allButValidation[] -->
      <br/>

      <label for="state">State: </label>
      <input type="text" th:field="*state"/>
<!-- end::allButValidation[] -->
      <span class="validationError"
            th:if="$#fields.hasErrors('state')"
            th:errors="*state">State Error</span>
<!-- tag::allButValidation[] -->
      <br/>

      <label for="zip">Zip code: </label>
      <input type="text" th:field="*zip"/>
<!-- end::allButValidation[] -->
      <span class="validationError"
            th:if="$#fields.hasErrors('zip')"
            th:errors="*zip">Zip Error</span>
<!-- tag::allButValidation[] -->
      <br/>

      <h3>Here's how I'll pay...</h3>
<!-- tag::validatedField[] -->
      <label for="ccNumber">Credit Card #: </label>
      <input type="text" th:field="*ccNumber"/>
<!-- end::allButValidation[] -->
      <span class="validationError"
            th:if="$#fields.hasErrors('ccNumber')"
            th:errors="*ccNumber">CC Num Error</span>
<!-- tag::allButValidation[] -->
<!-- end::validatedField[] -->
      <br/>

      <label for="ccExpiration">Expiration: </label>
      <input type="text" th:field="*ccExpiration"/>
<!-- end::allButValidation[] -->
      <span class="validationError"
            th:if="$#fields.hasErrors('ccExpiration')"
            th:errors="*ccExpiration">CC Num Error</span>
<!-- tag::allButValidation[] -->
      <br/>

      <label for="ccCVV">CVV: </label>
      <input type="text" th:field="*ccCVV"/>
<!-- end::allButValidation[] -->
      <span class="validationError"
            th:if="$#fields.hasErrors('ccCVV')"
            th:errors="*ccCVV">CC Num Error</span>
<!-- tag::allButValidation[] -->
      <br/>

      <input type="submit" value="Submit order"/>
    </form>

  </body>
</html>
<!-- end::allButValidation[] -->

我认为您没有按照本章中解释的 bean 中添加的验证规则在表单中插入正确的信息。通过显示验证错误,您将在提交订单时准确知道哪个输入没有正确插入。


在调查您的代码之后。 Order.java 中的属性名称与视图页面 orderForm.html 中的属性名称不同

例如,在 orderForm 中,属性是 name

  <h3>Deliver my taco masterpieces to...</h3>
    <label for="name">Name: </label>
    <input type="text" th:field="*name"/>

而在 Order.java 中是 deliveryName

 @NotBlank(message="Delivery name is required")
    private String deliveryName;

解决方法是在Order.java和orderForm.html页面中使用同名的属性。

【讨论】:

我遇到了同样的错误。我到达一个页面,我需要单击复选框并为我的 taco 命名。提交订单后,我在后续页面中收到错误消息。问题与验证无关。 2019-05-28 21:01:27.110 错误 5417 --- [nio-8080-exec-8] org.thymeleaf.TemplateEngine:[THYMELEAF][http-nio-8080-exec-8] 异常处理模板“orderForm” : 模板解析时出错 (template: "class path resource [templates/orderForm.html]") org.thymeleaf.exceptions.TemplateInputException: An error occurred during. 这很奇怪。您是否更新了 orderForm.html、填写输入并获取解析错误消息?你能添加你的 Order 和 Taco 类吗?或者你有 Git 中你的代码的源代码吗?我使用相同的 oderForm.html 运行应用程序并且没有错误。 今晚我会点它,现在我在工作。谢谢您的帮助。如果您愿意,我可以通过电子邮件将项目发送给您【参考方案2】:

我修复了此代码添加 delivery 前缀:

    <!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>

<form method="POST" th:action="@/orders" th:object="$order">
    <h1>Order your taco creations!</h1>

    <img th:src="@/images/TacoCloud.png"/>

    <h3>Your tacos in this order:</h3>
    <a th:href="@/design" id="another">Design another taco</a><br/>
    <ul>
        <li th:each="taco : $order.tacos"><span th:text="$taco.name">taco name</span></li>
    </ul>

    <div th:if="$#fields.hasErrors()">
        <span class="validationError">
        Please correct the problems below and resubmit.
        </span>
    </div>

    <h3>Deliver my taco masterpieces to...</h3>
    <label for="deliveryName">Name: </label>
    <input type="text" th:field="*deliveryName"/>
    <span class="validationError"
          th:if="$#fields.hasErrors('deliveryName')"
          th:errors="*deliveryName">Name Error</span>
    <br/>

    <label for="deliveryStreet">Street address: </label>
    <input type="text" th:field="*deliveryStreet"/>
    <span class="validationError"
          th:if="$#fields.hasErrors('deliveryStreet')"
          th:errors="*deliveryStreet">Street Error</span>
    <br/>

    <label for="deliveryCity">City: </label>
    <input type="text" th:field="*deliveryCity"/>
    <span class="validationError"
          th:if="$#fields.hasErrors('deliveryCity')"
          th:errors="*deliveryCity">City Error</span>
    <br/>

    <label for="deliveryState">State: </label>
    <input type="text" th:field="*deliveryState"/>
    <span class="validationError"
          th:if="$#fields.hasErrors('deliveryState')"
          th:errors="*deliveryState">State Error</span>
    <br/>

    <label for="deliveryZip">Zip code: </label>
    <input type="text" th:field="*deliveryZip"/>
    <span class="validationError"
          th:if="$#fields.hasErrors('deliveryZip')"
          th:errors="*deliveryZip">Zip Error</span>
    <br/>

    <h3>Here's how I'll pay...</h3>
    <label for="ccNumber">Credit Card #: </label>
    <input type="text" th:field="*ccNumber"/>
    <span class="validationError"
          th:if="$#fields.hasErrors('ccNumber')"
          th:errors="*ccNumber">CC Num Error</span>
    <br/>

    <label for="ccExpiration">Expiration: </label>
    <input type="text" th:field="*ccExpiration"/>
    <span class="validationError"
          th:if="$#fields.hasErrors('ccExpiration')"
          th:errors="*ccExpiration">CC Num Error</span>
    <br/>

    <label for="ccCVV">CVV: </label>
    <input type="text" th:field="*ccCVV"/>
    <span class="validationError"
          th:if="$#fields.hasErrors('ccCVV')"
          th:errors="*ccCVV">CC Num Error</span>
    <br/>

    <input type="submit" value="Submit order"/>
</form>

</body>
</html>

【讨论】:

【参考方案3】:

您在 orderForm 模板中使用了 th:object="$order",但 Thymeleaf 不知道。 你必须让 Thymeleaf 知道这个对象,方法是像这样将它传递给模板

@GetMapping("/current")
public ModelAndView orderForm() 
    ModelAndView mv = new ModelAndView("orderForm");
    mv.addObject("order", new Order());
    return mv;

注意:您必须在模板中使用该对象的所有位置从控制器层传递该对象。


更新 1

同时更新你的发布方法

@PostMapping
public ModelAndView processOrder(@Valid Order order, Errors errors,
                           SessionStatus sessionStatus) 
    if (errors.hasErrors()) 
        ModelAndView mv = new ModelAndView("orderForm");
        mv.addObject("order", new Order());
        return mv;
    

    orderRepo.save(order);
    sessionStatus.setComplete();

    return new ModelAndView("redirect:/");

【讨论】:

同样的错误。如果我需要展示更多课程,请告诉我 当您提交订单时,您必须在该方法的第三行添加该对象。因为您还展示了需要order 对象的orderForm 模板。 你能告诉我怎么做吗?正如你所说,在你的第一次演讲中,我改变了。 第一个返回的是ModelAndView,第二个是String 是的,再检查一遍【参考方案4】:

我认为你可以这样做

@Controller
public class OrderController 

   @GetMapping("/orders")
   public String orders(Order order) 
      return "orderForm";
   

   @PostMapping("/orders")
   public String orderForm(@Valid Order order, BindingResult result, Model model) 
       if(result.hasErrors()) 
           return "orderForm";
        else 
           retrun "your_success_view";   
       
    

【讨论】:

我也试过你的方法,终端没有报错,但是提交后有一个空白页,就像我发布的第一张图片一样,直到状态500 也许 @Valid @ModelAttribute("order") Order order 在后期映射中?

以上是关于Spring - Thymeleaf:异常处理模板的主要内容,如果未能解决你的问题,请参考以下文章

评估 SpringEL 表达式的 Spring Boot 异常

Spring Boot 2.4.2 和 Thymeleaf 3.0.12 - 访问静态方法

Thymeleaf-spring boot 的模型属性为空

在基于 Web 的 Spring 范围中使用 Thymeleaf 处理 HTML 文件并将处理后的模板存储为字符串

Spring Boot使用thymeleaf模板时报异常:template might not exist or might not be accessible by any of the confi

Spring Boot 最佳实践模板引擎Thymeleaf集成