SpringRestTemplateの例外:「拡張するのに十分な変数がありません」
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. アプリケーション例
次に、RestTemplateを使用してこのIllegalArgumentExceptionを生成する方法の例を見てみましょう。
簡単にするために、単一のGETエンドポイントを使用した製品管理用の基本的な RESTAPIを作成します。
まず、モデルクラスProductを作成しましょう。
public class Product {
private int id;
private String name;
private double price;
// default constructor + all args constructor + getters + setters
}
次に、RESTAPIのロジックをカプセル化するスプリングコントローラーを定義します。
@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()の基本的な考え方は、特定の基準に基づいて製品オブジェクトを取得することです。
基準は、propとvalueの2つのキーを持つJSON文字列として表すことができます。
prop キーは製品プロパティを参照するため、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);
}
}
実際、URLの一部として {“ prop”:” name”、” value”:” ASUS VivoBook”} を渡そうとしているため、単体テストはIllegalArgumentExceptionをスローします。
5. ソリューション
経験則として、JSONデータを送信するには常にPOSTリクエストを使用する必要があります。
ただし、推奨されていませんが、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を構築してから、getForObjectメソッド
6. 結論
この簡単な記事では、 RestTemplateがIllegalArgumentExceptionをスローする原因について説明しました:「展開するのに十分な変数がありません」。
その過程で、例外を生成して解決する方法を示す実際的な例を見ていきました。
いつものように、例の完全なソースコードは、GitHubでから入手できます。