Spring Framework 中 UriComponentsBuilder工具类
前言
在 Spring Framework 中,UriComponentsBuilder
是一个功能强大的工具类,用于构建和操作 URI(统一资源标识符)。无论是构建 RESTful API 的请求 URL、动态拼接路径参数,还是处理查询参数和编码问题,UriComponentsBuilder
都能提供优雅且安全的解决方案。
一、UriComponentsBuilder 的核心概念
1.1 什么是 UriComponentsBuilder?
UriComponentsBuilder
是 Spring 提供的一个链式调用工具类,用于逐步构建 URI 的各个部分(如协议、主机、路径、查询参数、片段等)。它支持灵活的 URI 拼接、模板变量替换和自动编码,避免了手动拼接字符串可能导致的安全问题(如注入攻击)。
1.2 UriComponents 与 UriComponentsBuilder 的关系
- UriComponents:一个不可变的 URI 组件对象,由
UriComponentsBuilder
构建而成。它包含了 URI 的所有组成部分(协议、主机、路径、查询参数等)。 - UriComponentsBuilder:用于构建
UriComponents
的可变构建器,支持链式调用。
二、UriComponentsBuilder 的核心 API
1. 创建 UriComponentsBuilder 实例的静态方法
(1) newInstance()
- 作用:从头开始构建一个空的
UriComponentsBuilder
实例。 - 使用场景:需要完全自定义 URI 的各个部分(协议、主机、路径等)。
- 示例:
UriComponentsBuilder builder = UriComponentsBuilder.newInstance().scheme("https").host("example.com").path("/api/data"); System.out.println(builder.build().toUriString()); // 输出: https://example.com/api/data
(2) fromHttpUrl(String url)
- 状态:已弃用(推荐使用
fromUriString
)。 - 作用:从 HTTP/HTTPS URL 字符串构建实例。
- 使用场景:仅适用于 HTTP/HTTPS 协议的 URL。
- 示例:
// 不推荐(弃用) UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("https://example.com/api/data"); System.out.println(builder.build().toUriString());
(3) fromUriString(String uri)
- 推荐替代方法:替代
fromHttpUrl
。 - 作用:从完整的 URI 字符串构建实例(支持任意协议)。
- 使用场景:通用的 URI 构建,支持 HTTP/HTTPS 以外的协议(如
ftp://
)。 - 示例:
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("https://example.com/api/data"); System.out.println(builder.build().toUriString());
(4) fromUri(URI uri)
- 作用:从
java.net.URI
对象构建实例。 - 使用场景:已有
URI
对象,需进一步修改其路径或参数。 - 示例:
URI originalUri = new URI("https://example.com/api/data"); UriComponentsBuilder builder = UriComponentsBuilder.fromUri(originalUri).path("/v2"); System.out.println(builder.build().toUriString()); // 输出: https://example.com/api/data/v2
(5) fromPath(String path)
- 作用:从路径字符串构建实例(忽略协议、主机等信息)。
- 使用场景:仅需构建相对路径(如
/api/users
)。 - 示例:
UriComponentsBuilder builder = UriComponentsBuilder.fromPath("/api/users"); System.out.println(builder.build().toUriString()); // 输出: /api/users
(6) fromHttpRequest(HttpRequest request)
- 作用:从 HTTP 请求对象构建 URI(提取协议、主机、端口等信息)。
- 使用场景:基于当前请求动态构建 URI。
- 示例:
// 假设 request 是一个 HttpServletRequest 对象 UriComponentsBuilder builder = UriComponentsBuilder.fromHttpRequest(request).path("/new-path"); System.out.println(builder.build().toUriString());
(7) fromOriginHeader(String origin)
- 作用:从跨域请求的
Origin
头构建 URI(仅包含协议、主机和端口)。 - 使用场景:处理跨域请求时,动态构建目标 URI。
- 示例:
UriComponentsBuilder builder = UriComponentsBuilder.fromOriginHeader("https://example.com").path("/api/data"); System.out.println(builder.build().toUriString()); // 输出: https://example.com/api/data
2. 构建 URI 的组成部分
(1) 添加路径
- 方法:
path(String path)
- 作用:追加路径到当前 URI。
- 使用场景:动态拼接多级路径(如
/api/users/123
)。 - 示例:
UriComponentsBuilder builder = UriComponentsBuilder.newInstance().scheme("https").host("example.com").path("/api").path("/users").path("/{id}"); System.out.println(builder.buildAndExpand("123").toUriString()); // 输出: https://example.com/api/users/123
(2) 替换路径
- 方法:
replacePath(String path)
- 作用:覆盖当前路径。
- 使用场景:需要完全替换现有路径(如从
/old
改为/new
)。 - 示例:
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("https://example.com/old").replacePath("/new"); System.out.println(builder.build().toUriString()); // 输出: https://example.com/new
(3) 添加查询参数
- 方法:
queryParam(String name, Object... values)
- 作用:添加查询参数(支持重复参数)。
- 使用场景:构建带分页或过滤条件的 API 请求。
- 示例:
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("https://example.com/api/data").queryParam("page", 1).queryParam("size", 20); System.out.println(builder.build().toUriString()); // 输出: https://example.com/api/data?page=1&size=20
(4) 替换查询参数
- 方法:
replaceQueryParam(String name, Object... values)
- 作用:替换指定查询参数的值。
- 使用场景:动态更新查询参数(如从
page=1
改为page=2
)。 - 示例:
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("https://example.com/api/data?page=1").replaceQueryParam("page", 2); System.out.println(builder.build().toUriString()); // 输出: https://example.com/api/data?page=2
(5) 添加片段(Fragment)
- 方法:
fragment(String fragment)
- 作用:添加 URI 片段(即
#
后的内容)。 - 使用场景:构建带有锚点的 URL(如
/page#section1
)。 - 示例:
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("https://example.com/page").fragment("section1"); System.out.println(builder.build().toUriString()); // 输出: https://example.com/page#section1
(6) 编码
- 方法:
encode()
/encode(Charset charset)
- 作用:对 URI 的各部分进行编码(默认 UTF-8)。
- 使用场景:确保生成的 URI 符合 RFC 3986 标准(如处理中文参数)。
- 示例:
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("https://example.com/search").queryParam("q", "1 + 2 = 3"); System.out.println(builder.encode().build().toUriString()); // 输出: https://example.com/search?q=1%20%2B%202%20%3D%203
(7) 构建最终 URI
- 方法:
build()
/buildAndExpand(Object... values)
- 作用:
build()
:构建不可变的UriComponents
对象。buildAndExpand(...)
:构建并替换路径/查询参数中的占位符(如{id}
)。
- 使用场景:动态替换模板变量(如
/users/{id}
)。 - 示例:
// 使用 buildAndExpand 替换占位符 UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("https://example.com/users/{id}"); System.out.println(builder.buildAndExpand("123").toUriString()); // 输出: https://example.com/users/123
(8) 转换为 URI 或字符串
- 方法:
toUri()
:返回java.net.URI
对象。toUriString()
:返回编码后的 URI 字符串。
- 使用场景:将构建的 URI 用于 HTTP 客户端或日志记录。
- 示例:
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("https://example.com/data").queryParam("sort", "name"); URI uri = builder.build().toUri(); String uriString = builder.build().toUriString(); System.out.println(uri); // java.net.URI 对象 System.out.println(uriString); // 字符串形式
二、变量替换与模板化构建
1. 路径模板
- 使用场景:动态替换路径中的变量(如
/users/{id}
)。 - 示例:
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("https://example.com/users/{id}").queryParam("action", "{action}"); System.out.println(builder.buildAndExpand("123", "delete").toUriString()); // 输出: https://example.com/users/123?action=delete
2. 查询参数模板
- 使用场景:动态替换查询参数中的变量(如
q={query}
)。 - 示例:
UriComponentsBuilder builder = UriComponentsBuilder.newInstance().path("/search").queryParam("q", "{query}"); System.out.println(builder.buildAndExpand("Spring").toUriString()); // 输出: /search?q=Spring
三、编码规则与注意事项
1. 默认编码
- 规则:
UriComponentsBuilder
使用 RFC 3986 编码规则。 - 示例:
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("https://example.com/search").queryParam("q", "1 + 2 = 3"); System.out.println(builder.build().toUriString()); // 输出: https://example.com/search?q=1%20%2B%202%20%3D%203
2. 自定义编码
- 场景:需要使用
java.net.URLEncoder
的编码方式。 - 示例:
String encodedValue = URLEncoder.encode("1 + 2 = 3", "UTF-8"); UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("https://example.com/search").queryParam("q", encodedValue); System.out.println(builder.build(true).toUriString()); // build(true) 表示参数已编码
四、典型应用场景
1. 构建 RESTful API 请求 URL
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("https://api.example.com/users").path("/{id}").queryParam("sort", "name").buildAndExpand("123");
System.out.println(builder.toUriString());
// 输出: https://api.example.com/users/123?sort=name
2. 动态生成带分页的 URL
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("https://api.example.com/data").queryParam("page", 2).queryParam("size", 10);
System.out.println(builder.build().toUriString());
// 输出: https://api.example.com/data?page=2&size=10
3. 处理跨域请求的 Origin
UriComponentsBuilder builder = UriComponentsBuilder.fromOriginHeader("https://example.com").path("/api/data");
System.out.println(builder.build().toUriString());
// 输出: https://example.com/api/data
五、常见问题与解决方案
1. 如何避免重复编码?
- 解决方案:如果手动编码参数,调用
build(true)
告知UriComponentsBuilder
参数已编码:String encodedValue = URLEncoder.encode("test", "UTF-8"); builder.queryParam("q", encodedValue).build(true);
2. 如何处理特殊字符?
- 解决方案:依赖
encode()
方法自动编码,或手动处理特殊字符(如+
、=
)。
3. 如何从现有 URI 修改?
- 解决方案:使用
fromUri(URI)
或fromUriString(String)
读取现有 URI,再修改路径或参数。
六、总结
方法 | 用途 | 推荐场景 |
---|---|---|
fromUriString | 通用 URI 构建 | 替代 fromHttpUrl ,支持所有协议 |
path /queryParam | 构建路径和查询参数 | 动态拼接 API 地址 |
buildAndExpand | 替换模板变量 | 路径或查询参数中包含占位符(如 {id} ) |
encode | 自动编码 | 生成符合 RFC 3986 标准的 URI |
七、扩展阅读
- 官方文档:Spring Framework URI Building
- 相关工具类:
ServletUriComponentsBuilder
:基于当前请求构建 URI。MvcUriComponentsBuilder
:基于控制器方法构建 URI(与@RequestMapping
一起使用)。