1. 개요

이 짧은 튜토리얼에서는 Spring의 RestTemplate 예외를 자세히 살펴볼 것입니다. IllegalArgumentException : 확장 할 수있는 변수가 충분하지 않습니다.

먼저이 예외의 주요 원인에 대해 자세히 설명합니다. 그런 다음 생산 방법과 마지막으로 해결 방법을 보여줄 것입니다.

2. 원인

간단히 말해 예외는 일반적으로 GET 요청에서 JSON 데이터를 보내려고 할 때 발생합니다 .

간단히 말해서 RestTemplate지정된 URL에 대해 GET 요청을 만들어 표현을 가져 오는 getForObject 메서드를 제공합니다 .

예외의 주요 원인은 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 
}

다음으로 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 () 의 기본 아이디어 는 특정 기준에 따라 제품 객체를 검색하는 것 입니다.

기준은 두 개의 키 ( propvalue)가 있는 JSON 문자열로 표시 될 수 있습니다 .

소품 그것이 아이디, 이름, 또는 가격이 될 수 있도록 키는 제품 속성을 의미한다.

위에 표시된대로 기준은 핸들러 메소드에 문자열 인수로 전달됩니다. 우리는 사용 ObjectMapper의 우리의 변환 클래스를 객체에 JSON 문자열기준 .

우리의 방법이다 기준의 클래스 본다 :

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. 결론

이 빠른 기사에서는 RestTemplateIllegalArgumentException발생시키는 원인에 대해 설명했습니다 . " 확장 할 수있는 변수가 충분하지 않습니다."

그 과정에서 예외를 생성하고 해결하는 방법을 보여주는 실제 예제를 살펴 보았습니다.

항상 그렇듯이 예제의 전체 소스 코드는 GitHub에서 사용할 수 있습니다 .