From 0f421f9f86fc6a06d2b41fbe538ac6b97ed2470f Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Fri, 30 Jul 2021 14:42:45 +0200 Subject: [PATCH] Support default character encoding for response in MockMvc Commit e4b9b1fadb introduced support for setting the default character encoding in MockHttpServletResponse. This commit introduces support for configuring the default character encoding in the underlying MockHttpServletResponse used in MockMvc. Closes gh-27230 --- .../test/web/servlet/MockMvc.java | 16 ++++++++++++++ .../web/servlet/MockMvcBuilderSupport.java | 21 ++++++++++++++++++- .../servlet/setup/AbstractMockMvcBuilder.java | 21 +++++++++++++++++-- .../setup/ConfigurableMockMvcBuilder.java | 18 +++++++++++++++- .../samples/standalone/ResponseBodyTests.java | 16 +++++++------- 5 files changed, 81 insertions(+), 11 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvc.java b/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvc.java index 39492cbd0a..0c8a28c7fd 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvc.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvc.java @@ -16,6 +16,7 @@ package org.springframework.test.web.servlet; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; @@ -78,6 +79,9 @@ public final class MockMvc { @Nullable private RequestBuilder defaultRequestBuilder; + @Nullable + private Charset defaultResponseCharacterEncoding; + private List defaultResultMatchers = new ArrayList<>(); private List defaultResultHandlers = new ArrayList<>(); @@ -106,6 +110,14 @@ public final class MockMvc { this.defaultRequestBuilder = requestBuilder; } + /** + * The default character encoding to be applied to every response. + * @see org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder#defaultResponseCharacterEncoding(Charset) + */ + void setDefaultResponseCharacterEncoding(@Nullable Charset defaultResponseCharacterEncoding) { + this.defaultResponseCharacterEncoding = defaultResponseCharacterEncoding; + } + /** * Expectations to assert after every performed request. * @see org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder#alwaysExpect(ResultMatcher) @@ -169,6 +181,10 @@ public final class MockMvc { servletResponse = mockResponse; } + if (this.defaultResponseCharacterEncoding != null) { + mockResponse.setDefaultCharacterEncoding(this.defaultResponseCharacterEncoding.name()); + } + if (requestBuilder instanceof SmartRequestBuilder) { request = ((SmartRequestBuilder) requestBuilder).postProcessRequest(request); } diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvcBuilderSupport.java b/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvcBuilderSupport.java index d08a96f322..d635b00b1b 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvcBuilderSupport.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/MockMvcBuilderSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package org.springframework.test.web.servlet; +import java.nio.charset.Charset; import java.util.List; import javax.servlet.Filter; @@ -37,10 +38,28 @@ import org.springframework.web.context.WebApplicationContext; * @author Rossen Stoyanchev * @author Rob Winch * @author Stephane Nicoll + * @author Sam Brannen * @since 3.2 */ public abstract class MockMvcBuilderSupport { + /** + * Delegates to {@link #createMockMvc(Filter[], MockServletConfig, WebApplicationContext, RequestBuilder, List, List, List)} + * for creation of the {@link MockMvc} instance and configures that instance + * with the supplied {@code defaultResponseCharacterEncoding}. + * @since 5.3.10 + */ + protected final MockMvc createMockMvc(Filter[] filters, MockServletConfig servletConfig, + WebApplicationContext webAppContext, @Nullable RequestBuilder defaultRequestBuilder, + @Nullable Charset defaultResponseCharacterEncoding, + List globalResultMatchers, List globalResultHandlers, + @Nullable List dispatcherServletCustomizers) { + + MockMvc mockMvc = createMockMvc(filters, servletConfig, webAppContext, defaultRequestBuilder, globalResultMatchers, globalResultHandlers, dispatcherServletCustomizers); + mockMvc.setDefaultResponseCharacterEncoding(defaultResponseCharacterEncoding); + return mockMvc; + } + protected final MockMvc createMockMvc(Filter[] filters, MockServletConfig servletConfig, WebApplicationContext webAppContext, @Nullable RequestBuilder defaultRequestBuilder, List globalResultMatchers, List globalResultHandlers, diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/AbstractMockMvcBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/AbstractMockMvcBuilder.java index de05563d70..e5ed43ce30 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/AbstractMockMvcBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/AbstractMockMvcBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package org.springframework.test.web.servlet.setup; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; @@ -48,6 +49,7 @@ import org.springframework.web.context.WebApplicationContext; * * @author Rossen Stoyanchev * @author Stephane Nicoll + * @author Sam Brannen * @since 4.0 * @param a self reference to the builder type */ @@ -59,6 +61,9 @@ public abstract class AbstractMockMvcBuilder @Nullable private RequestBuilder defaultRequestBuilder; + @Nullable + private Charset defaultResponseCharacterEncoding; + private final List globalResultMatchers = new ArrayList<>(); private final List globalResultHandlers = new ArrayList<>(); @@ -95,6 +100,17 @@ public abstract class AbstractMockMvcBuilder return self(); } + /** + * Define the default character encoding to be applied to every response. + * @param defaultResponseCharacterEncoding the default response character encoding + * @since 5.3.10 + */ + @Override + public final T defaultResponseCharacterEncoding(Charset defaultResponseCharacterEncoding) { + this.defaultResponseCharacterEncoding = defaultResponseCharacterEncoding; + return self(); + } + @Override public final T alwaysExpect(ResultMatcher resultMatcher) { this.globalResultMatchers.add(resultMatcher); @@ -157,7 +173,8 @@ public abstract class AbstractMockMvcBuilder Filter[] filterArray = this.filters.toArray(new Filter[0]); return super.createMockMvc(filterArray, mockServletConfig, wac, this.defaultRequestBuilder, - this.globalResultMatchers, this.globalResultHandlers, this.dispatcherServletCustomizers); + this.defaultResponseCharacterEncoding, this.globalResultMatchers, this.globalResultHandlers, + this.dispatcherServletCustomizers); } /** diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/ConfigurableMockMvcBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/ConfigurableMockMvcBuilder.java index a619ee7a58..43ef6ca954 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/ConfigurableMockMvcBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/ConfigurableMockMvcBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ package org.springframework.test.web.servlet.setup; +import java.nio.charset.Charset; + import javax.servlet.Filter; import org.springframework.test.web.servlet.DispatcherServletCustomizer; @@ -28,6 +30,7 @@ import org.springframework.test.web.servlet.ResultMatcher; * Defines common methods for building a {@code MockMvc}. * * @author Rossen Stoyanchev + * @author Sam Brannen * @since 4.1 * @param a self reference to the builder type */ @@ -81,6 +84,18 @@ public interface ConfigurableMockMvcBuilder T defaultRequest(RequestBuilder requestBuilder); + /** + * Define the default character encoding to be applied to every response. + *

The default implementation of this method ignores the supplied value. + * Concrete implementations are therefore encouraged to override this method. + * @param defaultResponseCharacterEncoding the default response character encoding + * @since 5.3.10 + */ + @SuppressWarnings("unchecked") + default T defaultResponseCharacterEncoding(Charset defaultResponseCharacterEncoding) { + return (T) this; + } + /** * Define a global expectation that should always be applied to * every response. For example, status code 200 (OK), content type @@ -110,6 +125,7 @@ public interface ConfigurableMockMvcBuilder T addDispatcherServletCustomizer(DispatcherServletCustomizer customizer); diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/ResponseBodyTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/ResponseBodyTests.java index 2799b24d19..e6cbe17338 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/ResponseBodyTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/ResponseBodyTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,6 @@ package org.springframework.test.web.servlet.samples.standalone; -import javax.validation.constraints.NotNull; - import org.junit.jupiter.api.Test; import org.springframework.http.MediaType; @@ -25,6 +23,8 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; @@ -42,11 +42,14 @@ class ResponseBodyTests { @Test void json() throws Exception { - standaloneSetup(new PersonController()).build() - .perform(get("/person/Lee").accept(MediaType.APPLICATION_JSON)) + standaloneSetup(new PersonController()).defaultResponseCharacterEncoding(UTF_8).build() + // We use a name containing an umlaut to test UTF-8 encoding for the request and the response. + .perform(get("/person/Jürgen").characterEncoding(UTF_8.name()).accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentType("application/json")) - .andExpect(jsonPath("$.name").value("Lee")) + .andExpect(content().encoding(UTF_8.name())) + .andExpect(content().string(containsString("Jürgen"))) + .andExpect(jsonPath("$.name").value("Jürgen")) .andExpect(jsonPath("$.age").value(42)) .andExpect(jsonPath("$.age").value(42.0f)) .andExpect(jsonPath("$.age").value(equalTo(42))) @@ -70,7 +73,6 @@ class ResponseBodyTests { @SuppressWarnings("unused") private static class Person { - @NotNull private final String name; private int age;