Back-End

12 set, 2013

Spring MVC, Ajax e JSON – Parte 02: o código do lado do servidor

Publicidade

No meu último artigo, eu disse que falaria sobre o Spring, Ajax e JSON, mas não falei. A razão para isso é que eu quis definir o cenário usando uma loja online (minimamente) crível. Nesse cenário, quando o usuário clica no link da página de ecommerce, o servidor de aplicativos carrega alguns itens do catálogo e os exibe na página. O usuário escolhe então um número de itens e pressiona “Confirmar a Compra”. Agora, essa é a parte onde entram o Ajax e o JSON; ao pressionar “Confirmar a Compra”, o navegador faz uma requisição Ajax ao servidor enviando a ele os IDs dos itens. O servidor então recupera os itens na base de dados e os retorna como JSON para o navegador. O navegador então processa o JSON, exibindo os itens na tela.

Meu último artigo foi até o momento da criação e da exibição de um formulário que apresenta ao usuário uma lista de itens a partir de um catálogo imaginário. Este artigo mostra o próximo passo nesse projeto: criar o JSON.

Os caras no Spring estiveram ocupados trabalhando no Ajax e no JSON nos últimos anos e, como você poderia esperar, eles fazem boa parte do trabalho para você no segundo plano. Isso significa que tudo que você precisa fazer é definir uma classe bean simples para que o Spring possa criar o JSON e escrever algum código de controller. Nesse caso, a classe que o Spring converterá em JSON é a classe OrderFrom:

[java]

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;
}
}

[/java]

A classe OrderForm contém uma lista dos objetos em Item e um ID de pedido único, usado para definir a ordem.

Tendo criado o OrderForm, a próxima coisa a se fazer é ordenar o código de controle do Spring:

[java]

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();
}

[/java]

O código acima é tudo que é necessário para retornar algum JSON ao navegador, e você pode ver que não há muito a se fazer. Primeiramente, a anotação @RequestMapping do método usa confirm e RequestMethod. Valores do POST mapeiam os atributos do formulário do artigo anterior para esse método.

[xml]

<form:form modelAttribute="userSelections" action="confirm" method="post">

[/xml]

A anotação modelAttribute diz ao Spring para criar e mapear um objeto userSelections dos dados dos formulários postados e injetá-los no argumento userSelections do método confirmPurchase(…). A classe UserSelections é uma classe conveniente que envolve uma lista de Strings. Apesar de ser um exemplo do anti-pattern Lazy Class, essa classe é usada para se integrar sem esforço à tag <form:checkbox> do Spring, e um aplicativo do mundo real teria muito mais atributos.

[java]

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();
}
}

[/java]

O método confirmPurchases(…) converte um objeto de entrada UserSelections em um objeto de saída OrderForm que é passado de volta para o navegador como um JSON. O objeto OrderForm é criado ao efetuar um loop pela lista de IDs de Item no objeto UserSelection e procurar por um Item correspondente ao utilizar o serviço falso catalogue. Uma vez que ele possuir a lista de Items, ele cria então um ID de compra único fazendo uso da classe UUID do Java. A lista de Items e o ID de compra são então passados ao construtor do OrderForm, e o formulário de pedido retorna ao Spring. Não se esqueça da anotação @ResponseBody, que diz ao Spring para unir o OrderForm ao body da resposta HTTP utilizando um HttpMessageConverter apropriado. Aí é onde a mágica acontece. Como muitos podem imaginar, o body da resposta HTTP precisa incluir os dados pertencentes ao tipo de mídia correta para enviá-los pela Internet, e o OrderForm definitivamente não se encaixa nessa necessidade. Para corrigir o problema, parece que o Spring visualiza o config do projeto para encontrar formas adequadas de converter o objeto OrderForm onde ele encontrar as bibliotecas jackson-core e jackson-databin que foram acrescentadas ao projeto no artigo anterior.

[xml]

<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>

[/xml]

Na falta de outros candidatos apropriados, as bibliotecas são usadas para converter o objeto OrderForm para JSON. Tudo isso significa que você na verdade não tem que escrever nenhum código para produzir um JSON de saída. Portanto, acho útil criar um simples teste unitário similar ao mostrado abaixo:

[java]

@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);
}

[/java]

Você talvez argumente que isso não é um teste real, uma vez que ele não declara nada. O valor deste teste é dar uma representação visual do JSON de saída e certificar-se de que você está anexando o objeto ao body da resposta HTTP que pode ser convertido em JSON pelo parser Jackson. Se não puder, você pode executar esse teste e visualizar a exceção.

Portanto, cobrimos o lado do servidor. No próximo (e esperamos, útimo) artigo desta curta série, veremos o código do lado do cliente.

Para o código fonte completo deste artigo, veja o Github – https://github.com/roghughe/captaindebug/tree/master/ajax-json

***

Artigo traduzido pela Redação iMasters, com autorização do autor. Publicado originalmente em http://www.captaindebug.com/2013/05/spring-mvc-ajax-and-json-part-2-server.html#.UdR8YPlwrnh