Java SpringBoot URL以不一致的方式对查询参数中的加号进行编码
我有一个简单的回声控制器Java SpringBoot URL以不一致的方式对查询参数中的加号进行编码,java,spring-boot,Java,Spring Boot,我有一个简单的回声控制器 @RestController public class EchoController { @GetMapping(path = "/param", produces = MediaType.TEXT_PLAIN_VALUE) String echoParam(@RequestParam("p") String paramValue) { return paramValue; } @GetMapping(path = "
@RestController
public class EchoController {
@GetMapping(path = "/param", produces = MediaType.TEXT_PLAIN_VALUE)
String echoParam(@RequestParam("p") String paramValue) {
return paramValue;
}
@GetMapping(path = "/path-variable/{val}", produces = MediaType.TEXT_PLAIN_VALUE)
String echoPathVariable(@PathVariable("val") String val) {
return val;
}
}
其中一种方法与所给出的参数值相呼应;第二种方法使用通过URI提供的值执行相同的操作
我有以下测试:
@Autowired
private WebTestClient webTestClient;
@Test
public void rawPlus_inQueryParam() {
String value = "1+1";
String response = getValueEchoedThroughQueryParam(value);
assertThat(response, is(equalTo(value)));
}
@Test
public void urlencodedPlus_inQueryParam() {
String value = "1%2B1";
String response = getValueEchoedThroughQueryParam(value);
assertThat(response, is(equalTo(value)));
}
private String getValueEchoedThroughQueryParam(String value) {
return webTestClient.get()
.uri(builder -> {
return builder
.path("/param")
.queryParam("p", value)
.build();
})
.exchange()
.expectStatus().is2xxSuccessful()
.expectBody(String.class)
.returnResult()
.getResponseBody();
}
这两个测试都只是通过查询参数发送一个字符串,读取响应并断言内容得到了正确响应
第一次测试失败:
java.lang.AssertionError:
Expected: is "1+1"
but: was "1 1"
第二次测试通过
看起来在第二个测试中,WebTestClient
url对值进行编码(实际上,它只是url对百分比字符进行编码),然后web服务器url对其进行解码,一切正常。但是在第一个测试中,客户机不对加号字符进行url编码,而服务器对加号字符进行url解码,因此得到一个空格字符
这看起来不一致。我怀疑我能做些愚蠢的事情导致它,因为一切都在默认模式下工作;实际上,我在这里展示的控制器几乎是应用程序拥有的唯一代码(应用程序类本身是默认的,在它生成后我没有触摸它)
只是比较一下:当在URI中传递相同的数据时,一切都正常工作。以下测试通过:
@Test
public void rawPlus_inPathVariable() {
String value = "1+1";
String response = getValueEchoedThroughPathVariable(value);
assertThat(response, is(equalTo(value)));
}
@Test
public void urlencodedPlus_inPathVariable() {
String value = "1%2B1";
String response = getValueEchoedThroughPathVariable(value);
assertThat(response, is(equalTo(value)));
}
private String getValueEchoedThroughPathVariable(String value) {
return webTestClient.get()
.uri("/path-variable/" + value)
.exchange()
.expectStatus().is2xxSuccessful()
.expectBody(String.class)
.returnResult()
.getResponseBody();
}
问题是:
WebTestClient
编写的测试中,如何传递包含加号字符的查询参数值?如果我不手动对其进行url编码,它将由web服务器对url进行解码;如果我这样做,它会第二次得到url编码,所以服务器(在url解码后)会得到一个url解码版本2.1.4.发行版
(在撰写本文时的最新版本)
POM如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>springboot-plus-in-query-string</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-plus-in-query-string</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
4.0.0
org.springframework.boot
只需运行测试(mvn clean test
)。请参见此
理解这一点的关键是不同程度的编码是不同的
应用于URI模板vs URI变量。换言之:
http://example.com/a/{b} /c?q={q}&p={p}
URI模板是除URI变量之外的所有内容
占位符。但是,您显示的代码片段只构建了一个URI
literal没有任何变量,因此级别编码是相同的,只是
非法字符,无论使用哪种方法
所以它也必须是这样的:
builder
.path("/param")
.queryParam("p", "{value}")
.build(value)
.queryParam(“foo”,“{foo}”).buildAndExpand(foo)
因此,我们的想法是使用如下内容:
builder
.path("/param")
.queryParam("p", "{value}")
.build(value)
以获取已编码的查询参数。添加一些详细信息
@Eugen Covaci建议的代码片段实际上是有效的:
.uri(builder -> {
return builder
.path("/param")
.queryParam("p", "{value}")
.build(value);
})
有一件事仍然很奇怪:为什么以更详细的方式编写(似乎)相同的代码会产生不同的结果?两种形式(纯文本和使用URI变量的形式)实际上对查询参数使用了不同的编码级别,这似乎违反了我最不惊讶的原则
看起来这是由遗留问题造成的:之前,有一个bug:+
字符总是被编码的,尽管它不应该被编码。现在,作者修复了这个bug,但它产生了一个不一致性:.queryParam(“p”),“{value}”).build(value)
与.queryParam(“p”),value.build()不同
看来你必须知道/记住这一点
完整的工作方法代码如下所示
private String getValueEchoedThroughQueryParam(String value) {
return webTestClient.get()
.uri(builder -> {
return builder
.path("/param")
.queryParam("p", "{value}")
.build(value);
})
.exchange()
.expectStatus().is2xxSuccessful()
.expectBody(String.class)
.returnResult()
.getResponseBody();
}
可能重复@net它看起来不像一个复制品。在那里,OP想要禁用解码。在这里,我想理解为什么编码+解码是不一致的,并知道如何修复它:通过修复Tomcat中的错误,通过修复web客户端中的错误,或者做其他事情。这种行为是标准的。你试过“1%2B1”吗?@Bohemian是的,我试过了。问题是:)%
被编码,所以我们在请求中得到双编码加字符;web服务器解码一次,控制器得到1%2B1
而不是1+1
。谢谢你,Eugen!我还在另一个答案中添加了一些细节。当使用build
时,它将进行编码,当不使用build
时,它假定值已经编码。在这种情况下,+
是空白的编码值。@M.Deinum对不起,我不明白:你怎么能不使用build()
(2个中的任何一个)?没有它,您就有了UriBuilder
,但必须返回URI
。此外,如果执行.queryParam(“p”,“1+1”).build()
,它不会对加号进行编码。这就是我要说明的。当使用path/variable SUBSTION时,它假定需要进行编码;当不进行path/variable SUBSTION时,它假定值已经编码。因此,对于build(value)
您使用的是path/variable substation,而对于build()
您不是。@M.Deinum我明白您的意思。是的,当您熟悉此工具的逻辑时,此行为似乎是合乎逻辑的。但当你不这样做时,情况似乎并非如此:)当然,这可能只是我个人的问题。文档规则。主要区别在于是否使用变量substation。