В своем последнем блоге я сказал, что собираюсь поговорить о Spring, Ajax и JSON, но не стал. Причина этого в том, что я хотел установить сцену, используя (едва ли) заслуживающий доверия сценарий веб-сайта для покупок. В этом случае, когда пользователь нажимает на ссылку на странице электронной коммерции, серверное приложение загружает некоторые элементы из каталога и отображает их на странице. Затем пользователь проверяет количество товаров и нажимает «Подтвердить покупку». Теперь, когда приходят Ajax и JSON, после нажатия «Подтвердить покупку» браузер отправляет Ajax-запрос на сервер, отправляя ему идентификаторы товара. Затем сервер извлекает элементы из базы данных и возвращает их как JSON в браузер. Затем браузер обрабатывает JSON, отображая элементы на экране.
Мой последний блог дошел до создания и отображения формы, представляющей список элементов
из воображаемого каталога для пользователя. Этот блог посвящен следующему этапу проекта: созданию JSON.
Последние два года ребята из Spring были заняты работой над Ajax и JSON, и, как и следовало ожидать, они выполняют большую часть работы за вас в фоновом режиме. Это означает, что все, что вам нужно сделать, это определить простой класс bean-компонента, который Spring может превратить в JSON, и написать некоторый код контроллера. В этом случае тот класс, который Spring преобразует в JSON, является классом OrderForm :
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
public class OrderForm { private final List<Item> items; private final String purchaseId; public OrderForm(List<Item> items, String purchaseId) { super(); this.items = items; this.purchaseId = purchaseId; } public List<Item> getItems() { return items; } public String getPurchaseId() { return purchaseId; } } |
Класс OrderForm содержит список объектов Item и уникальный идентификатор заказа, используемый для определения заказа.
Создав OrderForm , вам нужно разобраться с кодом контроллера Spring:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
public @ResponseBody OrderForm confirmPurchases(@ModelAttribute("userSelections") UserSelections userSelections) { logger.debug("Confirming purchases..."); OrderForm orderForm = createOrderForm(userSelections.getSelection()); return orderForm; } private OrderForm createOrderForm(List<String> selections) { List<Item> items = findItemsInCatalogue(selections); String purchaseId = getPurchaseId(); OrderForm orderForm = new OrderForm(items, purchaseId); return orderForm; } private List<Item> findItemsInCatalogue(List<String> selections) { List<Item> items = new ArrayList<Item>(); for (String selection : selections) { Item item = catalogue.findItem(Integer.valueOf(selection)); items.add(item); } return items; } private String getPurchaseId() { return UUID.randomUUID().toString(); } |
Приведенный выше код — это все, что требуется для возврата некоторого JSON в браузер, и вы можете видеть, что в этом нет ничего особенного. Во-первых, аннотация метода @RequestMapping , использующая значения confirm и RequestMethod.POST , отображает атрибуты формы из моего
предыдущий блог по этому методу.
|
1
|
<form:form modelAttribute="userSelections" action="confirm" method="post"> |
Аннотация modelAttribute указывает Spring создать и userSelections объект userSelections из опубликованных данных форм и userSelections аргумент userSelections метода confirmPurchases(...) . Класс UserSelections является UserSelections классом, который UserSelections список String . Несмотря на то, что это пример анти-паттерна Lazy Class, этот класс используется для легкой интеграции с <form:checkbox> Spring <form:checkbox> и в реальном приложении содержит больше атрибутов.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
public class UserSelections { private List<String> selection = Collections.emptyList(); public List<String> getSelection() { return selection; } public void setSelection(List<String> selection) { this.selection = selection; } @Override public String toString() { StringBuilder sb = new StringBuilder("Selections are: "); for (String str : selection) { sb.append(str); sb.append(", "); } return sb.toString(); } } |
Метод confirmPurchases(...) преобразует входной объект OrderForm выходной объект OrderForm который передается обратно в браузер как JSON. Объект OrderForm создается путем циклического просмотра списка идентификаторов Item содержащихся в объекте UserSelection и поиска соответствующих Item с помощью службы поддельного catalogue . Как только у него есть список Предметов, он создает уникальный идентификатор покупки, используя класс UUID Java. Затем он передает список Item и идентификатор OrderForm конструктору OrderForm , а затем форма заказа передается обратно в Spring. Не забудьте аннотацию @ResposeBody , которая сообщает Spring, чтобы привязать OrderForm к OrderForm HTTP-ответа, используя подходящий HttpMessageConverter . В этом и заключается магия. Как вы можете догадаться, тело ответа HTTP должно включать данные, которые имеют правильный тип мультимедиа для отправки через Интернет, и OrderForm определенно не подходит под этот счет. Чтобы решить эту проблему, кажется, что Spring рассматривает конфигурацию проекта для подходящих способов преобразования объекта OrderForm где он находит библиотеки OrderForm jackson-core и jackson-databind которые были добавлены в проект в последнем блоге .
|
01
02
03
04
05
06
07
08
09
10
|
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.0.4</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.0.4</version> </dependency> |
При отсутствии других подходящих кандидатов использует эти библиотеки для преобразования объекта OrderForm в JSON. Все это означает, что вам и мне на самом деле не нужно делать никакого реального кодирования для создания нашего вывода JSON. Довольно умно, да!
Очевидно, что все эти магические покерные игры, которые происходят в фоновом режиме, скрывают фактический вывод JSON, поэтому я считаю полезным создать простой модульный тест, подобный показанному ниже:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
@Test public void testDemonstrateJSON() throws JsonGenerationException, JsonMappingException, IOException { UserSelections userSelection = new UserSelections(); String[] selections = { "1", "2" }; userSelection.setSelection(Arrays.asList(selections)); Item item1 = Item.getInstance(1, "name", "description", new BigDecimal("1.00")); when(catalogue.findItem(1)).thenReturn(item1); Item item2 = Item.getInstance(2, "name2", "description2", new BigDecimal("2.00")); when(catalogue.findItem(2)).thenReturn(item2); OrderForm orderForm = instance.confirmPurchases(userSelection); ObjectMapper mapper = new ObjectMapper(); String result = mapper.writeValueAsString(orderForm); System.out.println(result); } |
Вы можете утверждать, что это не настоящий тест, поскольку он ничего не утверждает. Ценность этого теста состоит в том, чтобы дать визуальное представление выходных данных JSON и убедиться, что объект, который вы присоединяете к телу ответа HTTP, может быть преобразован в JSON анализатором Джексона. Если это невозможно, то при запуске этого теста вы получите исключение.
Итак, это код серверной части. Следующий и, надеюсь, последний блог в этой короткой серии статей посвящен клиентскому коду. Полный исходный код этого блога см. На GitHub — https://github.com/roghughe/captaindebug/tree/master/ajax-json.