diff --git a/spring-web/src/main/kotlin/org/springframework/web/client/RestClientExtensions.kt b/spring-web/src/main/kotlin/org/springframework/web/client/RestClientExtensions.kt index 12092af8dfe..5159993951a 100644 --- a/spring-web/src/main/kotlin/org/springframework/web/client/RestClientExtensions.kt +++ b/spring-web/src/main/kotlin/org/springframework/web/client/RestClientExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 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. @@ -18,6 +18,8 @@ package org.springframework.web.client import org.springframework.core.ParameterizedTypeReference import org.springframework.http.ResponseEntity +import org.springframework.web.client.RestClient.RequestHeadersSpec +import org.springframework.web.client.RestClient.RequestHeadersSpec.ExchangeFunction /** * Extension for [RestClient.RequestBodySpec.body] providing a `bodyWithType(...)` variant @@ -51,6 +53,15 @@ inline fun RestClient.ResponseSpec.body(): T? = inline fun RestClient.ResponseSpec.requiredBody(): T = body(object : ParameterizedTypeReference() {}) ?: throw NoSuchElementException("Response body is required") +/** + * Extension for [RestClient.RequestHeadersSpec.exchange] providing a `requiredExchange(...)` variant with a + * non-nullable return value. + * @throws NoSuchElementException if there is no response value + * @since 6.2.6 + */ +fun RequestHeadersSpec<*>.requiredExchange(exchangeFunction: ExchangeFunction, close: Boolean = true): T = + exchange(exchangeFunction, close) ?: throw NoSuchElementException("Response value is required") + /** * Extension for [RestClient.ResponseSpec.toEntity] providing a `toEntity()` variant * leveraging Kotlin reified type parameters. This extension is not subject to type @@ -60,4 +71,5 @@ inline fun RestClient.ResponseSpec.requiredBody(): T = * @since 6.1 */ inline fun RestClient.ResponseSpec.toEntity(): ResponseEntity = - toEntity(object : ParameterizedTypeReference() {}) \ No newline at end of file + toEntity(object : ParameterizedTypeReference() {}) + diff --git a/spring-web/src/test/kotlin/org/springframework/web/client/RestClientExtensionsTests.kt b/spring-web/src/test/kotlin/org/springframework/web/client/RestClientExtensionsTests.kt index 6e915901664..e0a04a16022 100644 --- a/spring-web/src/test/kotlin/org/springframework/web/client/RestClientExtensionsTests.kt +++ b/spring-web/src/test/kotlin/org/springframework/web/client/RestClientExtensionsTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 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. @@ -19,9 +19,12 @@ package org.springframework.web.client import io.mockk.every import io.mockk.mockk import io.mockk.verify +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import org.springframework.core.ParameterizedTypeReference +import org.springframework.http.HttpRequest +import org.springframework.web.client.RestClient.RequestHeadersSpec /** * Mock object based tests for [RestClient] Kotlin extensions @@ -59,6 +62,24 @@ class RestClientExtensionsTests { assertThrows { responseSpec.requiredBody() } } + @Test + fun `RequestHeadersSpec#requiredExchange`() { + val foo = Foo() + every { requestBodySpec.exchange(any>(), any()) } returns foo + val exchangeFunction: (HttpRequest, RequestHeadersSpec.ConvertibleClientHttpResponse) -> Foo? = + { request, response -> foo } + val value = requestBodySpec.requiredExchange(exchangeFunction) + assertThat(value).isEqualTo(foo) + } + + @Test + fun `RequestHeadersSpec#requiredExchange with null response throws NoSuchElementException`() { + every { requestBodySpec.exchange(any>(), any()) } returns null + val exchangeFunction: (HttpRequest, RequestHeadersSpec.ConvertibleClientHttpResponse) -> Foo? = + { request, response -> null } + assertThrows { requestBodySpec.requiredExchange(exchangeFunction) } + } + @Test fun `ResponseSpec#toEntity with reified type parameters`() { responseSpec.toEntity>()