Автор оригинала: 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 ListproductList = 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 .