В своем последнем блоге я сказал, что собираюсь поговорить о 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.