RestTemplate 上 URI 變量的編碼
一、概述
在本教程中,我們將學習如何在 Spring 的RestTemplate
上對 URI 變量進行編碼。
我們面臨的常見編碼問題之一是當我們有一個包含加號 ( +
) 的 URI 變量時。例如,如果我們有一個值為http://localhost:8080/api/v1/plus+sign
的 URI 變量,加號將被編碼為空格,這可能會導致意外的服務器響應。
讓我們看看解決這個問題的幾種方法。
2.項目設置
我們將創建一個使用RestTemplate
調用 API 的小項目。
2.1. Spring Web 依賴
讓我們從將Spring Web Starter依賴項添加到我們的pom.xml
開始:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
或者,我們可以使用Spring Initializr生成項目並添加依賴項。
2.2. RestTemplate
Bean
接下來,我們將創建一個RestTemplate
bean:
@Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } }
3.API調用
讓我們創建一個調用公共 API [http://httpbin.org/get](https://httpbin.org/get)
的服務類。
API 返回帶有請求參數的 JSON 響應。例如,如果我們在瀏覽器上調用 URL [https://httpbin.org/get?parameter=springboot](https://httpbin.org/get?parameter=springboot)
,我們會得到這樣的響應:
{ "args": { "parameter": "springboot" }, "headers": { }, "origin": "", "url": "" }
這裡, args
對象包含請求參數。為簡潔起見,省略了其他值。
3.1.服務等級
讓我們創建一個調用 API 並返回parameter
鍵值的服務類:
`@Service
public class HttpBinService {
private final RestTemplate restTemplate;
public HttpBinService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public String get(String parameter) {
String url = "http://httpbin.org/get?parameter={parameter}";
ResponseEntity
get()
方法調用指定的 URL,將響應解析為Map
,並檢索args
對像中字段parameter
的值。
3.2.測試
讓我們用兩個參數測試我們的服務類springboot
和spring+boot
並檢查響應是否符合預期:
`@SpringBootTest
class HttpBinServiceTest {
@Autowired
private HttpBinService httpBinService;
@Test
void givenWithoutPlusSign_whenGet_thenSameValueReturned() throws JsonProcessingException {
String parameterWithoutPlusSign = "springboot";
String responseWithoutPlusSign = httpBinService.get(parameterWithoutPlusSign);
assertEquals(parameterWithoutPlusSign, responseWithoutPlusSign);
}
@Test
void givenWithPlusSign_whenGet_thenSameValueReturned() throws JsonProcessingException {
String parameterWithPlusSign = "spring+boot";
String responseWithPlusSign = httpBinService.get(parameterWithPlusSign);
assertEquals(parameterWithPlusSign, responseWithPlusSign);
}
}`
如果我們運行測試,我們會看到第二個測試失敗了。響應是spring boot
而不是spring+boot
。
4. 將攔截器與RestTemplate
使用
我們可以使用攔截器對 URI 變量進行編碼。
讓我們創建一個實現ClientHttpRequestInterceptor
接口的類:
public class UriEncodingInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { HttpRequest encodedRequest = new HttpRequestWrapper(request) { @Override public URI getURI() { URI uri = super.getURI(); String escapedQuery = uri.getRawQuery().replace("+", "%2B"); return UriComponentsBuilder.fromUri(uri) .replaceQuery(escapedQuery) .build(true).toUri(); } }; return execution.execute(encodedRequest, body); } }
我們已經實現了intercept()
方法。此方法將在RestTemplate
發出每個請求之前執行。
讓我們分解一下代碼:
- 我們創建了一個新的
HttpRequest
對象來包裝原始請求。 - 對於這個包裝器,我們覆蓋了
getURI()
方法來編碼 URI 變量。在這種情況下,我們將查詢字符串中的加號替換為%2B
。 - 使用
UriComponentsBuilder
,我們創建一個新的URI
並將查詢字符串替換為編碼的查詢字符串。 - 我們從將替換原始請求的
intercept()
方法返回編碼請求。
4.1.添加攔截器
接下來,我們需要將攔截器添加到RestTemplate
bean:
@Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate() { RestTemplate restTemplate = new RestTemplate(); restTemplate.setInterceptors(Collections.singletonList(new UriEncodingInterceptor())); return restTemplate; } }
如果我們再次運行測試,我們會看到它通過了。
攔截器提供了更改我們想要的請求的任何部分的靈活性。它們對於復雜的場景很有用,例如添加額外的標頭或對請求中的字段執行更改。
對於像我們的示例這樣更簡單的任務,我們還可以使用DefaultUriBuilderFactory
來更改編碼。接下來讓我們看看如何做到這一點。
5. 使用DefaultUriBuilderFactory
另一種對 URI 變量進行編碼的方法是更改DefaultUriBuilderFactory
內部使用的RestTemplate
對象。
默認情況下,URI 構建器首先對整個 URL 進行編碼,然後分別對值進行編碼。我們將創建一個新的DefaultUriBuilderFactory
對象並將編碼模式設置為VALUES_ONLY.
這將編碼限制為僅值。
然後我們可以使用setUriTemplateHandler()
方法在我們的RestTemplate
bean 中設置新的DefaultUriBuilderFactory
對象。
讓我們用它來創建一個新的RestTemplate
bean:
@Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate() { RestTemplate restTemplate = new RestTemplate(); DefaultUriBuilderFactory defaultUriBuilderFactory = new DefaultUriBuilderFactory(); defaultUriBuilderFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY); restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory()); return restTemplate; } }
這是對 URI 變量進行編碼的另一種選擇。同樣,如果我們運行測試,我們會看到它通過了。
六,結論
在本文中,我們了解瞭如何在RestTemplate
請求中對 URI 變量進行編碼。我們看到了執行此操作的兩種方法 — 使用攔截器和更改DefaultUriBuilderFactory
對象。
與往常一樣,本文中使用的代碼示例可以 在 GitHub 上找到。