知識庫 / HTTP Client-Side RSS 訂閱

Spring RestTemplate 異常:“缺少變量可用以展開”

HTTP Client-Side,Spring Web
HongKong
5
12:42 PM · Dec 06 ,2025

1. 概述

本教程將深入探討 Spring 的 RestTemplate 異常 IllegalArgumentException:變量不足以展開。

首先,我們將詳細討論導致此異常的主要原因。然後,我們將演示如何產生此異常,最後,我們將展示如何解決它。

2. 根本原因

簡而言之,異常通常發生在嘗試在 GET 請求中發送 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. 示例應用程序

現在,讓我們來看一個如何使用 <em >IllegalArgumentException</em > 的示例,使用 `RestTemplate>。

為了簡化操作,我們將創建一個基本的 REST API,用於產品管理,該 API 包含一個 GET 端點。

首先,讓我們創建一個模型類 <em >Product</em >>:

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 字符串:propvalue

prop 鍵引用產品屬性,可以是 ID、名稱或價格。

如上所示,標準作為字符串參數傳遞給處理方法。我們使用 ObjectMapper 類將我們的 JSON 字符串轉換為 Criterion 類的對象。

以下是 Criterion 類的外觀:

public class Criterion {

    private String prop;
    private String value;

    // default constructor + getters + setters
}

最後,讓我們嘗試向映射到處理方法的 URL 發送 GET 請求,即 get() 方法。

@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);
    }
}

確實,單元測試拋出了 <em >IllegalArgumentException</em >,因為我們試圖將 <em >{"prop":"name","value":"ASUS VivoBook"}</em > 作為 URL 的一部分傳遞。

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);
}

如我們所見,我們使用 UriComponentsBuilder 類構建我們的 URI,在其中添加了 query 參數 criterion,然後將其傳遞給 getForObject 方法。

6. 結論

在本文中,我們探討了導致 RestTemplate 拋出 IllegalArgumentException: ““缺少可用的變量進行展開” 這一問題的原因。

在解決過程中,我們通過一個實際示例演示瞭如何產生該異常並解決它。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.