Skip to content

Commit 1bd422b

Browse files
committed
Support multipart uploads when Spring MVC uses a different management port
Closes gh-18286
1 parent e6352ee commit 1bd422b

File tree

5 files changed

+110
-9
lines changed

5 files changed

+110
-9
lines changed

Diff for: spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/WebMvcEndpointChildContextConfiguration.java

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,7 +16,10 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.web.servlet;
1818

19+
import javax.servlet.MultipartConfigElement;
20+
1921
import org.springframework.beans.factory.ListableBeanFactory;
22+
import org.springframework.beans.factory.ObjectProvider;
2023
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
2124
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextType;
2225
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@@ -83,8 +86,11 @@ DispatcherServlet dispatcherServlet() {
8386
}
8487

8588
@Bean(name = DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
86-
DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
87-
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
89+
DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet,
90+
ObjectProvider<MultipartConfigElement> multipartConfig) {
91+
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, "/");
92+
multipartConfig.ifAvailable(registration::setMultipartConfig);
93+
return registration;
8894
}
8995

9096
@Bean(name = DispatcherServlet.HANDLER_MAPPING_BEAN_NAME)
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,40 @@
1616

1717
package smoketest.actuator;
1818

19+
import java.io.IOException;
20+
import java.nio.charset.StandardCharsets;
21+
1922
import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint;
2023
import org.springframework.http.HttpStatus;
2124
import org.springframework.http.ResponseEntity;
2225
import org.springframework.stereotype.Component;
26+
import org.springframework.util.StreamUtils;
2327
import org.springframework.web.bind.annotation.ExceptionHandler;
2428
import org.springframework.web.bind.annotation.GetMapping;
29+
import org.springframework.web.bind.annotation.PostMapping;
30+
import org.springframework.web.bind.annotation.RequestParam;
2531
import org.springframework.web.bind.annotation.RestControllerAdvice;
32+
import org.springframework.web.multipart.MultipartFile;
2633

2734
/**
28-
* The Sample rest controller endpoint with exception.
35+
* The Sample rest controller endpoint.
2936
*
3037
* @author Guirong Hu
3138
*/
3239
@Component
33-
@RestControllerEndpoint(id = "exception")
34-
public class SampleRestControllerEndpointWithException {
40+
@RestControllerEndpoint(id = "rest")
41+
public class SampleRestControllerEndpoint {
3542

36-
@GetMapping("/")
43+
@GetMapping("/exception")
3744
public String exception() {
3845
throw new CustomException();
3946
}
4047

48+
@PostMapping("/upload")
49+
public String upload(@RequestParam("file") MultipartFile file) throws IOException {
50+
return StreamUtils.copyToString(file.getInputStream(), StandardCharsets.UTF_8);
51+
}
52+
4153
@RestControllerAdvice
4254
static class CustomExceptionHandler {
4355

Diff for: spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/ManagementDifferentPortAndEndpointWithExceptionHandlerSampleActuatorApplicationTests.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ class ManagementDifferentPortAndEndpointWithExceptionHandlerSampleActuatorApplic
4343
private int managementPort;
4444

4545
@Test
46-
void testExceptionHandlerRestControllerEndpoint() {
46+
void endpointCanUseExceptionHandler() {
4747
ResponseEntity<String> entity = new TestRestTemplate("user", "password")
48-
.getForEntity("http://localhost:" + this.managementPort + "/actuator/exception", String.class);
48+
.getForEntity("http://localhost:" + this.managementPort + "/actuator/rest/exception", String.class);
4949
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.I_AM_A_TEAPOT);
5050
assertThat(entity.getBody()).isEqualTo("this is a custom exception body");
5151
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright 2012-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package smoketest.actuator;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint;
22+
import org.springframework.boot.test.context.SpringBootTest;
23+
import org.springframework.boot.test.web.client.TestRestTemplate;
24+
import org.springframework.boot.test.web.server.LocalManagementPort;
25+
import org.springframework.context.annotation.Bean;
26+
import org.springframework.context.annotation.Configuration;
27+
import org.springframework.core.io.ClassPathResource;
28+
import org.springframework.http.HttpEntity;
29+
import org.springframework.http.HttpHeaders;
30+
import org.springframework.http.HttpStatus;
31+
import org.springframework.http.MediaType;
32+
import org.springframework.http.ResponseEntity;
33+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
34+
import org.springframework.security.web.SecurityFilterChain;
35+
import org.springframework.util.LinkedMultiValueMap;
36+
import org.springframework.util.MultiValueMap;
37+
38+
import static org.assertj.core.api.Assertions.assertThat;
39+
40+
/**
41+
* Integration tests for separate management and main service ports with Actuator's MVC
42+
* {@link RestControllerEndpoint rest controller endpoints} and multipart uploads.
43+
*
44+
* @author Guirong Hu
45+
*/
46+
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {
47+
ManagementDifferentPortAndEndpointWithMultipartUploadSampleActuatorApplicationTests.SecurityConfiguration.class,
48+
SampleActuatorApplication.class },
49+
properties = { "management.endpoints.web.exposure.include=*", "management.server.port=0" })
50+
class ManagementDifferentPortAndEndpointWithMultipartUploadSampleActuatorApplicationTests {
51+
52+
@LocalManagementPort
53+
private int managementPort;
54+
55+
@Test
56+
void endpointShouldBeSupportMultipartUpload() {
57+
HttpHeaders requestHeaders = new HttpHeaders();
58+
requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
59+
60+
MultiValueMap<String, Object> requestBody = new LinkedMultiValueMap<>();
61+
requestBody.add("file", new ClassPathResource("test-file.txt"));
62+
63+
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(requestBody, requestHeaders);
64+
65+
ResponseEntity<String> responseEntity = new TestRestTemplate("user", "password").postForEntity(
66+
"http://localhost:" + this.managementPort + "/actuator/rest/upload", requestEntity, String.class);
67+
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
68+
assertThat(responseEntity.getBody()).isEqualTo("this is a test file");
69+
}
70+
71+
@Configuration(proxyBeanMethods = false)
72+
static class SecurityConfiguration {
73+
74+
@Bean
75+
SecurityFilterChain configure(HttpSecurity http) throws Exception {
76+
http.csrf().disable();
77+
return http.build();
78+
}
79+
80+
}
81+
82+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
this is a test file

0 commit comments

Comments
 (0)