Affects: spring-boot 3.1.4, spring 6.0.12

Issue: When using Spring servlet router functions the Content-Type header is changed by the framework. Spring adds charset=UTF-8 to it, if the client doesn't provide it.

My background: In our project we are calculating a hash-sum over specific request parameters on client and server-side and compare them. Due to the changed Content-Type header the sums don't match.

Steps to reproduce:

You can reproduce this with a simple router function:

@Configuration
class RouterPoC(
    private val handlerPoc: HandlerPoc
) {
    @Bean
    fun demo() = router {
        GET("/router", handlerPoc::checkContentType)
    }
}

@Component
class HandlerPoc {

    private val logger = LoggerFactory.getLogger(this::class.java)

    fun checkContentType(request: ServerRequest): ServerResponse {
        val contentType = request.headers().contentTypeOrNull()!!.toString()
        logger.info(contentType)
        check(!contentType.endsWith("charset=UTF-8"))
        return ServerResponse.ok().build()
    }
}

You can find this demo code here: https://github.com/detached/spring-charset-poc/

And then sending a request with curl -X GET -H 'Content-Type: application/json' localhost:8080/router

The resulting Content-Type value is application/json;charset=UTF-8. I would expect that the header stays untouched like application/json as it is when using @RestController instead.

While debugging the code I saw that the CharacterEncodingFilter sets the character encoding of the Request. The charset is added by DefaultServerRequest parsing the servletRequest with ServletServerHttpRequest. The getHeaders() function adds then the charset to the header.

When asking on Stack-Overflow for that I got no good solution.

Comment From: poutsma

As you suspected, this is due to the CharacterEncodingFilter which Spring Boot includes by default, which adds the UTF-8 encoding if not set. If you don't want this filter to be enabled, put server.servlet.encoding.enabled=false in your application.properties.

ServerRequest.headers.contentType is composed from the contentType and characterEncoding properties of the HttpServletRequest. So it makes sense that the encoding set by CharacterEncodingFilter is reflected in HandlerPoc. To do the same what ControllerPoC does, read the servlet property instead: ServerRequest.servletRequest.contentType.

Comment From: detached

Thank you very much for the quick answer!