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;