fix: handle query parameters with square brackets

because of https://github.com/flutterchina/dio/issues/799
This commit is contained in:
Gauthier Roebroeck 2021-02-19 16:18:44 +08:00
parent f04674d6cd
commit 245dea906c
4 changed files with 343 additions and 0 deletions

View file

@ -1,6 +1,24 @@
package org.gotson.komga.infrastructure.language
import java.util.Enumeration
import java.util.SortedMap
fun <T> List<T>.toIndexedMap(): SortedMap<Int, T> =
mapIndexed { i, e -> i to e }.toMap().toSortedMap()
fun <T> List<T>.toEnumeration(): Enumeration<T> {
return object : Enumeration<T> {
var count = 0
override fun hasMoreElements(): Boolean {
return this.count < size
}
override fun nextElement(): T {
if (this.count < size) {
return get(this.count++)
}
throw NoSuchElementException("List enumeration asked for more elements than present")
}
}
}

View file

@ -0,0 +1,33 @@
package org.gotson.komga.infrastructure.web
import org.springframework.boot.web.servlet.FilterRegistrationBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import javax.servlet.Filter
import javax.servlet.FilterChain
import javax.servlet.ServletRequest
import javax.servlet.ServletResponse
import javax.servlet.http.HttpServletRequest
@Configuration
class BracketParamsFilterConfiguration {
@Bean
fun bracketParamsFilter(): FilterRegistrationBean<BracketParamsFilter> =
FilterRegistrationBean(BracketParamsFilter())
.also {
it.addUrlPatterns(
"/api/*"
)
it.setName("queryParamsFilter")
}
inner class BracketParamsFilter : Filter {
override fun doFilter(request: ServletRequest?, response: ServletResponse?, chain: FilterChain?) {
chain?.doFilter(BracketParamsRequestWrapper(request as HttpServletRequest), response)
}
}
}

View file

@ -0,0 +1,34 @@
package org.gotson.komga.infrastructure.web
import org.gotson.komga.infrastructure.language.toEnumeration
import java.util.Enumeration
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletRequestWrapper
class BracketParamsRequestWrapper(request: HttpServletRequest) : HttpServletRequestWrapper(request) {
override fun getParameter(name: String): String? {
val nameWithoutSuffix = name.removeSuffix("[]")
val values = listOfNotNull(super.getParameter(nameWithoutSuffix), super.getParameter("$nameWithoutSuffix[]"))
return if (values.isEmpty()) null
else values.joinToString(",")
}
override fun getParameterValues(name: String): Array<String>? {
val nameWithoutSuffix = name.removeSuffix("[]")
val regular = super.getParameterValues(nameWithoutSuffix)
val suffix = super.getParameterValues("$nameWithoutSuffix[]")
val values = listOfNotNull(regular, suffix)
return if (values.isEmpty()) null
else values.reduce { acc, strings -> acc + strings }
}
override fun getParameterNames(): Enumeration<String> =
super.getParameterNames().toList().map { it.removeSuffix("[]") }.distinct().toEnumeration()
override fun getParameterMap(): MutableMap<String, Array<String>> {
return super.getParameterMap().asSequence()
.groupBy({ it.key.removeSuffix("[]") }, { it.value })
.mapValues { it.value.reduce { acc, strings -> acc + strings } }
.toMutableMap()
}
}

View file

