diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/WebMvcEndpointChildContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/WebMvcEndpointChildContextConfiguration.java index e55b983b2c7a..f0566911d0f4 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/WebMvcEndpointChildContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/WebMvcEndpointChildContextConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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,7 +16,10 @@ package org.springframework.boot.actuate.autoconfigure.web.servlet; +import jakarta.servlet.MultipartConfigElement; + import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextType; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -83,8 +86,11 @@ DispatcherServlet dispatcherServlet() { } @Bean(name = DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME) - DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) { - return new DispatcherServletRegistrationBean(dispatcherServlet, "/"); + DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet, + ObjectProvider multipartConfig) { + DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, "/"); + multipartConfig.ifAvailable(registration::setMultipartConfig); + return registration; } @Bean(name = DispatcherServlet.HANDLER_MAPPING_BEAN_NAME) diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/main/java/smoketest/actuator/SampleRestControllerEndpointWithException.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/main/java/smoketest/actuator/SampleRestControllerEndpoint.java similarity index 70% rename from spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/main/java/smoketest/actuator/SampleRestControllerEndpointWithException.java rename to spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/main/java/smoketest/actuator/SampleRestControllerEndpoint.java index 26207123f151..6674ae4c4d20 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/main/java/smoketest/actuator/SampleRestControllerEndpointWithException.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/main/java/smoketest/actuator/SampleRestControllerEndpoint.java @@ -16,28 +16,40 @@ package smoketest.actuator; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; +import org.springframework.util.StreamUtils; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.multipart.MultipartFile; /** - * The Sample rest controller endpoint with exception. + * The Sample rest controller endpoint. * * @author Guirong Hu */ @Component -@RestControllerEndpoint(id = "exception") -public class SampleRestControllerEndpointWithException { +@RestControllerEndpoint(id = "rest") +public class SampleRestControllerEndpoint { - @GetMapping("/") + @GetMapping("/exception") public String exception() { throw new CustomException(); } + @PostMapping("/upload") + public String upload(@RequestParam("file") MultipartFile file) throws IOException { + return StreamUtils.copyToString(file.getInputStream(), StandardCharsets.UTF_8); + } + @RestControllerAdvice static class CustomExceptionHandler { diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/ManagementDifferentPortAndEndpointWithExceptionHandlerSampleActuatorApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/ManagementDifferentPortAndEndpointWithExceptionHandlerSampleActuatorApplicationTests.java index 89e0392177f8..b963a6e59298 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/ManagementDifferentPortAndEndpointWithExceptionHandlerSampleActuatorApplicationTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/ManagementDifferentPortAndEndpointWithExceptionHandlerSampleActuatorApplicationTests.java @@ -43,9 +43,9 @@ class ManagementDifferentPortAndEndpointWithExceptionHandlerSampleActuatorApplic private int managementPort; @Test - void testExceptionHandlerRestControllerEndpoint() { + void endpointCanUseExceptionHandler() { ResponseEntity entity = new TestRestTemplate("user", "password") - .getForEntity("http://localhost:" + this.managementPort + "/actuator/exception", String.class); + .getForEntity("http://localhost:" + this.managementPort + "/actuator/rest/exception", String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.I_AM_A_TEAPOT); assertThat(entity.getBody()).isEqualTo("this is a custom exception body"); } diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/ManagementDifferentPortAndEndpointWithMultipartUploadSampleActuatorApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/ManagementDifferentPortAndEndpointWithMultipartUploadSampleActuatorApplicationTests.java new file mode 100644 index 000000000000..bc28694077a9 --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/ManagementDifferentPortAndEndpointWithMultipartUploadSampleActuatorApplicationTests.java @@ -0,0 +1,82 @@ +/* + * Copyright 2012-2022 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package smoketest.actuator; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.server.LocalManagementPort; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for separate management and main service ports with Actuator's MVC + * {@link RestControllerEndpoint rest controller endpoints} and multipart uploads. + * + * @author Guirong Hu + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = { + ManagementDifferentPortAndEndpointWithMultipartUploadSampleActuatorApplicationTests.SecurityConfiguration.class, + SampleActuatorApplication.class }, + properties = { "management.endpoints.web.exposure.include=*", "management.server.port=0" }) +class ManagementDifferentPortAndEndpointWithMultipartUploadSampleActuatorApplicationTests { + + @LocalManagementPort + private int managementPort; + + @Test + void endpointShouldBeSupportMultipartUpload() { + HttpHeaders requestHeaders = new HttpHeaders(); + requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA); + + MultiValueMap requestBody = new LinkedMultiValueMap<>(); + requestBody.add("file", new ClassPathResource("test-file.txt")); + + HttpEntity> requestEntity = new HttpEntity<>(requestBody, requestHeaders); + + ResponseEntity responseEntity = new TestRestTemplate("user", "password").postForEntity( + "http://localhost:" + this.managementPort + "/actuator/rest/upload", requestEntity, String.class); + assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(responseEntity.getBody()).isEqualTo("this is a test file"); + } + + @Configuration(proxyBeanMethods = false) + static class SecurityConfiguration { + + @Bean + SecurityFilterChain configure(HttpSecurity http) throws Exception { + http.csrf().disable(); + return http.build(); + } + + } + +} diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/resources/test-file.txt b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/resources/test-file.txt new file mode 100644 index 000000000000..68a4528a66e8 --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/resources/test-file.txt @@ -0,0 +1 @@ +this is a test file \ No newline at end of file