Merge branch '3.2.x'

Closes gh-39445
This commit is contained in:
Scott Frederick 2024-02-07 14:41:41 -06:00
commit c05942d616
3 changed files with 70 additions and 3 deletions

View File

@ -83,6 +83,7 @@ import org.springframework.web.util.pattern.PathPattern;
* @author Madhura Bhave * @author Madhura Bhave
* @author Phillip Webb * @author Phillip Webb
* @author Brian Clozel * @author Brian Clozel
* @author Scott Frederick
* @since 2.0.0 * @since 2.0.0
*/ */
@ImportRuntimeHints(AbstractWebFluxEndpointHandlerMappingRuntimeHints.class) @ImportRuntimeHints(AbstractWebFluxEndpointHandlerMappingRuntimeHints.class)
@ -260,6 +261,26 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi
} }
protected static final class ExceptionCapturingInvoker implements OperationInvoker {
private final OperationInvoker invoker;
public ExceptionCapturingInvoker(OperationInvoker invoker) {
this.invoker = invoker;
}
@Override
public Object invoke(InvocationContext context) {
try {
return this.invoker.invoke(context);
}
catch (Exception ex) {
return Mono.error(ex);
}
}
}
/** /**
* Reactive handler providing actuator links at the root endpoint. * Reactive handler providing actuator links at the root endpoint.
*/ */
@ -303,9 +324,9 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi
private OperationInvoker getInvoker(WebOperation operation) { private OperationInvoker getInvoker(WebOperation operation) {
OperationInvoker invoker = operation::invoke; OperationInvoker invoker = operation::invoke;
if (operation.isBlocking()) { if (operation.isBlocking()) {
invoker = new ElasticSchedulerInvoker(invoker); return new ElasticSchedulerInvoker(invoker);
} }
return invoker; return new ExceptionCapturingInvoker(invoker);
} }
private Supplier<Mono<? extends SecurityContext>> getSecurityContextSupplier() { private Supplier<Mono<? extends SecurityContext>> getSecurityContextSupplier() {

View File

@ -226,6 +226,31 @@ public abstract class AbstractWebEndpointIntegrationTests<T extends Configurable
.isEqualTo("1 2")); .isEqualTo("1 2"));
} }
@Test
void readOperationWithQueryParametersMissing() {
load(QueryEndpointConfiguration.class,
(client) -> client.get().uri("/query").exchange().expectStatus().isBadRequest());
}
@Test
void reactiveReadOperationWithSingleQueryParameters() {
load(ReactiveQueryEndpointConfiguration.class,
(client) -> client.get()
.uri("/query?param=test")
.exchange()
.expectStatus()
.isOk()
.expectBody()
.jsonPath("query")
.isEqualTo("test"));
}
@Test
void reactiveReadOperationWithQueryParametersMissing() {
load(ReactiveQueryEndpointConfiguration.class,
(client) -> client.get().uri("/query").exchange().expectStatus().isBadRequest());
}
@Test @Test
void readOperationWithSingleQueryParametersAndMultipleValues() { void readOperationWithSingleQueryParametersAndMultipleValues() {
load(QueryEndpointConfiguration.class, load(QueryEndpointConfiguration.class,
@ -732,6 +757,17 @@ public abstract class AbstractWebEndpointIntegrationTests<T extends Configurable
} }
@Configuration(proxyBeanMethods = false)
@Import(BaseConfiguration.class)
static class ReactiveQueryEndpointConfiguration {
@Bean
ReactiveQueryEndpoint reactiveQueryEndpoint() {
return new ReactiveQueryEndpoint();
}
}
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@Import(BaseConfiguration.class) @Import(BaseConfiguration.class)
static class VoidWriteResponseEndpointConfiguration { static class VoidWriteResponseEndpointConfiguration {
@ -974,6 +1010,16 @@ public abstract class AbstractWebEndpointIntegrationTests<T extends Configurable
} }
@Endpoint(id = "query")
static class ReactiveQueryEndpoint {
@ReadOperation
Mono<Map<String, String>> query(String param) {
return Mono.just(Collections.singletonMap("query", param));
}
}
@Endpoint(id = "voidwrite") @Endpoint(id = "voidwrite")
static class VoidWriteResponseEndpoint { static class VoidWriteResponseEndpoint {

View File

@ -57,7 +57,7 @@ class ReactiveSessionsEndpointWebIntegrationTests {
.uri((builder) -> builder.path("/actuator/sessions").build()) .uri((builder) -> builder.path("/actuator/sessions").build())
.exchange() .exchange()
.expectStatus() .expectStatus()
.is5xxServerError(); // https://github.com/spring-projects/spring-boot/issues/39236 .is4xxClientError();
} }
@WebEndpointTest(infrastructure = Infrastructure.WEBFLUX) @WebEndpointTest(infrastructure = Infrastructure.WEBFLUX)