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()の基本的な考え方は、特定の基準に基づいて製品オブジェクトを取得することです。

基準は、propvalueの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メソッドに渡しました。[X186X ]

6. 結論

この簡単な記事では、 RestTemplateIllegalArgumentExceptionをスローする原因について説明しました:「展開するのに十分な変数がありません」。

その過程で、例外を生成して解決する方法を示す実際的な例を見ていきました。

いつものように、例の完全なソースコードは、GitHubから入手できます。