Skip to content

Instantly share code, notes, and snippets.

@matey-jack
Created November 7, 2019 17:16
Show Gist options
  • Save matey-jack/80039c4833b42bd67a42a47c01ae9199 to your computer and use it in GitHub Desktop.
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
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