Combinando Angular e Spring MVC

O leitor Bruno Leonardo pediu que eu escrevesse um post sobre como lidar com requisições ajax dentro dos controllers do Spring MVC. Talvez a maior dificuldade seja entender que, provavelmente, a única coisa que você vai precisar mudar nos métodos dos controllers é o retorno deles. Em vez de retornar uma página html completa, você tem a possibilidade de retornar os dados em outros formatos, como JSON.  Vou pegar um caso de uso real e mostrar um pouco do fluxo. Para completar, já que estamos falando de JavaScript, vou usar o angular como biblioteca :).

Em um dos projetos que estou programando atualmente, tive que possibilitar o login do usuário via ajax. O html ficou igual ao que segue abaixo.

  <form ng-submit="login()">
    <div>
     <input type="text" ng-model="usuario.email" />
    </div>
    <div>
     <input type="password" ng-model="usuario.senhaLimpa" />
    </div>

    <input type="submit" value="login">
  </form>

Como estou usando o angular, utilizei as diretivas do mesmo para indicar que quando o usuário decidir submeter o formulário, a função login() deve ser chamado. É justamente para isso que serve a diretiva ng-submit. Além disso, preciso pegar os valores que estão nos campos dos formulário e atualizar meu objeto JavaScript. Para esta parte, a gente pode usar a diretiva ng-model. A ideia aqui não é explicar em detalhes o angular, mas sim já fornecer um exemplo prático para que você possa estudá-lo depois :). Abaixo segue o código do meu controller do angular, para lidar com essa parte de login.

	angular.module('agendamento-online').controller('LoginController', function($scope,$http,$location,localStorageService,$routeParams) {	
				
		$scope.login = function(){
			var tentandoLogar = $http.post('/api/clientes/salao/login',$scope.usuario);
			
			tentandoLogar.success(function(retorno){
				lidaComRetornoDaApiUsuario(retorno);			
				$location.path("/agendamento-online/servicos.html");H
			});
			
			tentandoLogar.error(function(retorno,status){					
				console.log(retorno);
			});
		};
	}

Vamos dar um zoom aqui nas linhas, para ver o que cada uma está fazendo e também o que precisamos fazer no lado do servidor.

    var tentandoLogar = $http.post('/api/clientes/salao/login',$scope.usuario);

Aqui estamos tentando fazer um post para um endereço específico, passando como argumento o json contendo as informações de login. Agora sim, chegou o momento de suportarmos esse endereço no nosso servidor!

		@RequestMapping(method = RequestMethod.POST, value = "/api/clientes/salao/login", consumes = {
				MediaType.APPLICATION_JSON_VALUE}, produces = MediaType.APPLICATION_JSON_VALUE)
		public ResponseEntity<?> login(@Valid @RequestBody LoginClienteSalaoDTO form,
				BindingResult bindingResult) {
			if (bindingResult.hasErrors()) {
				return ResponseEntity.badRequest().body(
						ValidationErrorBuilder.fromBindingErrors(bindingResult));
			}

			Optional<ClienteSalao> cliente = loginClienteSalaoAction.execute(form);
			if (!cliente.isPresent()) {
				return ResponseEntity.notFound().build();
			}

			return ResponseEntity.ok(new RetornoSimples(cliente.get().getCodigoInterno()));
		}

Perceba que mapeamos ele com a annotation padrão, @RequestMapping. O detalhe que acrescentamos mais algumas informações. Informamos que o parâmetro do método vai vir via JSON, isso é feita através do header da requisição chamado content-type. O próprio angular já define isso para a gente, você pode definir a mesma coisa quando estiver usando jquery ou qualquer outra solução para fazer seu ajax. Também informamos, através do atributo produces que o retorno do método deve ser transformado em JSON e enviado para o cliente.

Só para deixar registrado, por default o seu método  recebe parâmetros com o content-type application/x-www-form-urlencoded, que é o utilizado pelos seus formulários nas páginas pela web. Um detalhe que você deve ficar atento em requisições do tipo post, que esperam, por exemplo, json como formato dos dados nos parâmetros, é que tais informações vem no próprio corpo da requisição. Por conta disso, você precisa anotar o seu parâmetro com @RequestBody. 

Um outro ponto interessante é que o retorno do método é um objeto do tipo ResponseEntity, justamente a abstração do Spring MVC que recebe algum objeto como argumento e transforma ele para o tipo especificado pelo header accept-type, também enviado pela aplicação cliente. Os tipos de retornos suportados são justamente os especificados no atributo producesPerceba que inclusive os erros de validação devem ser retornados como um objeto a ser serializado.

Para fechar, um ponto muito importante é a correta utilização dos status http. Perceba que nessa lógica podem ser gerados três tipos de status:

  1. Bad Request(400) -> Indica que a requisição veio com dados inválidos
  2. Not Found(404) -> Como o próprio nome diz, quer dizer que algo não foi encontrado
  3. ok(200) -> Quer dizer que deu tudo certo

O uso correto dos status influencia diretamente no uso da sua api javascript. Vamos dar uma olhada no trecho de código abaixo.

	var tentandoLogar = $http.post('/api/clientes/salao/login',$scope.usuario);

	tentandoLogar.success(function(retorno){
		lidaComRetornoDaApiUsuario(retorno);
		
		$location.path("/agendamento-online/servicos.html");H
	});

	tentandoLogar.error(function(retorno,status){					
		console.log(retorno);
	});

Perceba que a função post retorna um objeto(promise) que possui alguns outras funções. No exemplo acima, registramos dois callbacks: success e error. A função de erro só é chamada caso um status acima de 400 seja retornado. Isso é super importante! Já vi api’s que sempre retornam 200, mesmo quando um erro de validação acontece :(. Nessa situação, você tem que ficar acrescentando ifs no callback que lida com retornos de sucesso, poluindo o código e afetando na manutenção.

Mais um lembrete aqui. Para seus objetos poderem pegar os objetos e transformar os mesmos para JSON, XML ou qualquer outro formato, você vai precisar de uma biblioteca. Como JSON e XML são os formatos mais comuns, você pode usar o Jackson, que o Spring já possui integração.

Lembre que para ajudar nas configurações você sempre pode usar o Spring Boot, que facilita demais todo o setup do seu projeto. Aproveitando o último parágrafo, caso você tenha interesse no angular, tenta dar uma olhada no curso do Alura ou no curso presencial da Caelum.

10 thoughts on “Combinando Angular e Spring MVC

  1. Alberto Valeu pelo post, era uma dúvida minha também. Mas, apesar do seu blog ser sobre Spring, você saber dizer se há uma forma simples de fazer a mesma coisa(Angular req. ajax) com Java EE?

    Like

    • Oi Pedro,

      Obrigado pela sugestão. Vamos ver a possibilidade :). De qualquer jeito, posso produzir mais posts aqui no blog, ou no próprio blog do alura :). Quem sabe até um hangout, dado que com video fica mais simples.

      Like

  2. Olá Alberto,

    Obrigado pelo post sobre Angular + Spring Boot, com base na sua explicação eu iniciei um projeto de autorização de acesso usando Angular + NodeJS para o front-end chamado acessoFront e Spring Boot como back-end chamado de acessoBack, os dois estão separados em programas distintos e se comunicam através de chamadas REST, porém gostaria de saber como posso fazer para que esses serviços REST só possam ser acessados por usuários autenticados pela aplicação acessofront ?

    Like

    • Esqueci de mencionar que ao tentar acessar o serviço através de outro domino, eu recebo erros de CORS ou Não authorizado, mesmo passado login e senha do usuário.

      Like

    • Você vai precisar ter um endpoint de autenticação.. aí pesquisa sobre jwt, que é uma forma de encriptar os dados do usuário em um token e ficar trafegando ele em todo request.

      Like

Leave a comment