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: “
没有足够的变量可扩展”。
在此过程中,我们遍历了一个实际示例,展示了如何产生异常并解决异常。
0 评论