Created
November 7, 2019 17:16
-
-
Save matey-jack/80039c4833b42bd67a42a47c01ae9199 to your computer and use it in GitHub Desktop.
Spring's UriComponentsBuilder -- get it encode optional characters is not quite trivial
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.assertj.core.api.Assertions.assertThat | |
import org.assertj.core.api.Assertions.assertThatExceptionOfType | |
import org.junit.Test | |
import org.springframework.web.util.UriComponentsBuilder | |
import org.springframework.web.util.UriUtils | |
import java.nio.charset.StandardCharsets.UTF_8 | |
class UrlTests { | |
@Test | |
fun weakEncode() { | |
val uri = UriComponentsBuilder.newInstance() | |
.host("server.dev") | |
.queryParam("date", "12:35+01:00") | |
.queryParam("name", "Süß") | |
.build() | |
.toUri() | |
assertThat(uri.toString()).isEqualTo("//server.dev?date=12:35+01:00&name=Süß") | |
assertThat(uri.toASCIIString()).isEqualTo("//server.dev?date=12:35+01:00&name=S%C3%BC%C3%9F") | |
// "works", but causes problems when the receiver decides to read '+' as | |
// the traditional encoding for ' '. | |
} | |
@Test | |
fun manualEncodeFail() { | |
// so, we can try to encode our Date manually, but then ... | |
val encodedDate = UriUtils.encode("12:35+01:00", UTF_8) | |
val uri = UriComponentsBuilder.newInstance() | |
.host("server.dev") | |
.queryParam("date", encodedDate) | |
.queryParam("name", "Süß") | |
.build() | |
.toUri() | |
// ... it will encode the % sign again and that will definitely not | |
// yield a good reading on the receiver side. | |
assertThat(uri.toString()).isEqualTo("//server.dev?date=12%253A35%252B01%253A00&name=Süß") | |
assertThat(uri.toASCIIString()).isEqualTo("//server.dev?date=12%253A35%252B01%253A00&name=S%C3%BC%C3%9F") | |
} | |
@Test | |
fun manualEncode() { | |
// This solves the above, but now need to encode all parameters manually... | |
val encodedDate = UriUtils.encode("12:35+01:00", UTF_8) | |
val uri = UriComponentsBuilder.newInstance() | |
.host("server.dev") | |
.queryParam("date", encodedDate) | |
.build(true) | |
.toUri() | |
assertThat(uri.toString()).isEqualTo("//server.dev?date=12%3A35%2B01%3A00") | |
assertThat(uri.toASCIIString()).isEqualTo("//server.dev?date=12%3A35%2B01%3A00") | |
} | |
@Test | |
fun manualEncodeOtherFail() { | |
// for example, will fail here: | |
val encodedDate = UriUtils.encode("12:35+01:00", UTF_8) | |
val uriBuilder = UriComponentsBuilder.newInstance() | |
.host("server.dev") | |
.queryParam("date", encodedDate) | |
.queryParam("name", "Süß") | |
assertThatExceptionOfType(IllegalArgumentException::class.java).isThrownBy { | |
uriBuilder.build(true) | |
} | |
} | |
@Test | |
fun easyEncode() { | |
val uri = UriComponentsBuilder.newInstance() | |
.host("server.dev") | |
.queryParam("date", "12:35+01:00") | |
.queryParam("name", "Süß") | |
.encode() | |
.build() | |
.toUri() | |
// doesn't solve the problem! | |
// Strong encoding does not apply to parameters passed via 'queryParam'. | |
assertThat(uri.toString()).isEqualTo("//server.dev?date=12:35+01:00&name=S%C3%BC%C3%9F") | |
assertThat(uri.toASCIIString()).isEqualTo("//server.dev?date=12:35+01:00&name=S%C3%BC%C3%9F") | |
} | |
@Test | |
fun useVariables() { | |
val uri = UriComponentsBuilder.newInstance() | |
.host("server.dev") | |
.queryParam("date", "{date}") | |
.queryParam("name", "{name}") | |
.build() | |
.expand(mapOf("date" to "12:35+01:00", "name" to "Süß")) | |
.toUri() | |
// Also doesn't solve the problem! | |
// Strong encoding does not apply to parameters passed via 'queryParam'. | |
assertThat(uri.toString()).isEqualTo("//server.dev?date=12:35+01:00&name=Süß") | |
assertThat(uri.toASCIIString()).isEqualTo("//server.dev?date=12:35+01:00&name=S%C3%BC%C3%9F") | |
} | |
@Test | |
fun encodeVariables() { | |
// this is the only way which does the desired thing! | |
// must use 'expand' **and** 'encode'! | |
val uri = UriComponentsBuilder.newInstance() | |
.host("server.dev") | |
.queryParam("date", "{date}") | |
.queryParam("name", "{name}") | |
.encode() | |
.build() | |
.expand(mapOf("date" to "12:35+01:00", "name" to "Süß")) | |
.toUri() | |
assertThat(uri.toString()).isEqualTo("//server.dev?date=12%3A35%2B01%3A00&name=S%C3%BC%C3%9F") | |
} | |
/* | |
Side-note: the difference between toString() and toASCIIString() is not important in our case. | |
Ascii-String will always be used for making the actual HTTP-Requests. | |
toString() is just for information and is different from the "unencoded" form as you can see from | |
% being encoded again when passed in as a parameter, but not encoded when going from toString() to | |
the Ascii version. | |
*/ | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment