Spring RestTemplate異常:“Not enough variables available to expand”
1.概述
在這個簡短的教程中,我們將仔細研究Spring的RestTemplate
異常IllegalArgumentException
:沒有足夠的變量可擴展。
首先,我們將詳細討論此異常背後的主要原因。然後,我們將展示如何產生它,最後是如何解決它。
2.原因
簡而言之,當我們嘗試在GET request中發送JSON數據時,通常會發生異常。
簡而言之, RestTemplate
提供了getForObject
方法來通過在指定的URL上執行GET請求來獲取表示。
造成此異常的主要原因是**RestTemplate
將封裝在花括號中的JSON數據視為URI變量的佔位符**。
由於我們沒有為預期的URI變量提供任何值,因此getForObject
方法將引發異常。
例如,嘗試發送{“name”:”HP EliteBook”}
作為值:
String url = "http://products.api.com/get?key=a123456789z&criterion={\"name\":\"HP EliteBook\"}";
Product product = restTemplate.getForObject(url, Product.class);
只會導致RestTemplate
引發異常:
java.lang.IllegalArgumentException: Not enough variable values available to expand 'name'
3.示例應用
現在,讓我們看一個示例,說明如何使用RestTemplate
產生此IllegalArgumentException
。
為簡單起見,我們將使用單個GET端點創建用於產品管理的基本REST API。
首先,讓我們創建模型類Product
:
public class Product {
private int id;
private String name;
private double price;
// default constructor + all args constructor + getters + setters
}
接下來,我們將定義一個spring控制器來封裝REST API的邏輯:
@RestController
@RequestMapping("/api")
public class ProductApi {
private List<Product> productList = new ArrayList<>(Arrays.asList(
new Product(1, "Acer Aspire 5", 437),
new Product(2, "ASUS VivoBook", 650),
new Product(3, "Lenovo Legion", 990)
));
@GetMapping("/get")
public Product get(@RequestParam String criterion) throws JsonMappingException, JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
Criterion crt = objectMapper.readValue(criterion, Criterion.class);
if (crt.getProp().equals("name")) {
return findByName(crt.getValue());
}
// Search by other properties (id,price)
return null;
}
private Product findByName(String name) {
for (Product product : this.productList) {
if (product.getName().equals(name)) {
return product;
}
}
return null;
}
// Other methods
}
4.解釋示例應用程序
處理程序方法get()
的基本思想是根據特定條件檢索產品對象.
該條件可以表示為具有兩個鍵的JSON字符串: prop
和value
。
該prop
鍵指的是產品屬性,因此它可以是一個ID,名稱或價格。
如上所示,該條件作為字符串參數傳遞給處理程序方法。我們使用ObjectMapper
類將JSON字符串轉換為Criterion
對象。
這是我們的Criterion
類的外觀:
public class Criterion {
private String prop;
private String value;
// default constructor + getters + setters
}
最後,讓我們嘗試將GET請求發送到映射到處理程序方法get()
的URL。
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { RestTemplate.class, RestTemplateExceptionApplication.class })
public class RestTemplateExceptionLiveTest {
@Autowired
RestTemplate restTemplate;
@Test(expected = IllegalArgumentException.class)
public void givenGetUrl_whenJsonIsPassed_thenThrowException() {
String url = "http://localhost:8080/spring-rest/api/get?criterion={\"prop\":\"name\",\"value\":\"ASUS VivoBook\"}";
Product product = restTemplate.getForObject(url, Product.class);
}
}
確實,單元測試引發IllegalArgumentException
因為我們試圖將{“prop”:”name”,”value”:”ASUS VivoBook”}
作為URL的一部分{“prop”:”name”,”value”:”ASUS VivoBook”}
。
5.解決方案
根據經驗,我們應該始終使用POST請求發送JSON數據。
但是,儘管不建議這樣做,但使用GET的可能解決方案可能是定義一個包含我們標準的String
對象,並在URL中提供真實的URI變量。
@Test
public void givenGetUrl_whenJsonIsPassed_thenGetProduct() {
String criterion = "{\"prop\":\"name\",\"value\":\"ASUS VivoBook\"}";
String url = "http://localhost:8080/spring-rest/api/get?criterion={criterion}";
Product product = restTemplate.getForObject(url, Product.class, criterion);
assertEquals(product.getPrice(), 650, 0);
}
讓我們看看使用UriComponentsBuilder
類的另一個解決方案:
@Test
public void givenGetUrl_whenJsonIsPassed_thenReturnProduct() {
String criterion = "{\"prop\":\"name\",\"value\":\"Acer Aspire 5\"}";
String url = "http://localhost:8080/spring-rest/api/get";
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(url).queryParam("criterion", criterion);
Product product = restTemplate.getForObject(builder.build().toUri(), Product.class);
assertEquals(product.getId(), 1, 0);
}
如我們所見,在將其傳遞給getForObject
方法之前,我們使用UriComponentsBuilder
類構造了具有查詢參數criterion
URI .
六,結論
在這篇快速文章中,我們討論了導致RestTemplate
引發IllegalArgumentException: “
原因IllegalArgumentException: “
沒有足夠的變量可擴展”。
在此過程中,我們遍歷了一個實際示例,展示瞭如何產生異常並解決異常。