springcloudurlencodegateway

Spring Cloud Gateway. using + (plus) in the value of a query parameter


We are using Spring Cloud Gateway (MVC version) in front o a Rails service (let's call it mailbox) and are struggling when the value of a query parameter includes the + (plus) character. Here is an example:

  1. Client sends a request to the gateway with ?email=bob+test@mail.com
  2. Gateway decodes the parameter value, turning + into a space.
  3. Before sending the request to mailbox, it encodes back the value, resulting in email=bob%20test@mail.com
  4. When receiving this param, mailbox decodes it as email=bob test@mail.com which makes it fail

An alternative was to encode + in the client, but then we have:

  1. Client sends a request to the gateway with ?email=bob%2Btest@mail.com
  2. Gateway decodes the parameter value, turning %2B into +.
  3. Before sending the request to mailbox, it encodes back the value but, given that + is not considered special character, it stays as email=bob+test@mail.com
  4. When receiving this param, mailbox decodes it as email=bob test@mail.com which makes it fail

I can't find a way to either tell gateway not to decode the original request or force it to encode + before sending the request to mailbox. Is there any way to do that? Is there any other solution? I can't think of anything. It's like all the steps taken are okay (first decode, then encode), but the final result is wrong. I need mailbox to receive an email with a + in it, but I can't.

Thanks for your help!


Solution

  • We encountered the same issue and were able to work around the problem by manually encoding + in the forwarded requests after the existing encoder has run.

    This was achieved by using a customised ClientHttpRequestFactory as follows:

    @Configuration
    class WebMvcConfiguration {
    
        @Bean
        fun clientHttpRequestFactory(): ClientHttpRequestFactory =
            CustomClientHttpRequestFactory()
    
    }
    
    class CustomClientHttpRequestFactory : JdkClientHttpRequestFactory() {
    
        override fun createRequest(uri: URI, httpMethod: HttpMethod): ClientHttpRequest {
            val encodedURI = URI(customUriEncode(uri.toString()))
            return super.createRequest(encodedURI, httpMethod)
        }
    
        private fun customUriEncode(str: String): String = 
            str.replace(oldValue = "+", newValue = "%2B")
    
    }
    

    We believe this to be safe, because both + and %20 in the original request are decoded into (a space) before it reaches the request factory, so will not be re-encoded back into %2B.