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