1. 概述

本文将介绍如何在 Spring 项目中结合 Thymeleaf 模板引擎,正确生成带有路径变量(Path Variables)的 URL。这是日常开发中非常常见的需求,比如 /users/123 这种 RESTful 风格的接口调用。

路径变量的核心作用是:✅ 把动态参数嵌入 URL 路径中,而不是放在查询字符串里(如 ?id=123),更符合语义化设计。

在 Spring 控制器中,我们通过 @PathVariable 注解来提取这些路径中的值。而前端模板如何安全、简洁地拼接出正确 URL,就是本文的重点。


2. 单个路径变量的使用

先来看一个最基础的场景:点击商品列表,跳转到详情页 /pathvars/single/1

2.1 数据模型

定义一个简单的 Item 类:

public class Item {
    private int id;
    private String name;

    // 构造函数、getter 和 setter 省略
    public Item(int id, String name) {
        this.id = id;
        this.name = name;
    }

    // getter/setter...
}

2.2 控制器实现

@Controller
public class PathVariablesController {

    private List<Item> items = Arrays.asList(
        new Item(1, "First Item"),
        new Item(2, "Second Item")
    );

    @GetMapping("/pathvars")
    public String showList(Model model) {
        model.addAttribute("items", items);
        return "pathvariables/index";
    }
    
    @GetMapping("/pathvars/single/{id}")
    public String viewItem(@PathVariable("id") int id, Model model) {
        Item item = items.stream()
            .filter(i -> i.getId() == id)
            .findFirst()
            .orElse(new Item(id, "Unknown Item"));
        
        model.addAttribute("item", item);
        return "pathvariables/view";
    }
}

⚠️ 注意:生产环境应做更严谨的参数校验和异常处理,这里为简化逻辑略去。

2.3 Thymeleaf 模板写法

关键来了!如何在 HTML 中生成带 {id} 的链接?

✅ 推荐写法(类型安全、清晰):

<div th:each="item : ${items}">
    <a th:href="@{/pathvars/single/{id}(id = ${item.id})}">
        <span th:text="${item.name}"></span>
    </a>
</div>

生成的 HTML 示例:

<a href="/pathvars/single/1">First Item</a>
<a href="/pathvars/single/2">Second Item</a>

❌ 不推荐拼接字符串(易出错、可读性差):

<a th:href="@{'/pathvars/single/' + ${item.id}}">...</a>

虽然也能工作,但一旦路径复杂或参数增多,就容易踩坑,比如编码问题、斜杠缺失等。


3. 多个路径变量的处理

实际项目中,经常需要嵌套资源路径,例如:

/pathvars/item/1/detail/101

表示“ID 为 1 的商品下的第 101 条详情”。

3.1 扩展数据结构

新增 Detail 类:

public class Detail {
    private int id;
    private String description;

    public Detail(int id, String description) {
        this.id = id;
        this.description = description;
    }

    // getter/setter...
}

修改 Item 类:

public class Item {
    private int id;
    private String name;
    private List<Detail> details;

    // 构造函数等略
}

初始化数据时记得填充 details。

3.2 添加多变量接口

控制器新增方法:

@GetMapping("/pathvars/item/{itemId}/detail/{dtlId}")
public String viewDetail(
    @PathVariable("itemId") int itemId, 
    @PathVariable("dtlId") int dtlId, 
    Model model) {
    
    return items.stream()
        .filter(item -> item.getId() == itemId)
        .findFirst()
        .map(item -> {
            model.addAttribute("item", item);
            item.getDetails().stream()
                .filter(d -> d.getId() == dtlId)
                .findFirst()
                .ifPresent(detail -> model.addAttribute("detail", detail));
            return "pathvariables/view";
        })
        .orElse("error/404");
}

3.3 模板中使用多个路径变量

在 Thymeleaf 中传多个参数非常直观:

<ul>
    <li th:each="detail : ${item.details}">
        <a th:href="@{/pathvars/item/{itemId}/detail/{dtlId}(itemId = ${item.id}, dtlId = ${detail.id})}">
            <span th:text="${detail.description}"></span>
        </a>
    </li>
</ul>

📌 语法要点:

  • {} 定义路径占位符
  • (key = value) 传入实际参数,顺序无关
  • 多个参数用逗号分隔

生成示例:

<a href="/pathvars/item/1/detail/101">Detail of Item 1</a>

4. 总结

通过本文你应该掌握:

标准写法:使用 @{/path/{var}(var = ${expr})} 是最安全、清晰的方式
避免字符串拼接:简单粗暴的 '/' + ${id} 写法虽快,但不利于维护
多变量支持良好:Thymeleaf 对复杂路径的支持很成熟,无需手动处理编码

示例代码已托管至 GitHub:https://github.com/spring-thymeleaf-pathvars-demo(模拟地址)

这类技巧看似小,但在团队协作和长期维护中能显著减少 bug,建议直接加入你的项目模板中。


原始标题:Spring Path Variables with Thymeleaf