在URL中,由于 “;” 是保留字符,Java 默认不会对它转码,在某些情况下会出现问题。
在 influxDB 中,从多个 measurement 中查询数据的SQL使用 “;” 分隔,使用 CURL 能得到正确结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44curl -v -G 'http://127.0.0.1:8086/query?db=test' --data-urlencode 'q=show databases;SHOW MEASUREMENTS'
...
> GET /query?db=test&q=show%20databases%3BSHOW%20MEASUREMENTS HTTP/1.1
...
<
{
"results": [
{
"statement_id": 0,
"series": [
{
"name": "databases",
"columns": [
"name"
],
"values": [
[
"_internal"
],
[
"test"
]
]
}
]
},
{
"statement_id": 1,
"series": [
{
"name": "measurements",
"columns": [
"name"
],
"values": [
[
"proxy"
]
]
}
]
}
]
}
但是在Java中,只返回第一个SQL结果。和上面 curl 的请求对比,发现是参数 q 中的 “;” 分号没有转码成 %3B 导致的。
1
2
3
4
5
6
7
8
9
10
11
12
13public class Test {
public static void main(String[] args) {
String url = "http://127.0.0.1:8086/query?db=test&q={query}";
URI uri = new UriTemplate(url).expand("show databases;show measurements");
System.out.println(uri.toString());
RestTemplate restTemplate = new RestTemplate();
HttpEntity response = restTemplate.getForEntity(uri, String.class);
System.out.println(response.getBody());
}
}
运行结果
1
2http://127.0.0.1:8086/query?db=test&q=show%20databases;show%20measurements
{"results":[{"statement_id":0,"series":[{"name":"databases","columns":["name"],"values":[["_internal"],["test"]]}]}]}
不能直接使用 ‘uri.toString().replace(“;”, “%3B”)’ 将分号替换成 “%3B”, 因为后面还会再一次编码变为 “%253B” 而报错。
后来在 spring-web-5.0.4.RELEASE.jar 中发现了 DefaultUriBuilderFactory 类。首先看一下其源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29public class DefaultUriBuilderFactory implements UriBuilderFactory {
private final UriComponentsBuilder baseUri;
private final Map defaultUriVariables;
private DefaultUriBuilderFactory.EncodingMode encodingMode;
private boolean parsePath;
public DefaultUriBuilderFactory() {
this(UriComponentsBuilder.newInstance());
}
public DefaultUriBuilderFactory(String baseUriTemplate) {
this(UriComponentsBuilder.fromUriString(baseUriTemplate));
}
public DefaultUriBuilderFactory(UriComponentsBuilder baseUri) {
this.defaultUriVariables = new HashMap();
this.encodingMode = DefaultUriBuilderFactory.EncodingMode.URI_COMPONENT;
this.parsePath = true;
Assert.notNull(baseUri, "'baseUri' is required");
this.baseUri = baseUri;
}
public void setEncodingMode(DefaultUriBuilderFactory.EncodingMode encodingMode) {
this.encodingMode = encodingMode;
}
......
}
DefaultUriBuilderFactory 中有一个变量 encodingMode,可以通过它设置编码模式。EncodingMode 是 DefaultUriBuilderFactory 的内部类,里面定义了3中编码模式,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12public class DefaultUriBuilderFactory implements UriBuilderFactory {
......
public static enum EncodingMode {
URI_COMPONENT,
VALUES_ONLY,
NONE;
private EncodingMode() {
}
}
}
使用 DefaultUriBuilderFactory 来改写上面有问题的代码后,可以正确获取结果了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class Test {
public static void main(String[] args) {
String url = "http://127.0.0.1:8086/query?db=test&q={query}";
RestTemplate restTemplate = new RestTemplate();
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory();
factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);
URI uri = factory.expand(url,"show databases;show measurements");
System.out.println(uri.toString());
HttpEntity response = restTemplate.getForEntity(uri, String.class);
System.out.println(response.getBody());
}
}
运行结果
1
2
3http://127.0.0.1:8086/query?db=test&q=show%20databases%3Bshow%20measurements
{"results":[{"statement_id":0,"series":[{"name":"databases","columns":["name"],"values":[["_internal"],["test"]]}]},
{"statement_id":1,"series":[{"name":"measurements","columns":["name"],"values":[["proxy"]]}]}]}