Skip to content

MockMvcRequestConverter uses US_ASCII for URL decoding while encoding uses UTF-8 #1034

Merged
wilkinsona merged 1 commit intospring-projects:3.0.xfrom
config25:fix/mockmvc-url-decoder-charset
Apr 8, 2026
Merged

MockMvcRequestConverter uses US_ASCII for URL decoding while encoding uses UTF-8 #1034
wilkinsona merged 1 commit intospring-projects:3.0.xfrom
config25:fix/mockmvc-url-decoder-charset

Conversation

@config25
Copy link
Copy Markdown
Contributor

Summary

MockMvcRequestConverter.decode() uses StandardCharsets.US_ASCII for URL decoding, while urlEncode() in the same class uses StandardCharsets.UTF_8. This causes non-ASCII characters (e.g., CJK, accented characters) in
query parameters to be corrupted.

This was likely introduced unintentionally in commit f5a629af ("Handle form and query parameters separately"), as QueryParameters.decode() created in the same commit correctly uses StandardCharsets.UTF_8.

Changes

  • MockMvcRequestConverter.java: Changed US_ASCIIUTF_8 in decode() method
  • MockMvcRequestConverterTests.java: Added two tests verifying non-ASCII parameter handling for both GET (query string) and POST (form URL encoded body)

Fixes gh-1033

@config25 config25 force-pushed the fix/mockmvc-url-decoder-charset branch from 06e99f3 to aed94c2 Compare March 31, 2026 01:30
@wilkinsona wilkinsona changed the title Fix URL decoding charset mismatch in MockMvcRequestConverter MockMvcRequestConverter uses US_ASCII for URL decoding while encoding uses UTF-8 Mar 31, 2026
@wilkinsona wilkinsona added type: bug A bug and removed status: waiting-for-triage Untriaged issue labels Mar 31, 2026
@wilkinsona wilkinsona added this to the 3.0.x milestone Mar 31, 2026
Comment on lines +153 to +169
@Test
void getRequestWithNonAsciiParametersProducesCorrectQueryString() {
OperationRequest request = createOperationRequest(
MockMvcRequestBuilders.get("/foo").param("name", "\uD64D\uAE38\uB3D9"));
assertThat(request.getUri())
.isEqualTo(URI.create("http://localhost/foo?name=%ED%99%8D%EA%B8%B8%EB%8F%99"));
assertThat(request.getMethod()).isEqualTo(HttpMethod.GET);
}

@Test
void postRequestWithNonAsciiParametersCreatesCorrectFormUrlEncodedContent() {
OperationRequest request = createOperationRequest(
MockMvcRequestBuilders.post("/foo").param("name", "\uD64D\uAE38\uB3D9"));
assertThat(request.getContentAsString()).isEqualTo("name=%ED%99%8D%EA%B8%B8%EB%8F%99");
assertThat(request.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_FORM_URLENCODED);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests don't exercise the changed code. The decode method is only used when the request has both a form URL encoded body and a query string. See postRequestWithParametersAndQueryStringCreatesFormUrlEncodedContentWithoutDuplication below. Instead of adding these two tests, that test could be modified with some UTF-8 values or a new test added to cover that case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the review. You're right — the two added tests don't exercise the decode() method since it's only invoked when the request has both a query string and form parameters. I'll remove those two tests and add a new
one that covers the non-ASCII case with both a query string and parameters, similar to postRequestWithParametersAndQueryStringCreatesFormUrlEncodedContentWithoutDuplication. I'll update the PR shortly.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the updates. There are some formatting problems now (run ./gradlew format to fix those) and I think the new test also fails. Can you please update the PR to address these issues?

Copy link
Copy Markdown
Contributor Author

@config25 config25 Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching those issues! I've pushed an update that addresses both:

Formatting:
Ran ./gradlew format to fix the formatting problems.

Failing test:
The test was failing because the URL template "/foo?name=%ED%99%8D%EA%B8%B8%EB%8F%99" was being double-encoded.
MockMvcRequestBuilders.post(...) internally calls UriComponentsBuilder.encode(), which re-encodes % into %25.
As a result, request.getUri() became name=%25ED%2599%258D... instead of the expected name=%ED%99%8D....

I fixed it by passing raw (unencoded) characters in the URL template and letting Spring handle the encoding:

@Test
void postRequestWithNonAsciiParametersAndQueryStringCreatesFormUrlEncodedContentWithoutDuplication() {
    OperationRequest request = createOperationRequest(
        MockMvcRequestBuilders.post("/foo?name=\uD64D\uAE38\uB3D9")
            .param("name", "\uD64D\uAE38\uB3D9")
            .param("other", "\uD64D\uAE38\uB3D9")
    );

    assertThat(request.getUri())
        .isEqualTo(URI.create("http://localhost/foo?name=%ED%99%8D%EA%B8%B8%EB%8F%99"));

    assertThat(request.getMethod())
        .isEqualTo(HttpMethod.POST);

    assertThat(request.getContentAsString())
        .isEqualTo("other=%ED%99%8D%EA%B8%B8%EB%8F%99");

    assertThat(request.getHeaders().getContentType())
        .isEqualTo(MediaType.APPLICATION_FORM_URLENCODED);
}

This test now covers the case where both a query string and form parameters are present, which is the scenario where decoding and de-duplication are applied. It verifies that non-ASCII query parameter values are handled correctly and that duplicate parameters are removed from the form body.

Verified locally that:
./gradlew :spring-restdocs-mockmvc:checkFormat :spring-restdocs-mockmvc:test passes.

@wilkinsona wilkinsona added the status: waiting-for-feedback Feedback is required before progress can be made label Mar 31, 2026
@config25 config25 force-pushed the fix/mockmvc-url-decoder-charset branch from 24924a2 to fbf7cba Compare March 31, 2026 23:32
@wilkinsona wilkinsona removed the status: waiting-for-feedback Feedback is required before progress can be made label Apr 8, 2026
@wilkinsona wilkinsona changed the base branch from main to 3.0.x April 8, 2026 08:08
@wilkinsona wilkinsona force-pushed the fix/mockmvc-url-decoder-charset branch from c350ad4 to adffb9a Compare April 8, 2026 08:08
@wilkinsona wilkinsona merged commit 4a55491 into spring-projects:3.0.x Apr 8, 2026
1 check passed
@wilkinsona
Copy link
Copy Markdown
Member

Thanks very much, @config25.

@wilkinsona wilkinsona modified the milestones: 3.0.x, 3.0.6 Apr 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MockMvcRequestConverter uses US_ASCII for URL decoding while encoding uses UTF-8

3 participants