SpringBoot+Htmx全局错误处理程序案例


htmx -spring-boot 库 3.2.0刚刚发布,现在支持用作HtmxResponse错误处理程序的返回类型。这篇博文展示了如何使用它。

错误处理是任何应用程序的重要组成部分。它确保用户始终了解应用程序发生了什么并且可以采取行动。按下按钮却没有任何反应总是比看到错误消息出现更糟糕。显然,我们应该努力避免它们,但是有一个全局错误回退以防万一出现问题肯定是一个好主意。

让我们创建一个小示例来展示新功能。使用ttcli并选择“NPM Based with TailwindCSS”、“htmx”和“DaisyUI”。

我们添加 2 条路线HomeController:

    @GetMapping("/test")
    @HxRequest
    public String test() {
        return
"fragments :: message";
    }

    @GetMapping(
"/test-exception")
    @HxRequest
    public String testException() {
        throw new RuntimeException(
"Fake exception");
    }

第一个返回一个简单的结果<div>,第二个则模拟发生异常。

src/main/resources/templates/fragments.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div th:fragment=
"message">
    Button clicked!
</div>

在 index.html中,我们放置了两个按钮来触发我们的两条路线:

<div layout:fragment="content">
    <div class=
"m-4">
        <button class=
"btn btn-neutral"
                hx:get=
"@{/test}"
                hx-swap=
"outerHTML"
        >Do something</button>
    </div>
    <div class=
"m-4">
        <button class=
"btn btn-neutral"
                hx:get=
"@{/test-exception}"
                hx-swap=
"outerHTML"
        >Do something</button>
    </div>
</div>


在 中index.html,添加一个空字符div作为占位符来放置错误消息:

<div layout:fragment="content">
    <div class=
"mx-8 mt-4">
        <div id=
"global-error"></div>
    </div>
    ...


我们创建一个可用于带外交换的片段:

src/main/resources/templates/fragments.html

<div th:fragment="error-message(message)" id="global-error" role="alert" class="alert alert-error" hx-swap-oob="true">
    <svg xmlns=
"http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
    <span th:text=
"${message}">Error!</span>
</div>

这两件事很重要:

  • hx-swap-oob="true" 允许 htmx 使用带外交换。
  • 片段的 id 必须与 index.html 模板中占位符 div 的 id 一致。

最后一块拼图是在 HomeController 中添加异常处理程序:
HomeController.java

 

@ExceptionHandler(Exception.class
    public HtmxResponse handleError(Exception ex) {
        return HtmxResponse.builder()
                           .reswap(HtmxReswap.none()) 
                           .view(new ModelAndView("fragments :: error-message", Map.of("message", ex.getMessage()))) 
                           .build();
    }

  •     处理异常Exception 和异常的所有子类。
  • 将交换行为设置为 "无",这样我们就不会意外地交换我们不想交换的东西。我们只希望通过带外交换来显示错误信息。
  • 使用 error-message 片段,并将捕获的异常信息传递给该片段。

如果希望错误处理适用于应用程序的所有控制器,可以使用 @ControllerAdvice 来实现:

import io.github.wimdeblauwe.htmx.spring.boot.mvc.HtmxResponse;
import io.github.wimdeblauwe.htmx.spring.boot.mvc.HtmxReswap;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

import java.util.Map;

@ControllerAdvice
public class GlobalErrorHandler {

    @ExceptionHandler(Exception.class)
    public HtmxResponse handleError(Exception ex) {
        return HtmxResponse.builder()
                           .reswap(HtmxReswap.none())
                           .view(new ModelAndView("fragments :: error-message", Map.of("message", ex.getMessage())))
                           .build();
    }
}

结论
确保回退错误处理程序并不那么困难,随着 htmx-spring-boot 3.2.0 版本的发布,这变得更加容易。
请参阅GitHub 上的htmx-global-error-handler以获取此示例的完整源代码。