Spring RestTemplate异常:“Not enough variables available to expand”

2023/05/19

1. 概述

在这个简短的教程中,我们将仔细研究Spring的RestTemplate异常IllegalArgumentException:Not enough variables available to expand。

首先,我们将详细讨论此异常背后的主要原因。然后,我们将展示如何生成它,最后是如何解决它。

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. 示例应用

现在,让我们看看如何使用RestTemplate生成此IllegalArgumentException的示例。

为了简单起见,我们将创建一个基本的REST API,用于使用单个GET端点进行产品管理。

首先,让我们创建我们的模型类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 key指的是产品属性,因此它可以是id、名称或价格。

如上所示,标准作为字符串参数传递给处理程序方法。我们使用ObjectMapper类将我们的JSON字符串转换为Criterion的对象。

这是我们的Criterion类的样子:

public class Criterion {

    private String prop;
    private String value;

    // default constructor + getters + setters
}

最后,让我们尝试向映射到处理程序方法get()的URL发送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);
    }
}

实际上,单元测试会抛出IllegalArgumentException,因为我们试图将{“prop”: “name”, “value”: “ASUS VivoBook”}作为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);
}

如我们所见,在将URI传递给getForObject方法之前,我们使用UriComponentsBuilder类通过查询参数标准构造我们的URI。

6. 总结

在这篇快速文章中,我们讨论了导致RestTemplate抛出IllegalArgumentException的原因:“Not enough variables available to expand”。

在此过程中,我们通过一个实际示例展示了如何产生异常并解决它。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章