Рубрики
Без рубрики

Весеннее исключение RestTemplate: “Недостаточно переменных, доступных для расширения”

Автор оригинала: Abderrahim Azhrioun.

1. Обзор

В этом коротком учебнике, мы будем принимать близко взглянуть на весенний РестТемплет исключение НезаконныеАргументЭксцепция : Недостаточно переменных, доступных для расширения.

Во-первых, мы подробно обсудим основную причину этого исключения. Затем мы продемонстрируем, как его производить, и, наконец, как его решить.

2. Причина

Короче говоря, исключение обычно происходит когда мы пытаемся отправить данные JSON в запросе GET .

Проще говоря, РестТемплет обеспечивает getForObject способ получить представление, сделав запрос GET по указанному URL.

Основной причиной исключения является то, что РестТемплет рассматривает данные JSON, инкапсулированные в фигурных скобках, в качестве заполнителя переменных URI .

Поскольку мы не предоставляем никаких значений для ожидаемых переменных URI, getForObject метод бросает исключение.

Например, попытка отправки “Имя”:”HP EliteBook” как значение:

String url = "http://products.api.com/get?key=a123456789z&criterion={\"name\":\"HP EliteBook\"}";
Product product = restTemplate.getForObject(url, Product.class);

Будет просто причиной РестТемплет чтобы сделать исключение:

java.lang.IllegalArgumentException: Not enough variable values available to expand 'name'

3. Пример применения

Теперь давайте посмотрим пример того, как мы можем произвести этот НезаконныеАргументЭксцепция с помощью РестТемплет .

Чтобы все было просто, мы создадим базовый API REST для управления продуктом с одной конечной точкой GET.

Во-первых, давайте создадим наш модельный класс Продукт :

public class Product {

    private int id;
    private String name;
    private double price;

    // default constructor + all args constructor + getters + setters 
}

Далее мы определим пружинный контроллер, чтобы инкапсулировать логику нашего API REST:

@RestController
@RequestMapping("/api")
public class ProductApi {

    private List 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. Пример приложения Разъяснения

Основная идея метода обработчика получить () заключается в извлечении объекта продукта на основе конкретного критерия .

Критерий может быть представлен как строка JSON с двумя клавишами: реквизит и значение .

реквизит ключ относится к свойству продукта, так что это может быть идентификатор, имя или цена.

Как показано выше, критерий передается в качестве аргумента строки методу обработчика. Мы использовали ОбъектМаппер класса для преобразования наших Строка JSON к объекту Критерий .

Вот как наши Критерий класс выглядит:

public class Criterion {

    private String prop;
    private String value;

    // default constructor + getters + setters
}

Наконец, давайте попробуем отправить запрос 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);
    }
}

Действительно, единица испытания бросает НезаконныеАргументЭксцепция потому что мы пытаемся пройти “prop”:”имя”,”значение”:”ASUS VivoBook” – как часть URL.адреса.

5. Решение

Как правило, мы всегда должны использовать запрос POST для отправки данных JSON .

Однако, хотя это и не рекомендуется, возможным решением с использованием GET может быть определить Струнные объект, содержащий наш критерий и обеспечивающий реальную переменную URI в URL- .

@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. Заключение

В этой быстрой статье мы обсудили причины РестТемплет бросить НезаконноеАргументИсключение: “ Недостаточно переменных для расширения».

По пути мы прошли практический пример, показывающий, как произвести исключение и решить его.

Как всегда, полный исходный код примеров доступен более на GitHub .