@ -0,0 +1,258 @@
package org.gotson.komga.infrastructure.web
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.springframework.mock.web.MockHttpServletRequest
class BracketParamsRequestWrapperTest {
@Nested
inner class ParameterNames {
@Test
fun `given parameters with and without brackets when getting parameter names then only the name without bracket is returned`() {
// given
val request = MockHttpServletRequest()
request.setParameter("param")
request.setParameter("param[]")
// when
val filtered = BracketParamsRequestWrapper(request)
// then
assertThat(filtered.parameterNames.toList()).containsExactlyInAnyOrder("param")
}
@Test
fun `given parameters with brackets when getting parameter names then only the name without bracket is returned`() {
// given
val request = MockHttpServletRequest()
request.setParameter("param[]")
// when
val filtered = BracketParamsRequestWrapper(request)
// then
assertThat(filtered.parameterNames.toList()).containsExactlyInAnyOrder("param")
}
@Test
fun `given parameters without brackets when getting parameter names then only the name without bracket is returned`() {
// given
val request = MockHttpServletRequest()
request.setParameter("param")
// when
val filtered = BracketParamsRequestWrapper(request)
// then
assertThat(filtered.parameterNames.toList()).containsExactlyInAnyOrder("param")
}
@Test
fun `given empty parameters when getting parameter names then it is empty`() {
// given
val request = MockHttpServletRequest()
// when
val filtered = BracketParamsRequestWrapper(request)
// then
assertThat(filtered.parameterNames.toList()).isEmpty()
}
}
@Nested
inner class ParameterValue {
@Test
fun `given parameters with and without brackets when getting parameter value then both values are joined to string`() {
// given
val request = MockHttpServletRequest()
request.setParameter("param", "a")
request.setParameter("param[]", "b")
// when
val filtered = BracketParamsRequestWrapper(request)
val value = filtered.getParameter("param")
val valueBracket = filtered.getParameter("param[]")
// then
assertThat(value).isEqualTo(valueBracket)
assertThat(value).isEqualTo("a,b")
}
@Test
fun `given parameters with brackets when getting parameter value single value is returned`() {
// given
val request = MockHttpServletRequest()
request.setParameter("param[]", "b")
// when
val filtered = BracketParamsRequestWrapper(request)
val value = filtered.getParameter("param")
val valueBracket = filtered.getParameter("param[]")
// then
assertThat(value).isEqualTo(valueBracket)
assertThat(value).isEqualTo("b")
}
@Test
fun `given parameters without brackets when getting parameter value single value is returned`() {
// given
val request = MockHttpServletRequest()
request.setParameter("param", "b")
// when
val filtered = BracketParamsRequestWrapper(request)
val value = filtered.getParameter("param")
val valueBracket = filtered.getParameter("param[]")
// then
assertThat(value).isEqualTo(valueBracket)
assertThat(value).isEqualTo("b")
}
@Test
fun `given empty parameters when getting parameter value then return null`() {
// given
val request = MockHttpServletRequest()
// when
val filtered = BracketParamsRequestWrapper(request)
val value = filtered.getParameter("param")
// then
assertThat(value).isNull()
}
}
@Nested
inner class ParameterValues {
@Test
fun `given parameters with and without brackets when getting parameter values then both values are returned`() {
// given
val request = MockHttpServletRequest()
request.setParameter("param[]", "a")
request.setParameter("param", "b")
// when
val filtered = BracketParamsRequestWrapper(request)
val values = filtered.getParameterValues("param")
val valuesBracket = filtered.getParameterValues("param[]")
// then
assertThat(values).isEqualTo(valuesBracket)
assertThat(values).containsExactlyInAnyOrder("a", "b")
}
@Test
fun `given parameters with brackets when getting parameter values then value is returned`() {
// given
val request = MockHttpServletRequest()
request.setParameter("param[]", "a")
// when
val filtered = BracketParamsRequestWrapper(request)
val values = filtered.getParameterValues("param")
val valuesBracket = filtered.getParameterValues("param[]")
// then
assertThat(values).isEqualTo(valuesBracket)
assertThat(values).containsExactlyInAnyOrder("a")
}
@Test
fun `given parameters without brackets when getting parameter values then value is returned`() {
// given
val request = MockHttpServletRequest()
request.setParameter("param", "a")
// when
val filtered = BracketParamsRequestWrapper(request)
val values = filtered.getParameterValues("param")
val valuesBracket = filtered.getParameterValues("param[]")
// then
assertThat(values).isEqualTo(valuesBracket)
assertThat(values).containsExactlyInAnyOrder("a")
}
@Test
fun `given empty parameters when getting parameter values then return null`() {
// given
val request = MockHttpServletRequest()
// when
val filtered = BracketParamsRequestWrapper(request)
val values = filtered.getParameterValues("param")
// then
assertThat(values).isNull()
}
}
@Nested
inner class ParameterMap {
@Test
fun `given parameters with and without brackets when getting parameter map then both values are returned`() {
// given
val request = MockHttpServletRequest()
request.setParameter("param[]", "a")
request.setParameter("param", "b")
// when
val filtered = BracketParamsRequestWrapper(request)
val map = filtered.parameterMap
// then
assertThat(map.keys).hasSize(1)
assertThat(map.keys).containsExactly("param")
assertThat(map["param"]).containsExactlyInAnyOrder("a", "b")
}
@Test
fun `given parameters with brackets when getting parameter map then key without bracket is returned`() {
// given
val request = MockHttpServletRequest()
request.setParameter("param[]", "a")
// when
val filtered = BracketParamsRequestWrapper(request)
val map = filtered.parameterMap
// then
assertThat(map.keys).hasSize(1)
assertThat(map.keys).containsExactly("param")
assertThat(map["param"]).containsExactlyInAnyOrder("a")
}
@Test
fun `given parameters without brackets when getting parameter map then key without bracket is returned`() {
// given
val request = MockHttpServletRequest()
request.setParameter("param", "a")
// when
val filtered = BracketParamsRequestWrapper(request)
val map = filtered.parameterMap
// then
assertThat(map.keys).hasSize(1)
assertThat(map.keys).containsExactly("param")
assertThat(map["param"]).containsExactlyInAnyOrder("a")
}
@Test
fun `given empty parameters when getting parameter map then empty map is returned`() {
// given
val request = MockHttpServletRequest()
// when
val filtered = BracketParamsRequestWrapper(request)
val map = filtered.parameterMap
// then
assertThat(map).isEmpty()
}
}
}