Neste codelab você aprenderá a desenvolver um aplicativo Java Web utilizando o framework Spring Boot com Thymeleaf e acesso a dados com JdbcTemplate (SQL DAO - repository), com controle de acesso (autenticação e autorização com Spring Security) que gerencia informações em um banco de dados com operações CRUD padrão: Criar, Recuperar, Atualizar e Excluir. O sistema será hospedado no servidor Heroku.
Observação: a instalação destas ferramentas está fora do escopo deste documento.
A aplicação consiste de uma página web para exibir produtos cadastrados previamente. Teremos vários papéis que os usuários poderão desempenhar na aplicação. Vamos ter os seguintes papeis na aplicação:
Papel | Autorização |
USUÁRIO | permissão para visualizar todos os produtos |
CRIADOR | permissão para criar novos produtos |
EDITOR | permissão para editar produtos |
ADMIN | todas as permissões |
O diagrama de entidades e relacionamentos da aplicação:
A tabela de usuários armazena credenciais e a tabela de papeis armazena autorizações (direitos). O relacionamento de entidade entre usuários e papeis é muitos para muitos porque um usuário pode ter uma ou mais papeis e um papel pode ser atribuído a um ou mais usuários. É por isso que precisamos ter a tabela intermediária usuarios_papeis para representar essa associação muitos para muitos.
Script de criação do banco de dados:
CREATE TABLE IF NOT EXISTS `papeis` ( `papel_id` INT(11) NOT NULL AUTO_INCREMENT, `nome` VARCHAR(45) NOT NULL, PRIMARY KEY (`papel_id`)) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8; CREATE TABLE IF NOT EXISTS `produto` ( `produto_id` INT(11) NOT NULL AUTO_INCREMENT, `nome` VARCHAR(45) NOT NULL, `descricao` VARCHAR(45) NOT NULL, `preco` FLOAT NOT NULL, PRIMARY KEY (`produto_id`)) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8; CREATE TABLE IF NOT EXISTS `usuarios` ( `usuario_id` INT(11) NOT NULL AUTO_INCREMENT, `email` VARCHAR(45) NOT NULL, `senha` VARCHAR(64) NOT NULL, PRIMARY KEY (`usuario_id`)) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8; CREATE TABLE IF NOT EXISTS `usuarios_papeis` ( `usuario_id` INT(11) NOT NULL, `papel_id` INT(11) NOT NULL, CONSTRAINT `papel_fk` FOREIGN KEY (`papel_id`) REFERENCES `papeis` (`papel_id`), CONSTRAINT `user_fk` FOREIGN KEY (`usuario_id`) REFERENCES `autentica-autoriza`.`usuarios` (`usuario_id`)) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8; -- Script de inserção de dados INSERT INTO `papeis` (`nome`) VALUES ('USUARIO'); INSERT INTO `papeis` (`nome`) VALUES ('CRIADOR'); INSERT INTO `papeis` (`nome`) VALUES ('EDITOR'); INSERT INTO `papeis` (`nome`) VALUES ('ADMIN'); INSERT INTO `usuarios` (`email`, `senha`) VALUES ('mercurio@teste.com', '$2a$10$wSa39/yk/UTovsqPt817X.c0I8xlS2s76YQy4ViDxag0mlxUoYUq2'); INSERT INTO `usuarios` (`email`, `senha`) VALUES ('venus@teste.com', '$2a$10$v8Wr0mf6HgmIG0ANimKJOuOIt/09qIkXIF7wCwzq8.U/LTqTs9ovq'); INSERT INTO `usuarios` (`email`, `senha`) VALUES ('terra@teste.com', '$2a$10$SXuWtufjZVvmMFpc56HMEObBhX/vov8UJxUBeZX3RoEnOIR0yqdH6'); INSERT INTO `usuarios` (`email`, `senha`) VALUES ('marte@teste.com', '$2a$10$Wl1gojjJgFhXztvHIULT3e0hiEMrDbCWCys0p6LnfrqxcxYkgh9OW'); INSERT INTO `usuarios` (`email`, `senha`) VALUES ('jupiter@teste.com', '$2a$10$5sci59bfdcED4XxxuN9gx.SJBPsdNknirJSkLbTCouf2mFzLmX/Gi'); INSERT INTO `usuarios_papeis` (`usuario_id`, `papel_id`) VALUES (1, 1); -- usuário mercurio tem papel USUARIO INSERT INTO `usuarios_papeis` (`usuario_id`, `papel_id`) VALUES (2, 2); -- usuário venus tem papel CRIADOR INSERT INTO `usuarios_papeis` (`usuario_id`, `papel_id`) VALUES (3, 3); -- usuário terra tem papel EDITOR INSERT INTO `usuarios_papeis` (`usuario_id`, `papel_id`) VALUES (4, 2); -- usuário marte tem papel CRIADOR INSERT INTO `usuarios_papeis` (`usuario_id`, `papel_id`) VALUES (4, 3); -- usuário marte tem papel EDITOR INSERT INTO `usuarios_papeis` (`usuario_id`, `papel_id`) VALUES (5, 4); -- usuário jupiter tem papel ADMIN
Vamos usar o Maven para gerenciar as dependências do projeto e facilitar o processo de construção da aplicação. Maven usa um arquivo XML chamado pom.xml. Um modelo de objeto de projeto ou POM é a unidade fundamental de trabalho no Maven. É um arquivo XML que contém informações sobre o projeto e detalhes de configuração usados pelo Maven para construir o projeto. O uso do framework Spring Boot permite o desenvolvimento rápido de aplicações.
Neste projeto vamos usar as dependências
Web
- aplicação web mvc que simplifica a codificação da camada do controlador. Thymeleaf
- template engine para criação das visualizações do modelo mvc que simplifica a codificação da camada de visualização.JDBC
- acesso ao banco de dados MySQL
- conector do banco de dados mysqlSecurity
- implementação da autenticação e autorizaçãoValidation
- validação de dados recebidos de formuláriosDevtools
- ferramentas de desenvolvimentoO arquivo pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.1</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>br.edu.iftm</groupId> <artifactId>autentica-autoriza</artifactId> <version>0.0.1-SNAPSHOT</version> <name>autentica-autoriza</name> <description>Demo project for Spring Boot</description> <properties> <java.version>8</java.version> </properties> <dependencies> <!-- dependência para usar JdbcTemplate --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!-- dependência para usar engine template Thymeleaf --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!-- dependencia app web mvc --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- dependências de segurança--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity5</artifactId> </dependency> <!-- dependência conector banco de dados mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- dependências de testes --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> <!-- dependências ferramentas desenvolvedor --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <!-- Validação --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <!-- configuração para upload automatico de resources. Executar o app com maven:spring-boot:run --> <configuration> <addResources>true</addResources> </configuration> </plugin> </plugins> </build> </project>
O arquivo src\main\resources\application.properties contém informações para o framework Spring.
A configuração #1 estabelece as informações para conexão ao banco de dados. A configuração #2 permite a visualização do log do servidor no console. A configuração #3 permite a criação automática das tabelas do banco de dados e a configuração #4 habilita a página de erro que deve ser exibida quando alguma exceção acontecer.
#1 configuração da fonte de dados spring.datasource.url=jdbc:mysql://localhost:3306/autentica-autoriza spring.datasource.username=root spring.datasource.password= #2 configuração de log da aplicação. logging.level.root=INFO logging.level.org.springframework.jdbc.core.JdbcTemplate=TRACE logging.level.web=TRACE #3 O banco de dados deve ser criado antes de executar o app. # O nome do banco de dados deverá ser autentica-autoriza. O arquivo com o script de criação das tabelas deve # ter o seguinte nome e localização src\main\resources\schema.sql spring.sql.init.mode=always #4 página de erro src\main\resources\templates\error.html server.error.whitelabel.enabled=false server.error.include-stacktrace=always server.error.include-exception=true server.error.include-message=always server.error.include-binding-errors=always
O site tem a seguinte estrutura
A tela foi dividida em três partes
No projeto teremos um layout padrão que será usado por todas as páginas da aplicação. Assim na estrutura de pastas vamos criar dois arquivos de templates: um para conter os fragmentos e outro para conter o layout.
O arquivo fragmentos.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5"> <div th:fragment="navbar" class="navbar navbar-dark navbar-expand-md bg-dark mb-1"> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon "></span> </button> <div class="container d-flex"> <div class="collapse navbar-collapse col-6" id="navbarSupportedContent"> <ul class="navbar-nav"> <li class="nav-item"> <a class="nav-link text-white m-1 material-icons" href="/" title="Início">home</a> </li> <li class="nav-item dropdown"> <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> Usuários </a> <div class="dropdown-menu" aria-labelledby="navbarDropdown"> <a class="dropdown-item" href="/novo-usuario">Novo</a> <a class="dropdown-item" href="/usuarios">Todos</a> </div> </li> <li class="nav-item dropdown"> <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> Produtos </a> <div class="dropdown-menu" aria-labelledby="navbarDropdown"> <a class="dropdown-item" href="/novo-produto">Novo</a> <a class="dropdown-item" href="/produtos">Todos</a> </div> </li> </ul> </div> <h5 class="text-white col-4 mb-0">Projeto Sistema Web MVC SQL</h5> <div class="d-flex col-2" sec:authorize="isAuthenticated()"> <span class="text-white small" sec:authentication="name"></span> </div> <div> <form th:action="@{/logout}" method="post" class="ml-4" sec:authorize="isAuthenticated()"> <input class="btn btn-secondary btn-sm p-1 small" type="submit" value="Sair" /> </form> </div> </div> </div> <div th:fragment="rodape"> <button type="button" class="btn btn-dark" data-bs-toggle="modal" data-bs-target="#sobre"> Quem somos </button> <!-- Modal --> <div class="modal fade" id="sobre" tabindex="-1" role="dialog"> <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="exampleModalLongTitle">Projeto Sistema Web MVC e SQL</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> <p>No segundo período o estudante desenvolverá um sistema web monolítico utilizando template engine MVC e acesso a dados baseado em comandos SQL. A concepção deste projeto foi para que o estudante tenha a oportunidade tanto de utilizar uma tecnologia de template engine, bem como aplicar na prática o acesso a dados de uma forma mais manual, via comandos SQL, sem ainda recorrer a ferramentas modernas de mapeamento objeto-relacional.</p> <p>Tecnologias utilizadas neste sistema:</p> <ul> <li>Spring boot</li> <li>Maven</li> <li>MySQL</li> <li>GitHub</li> <li>Javascript</li> <li>JQuery</li> <li>MVC</li> <li>Bootstrap</li> <li>Spring Security</li> <li>Spring Data Access</li> <li>Heroku Server</li> </ul> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Fechar</button> </div> </div> </div> </div> </div> </html>
O arquivo layout.html
<html xmlns:th="http://www.thymeleaf.org" th:fragment="layout"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!-- Bootstrap CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous"> <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"> <title>Escola 5.3</title> </head> <body class="d-flex flex-column min-vh-100"> <!-- Início do conteúdo da página --> <nav th:insert="fragmentos/fragmentos :: navbar"></nav> <main th:insert="this :: conteudo" class="ml-3"></main> <footer th:insert="fragmentos/fragmentos :: rodape" class="mt-auto mx-auto my-3"></footer> <!-- Fim do conteúdo da página --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script> </body> </html>
Toda página nova deverá ter o seguinte código inicial
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" th:replace="fragmentos/layout"> <div th:fragment="conteudo"> <!-- insira seu código aqui --> </div> </html>
O seu código deverá ser inserido depois do comentário
Referências:
O modelo é uma classe Java que representa uma entidade do banco de dados. Chaves estrangeiras no banco de dados são representadas no modelo por um atributo da classe do tipo da tabela referenciada.
O repositório é uma classe Java que realiza as transações com o banco de dados fazendo a "ponte" entre os mundos dos objetos e o mundo das tabelas. Aqui são implementados métodos para efetivar as operações CRUD. Estas classes devem ser anotadas com @Repository.
Nestas classes encontramos os métodos que implementam a lógica MVC para atender às solicitações do usuário do sistema. Estas classes são anotadas com @Controller
e seus métodos podem ser anotados com @GetMapping
ou @PostMapping
, dependendo do tipo de requisição HTTP vinda do front-end. A interação do usuário com o sistema se dá através de links ou botões. Cada link ou botão de formulário define uma URL com uma parte (chamada path, veja figura abaixo) que é utilizada para mapear qual método atenderá a solicitação do usuário.
fonte: https://pzwiki.wdka.nl/mediadesign/Universal_Resource_Locator
Referências
Os templates são arquivos HTML que utilização atributos Thymeleaf e representam a letra V do MVC.
Referências:
Vamos usar a autenticação de nome de usuário / senha, que utiliza um formulário HTML para obter do usuário um nome e uma senha para autenticação.
Quando a página de login é especificada na configuração do Spring Security, você é responsável por renderizar a página.
Existem alguns pontos-chave sobre o formulário HTML padrão:
th:action="@{/login}"
username
password
error
for encontrado (th:if="${param.error}"
), isso indica que o usuário não forneceu um nome de usuário / senha válidoslogout
for encontrado (th:if="${param.logout}"
), indica que o usuário foi desconectado com sucessoPrimeiro vamos adicionar uma nova pasta à nossa estrutura do projeto
package br.edu.iftm.autenticaautoriza.security; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @Configuration @EnableWebSecurity public class WebConfigSecurity extends WebSecurityConfigurerAdapter { @Bean public UserDetailsService userDetailsService() { return new MyUserDetailsService(); } @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public DaoAuthenticationProvider authenticationProvider() { DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); authProvider.setUserDetailsService(userDetailsService()); authProvider.setPasswordEncoder(passwordEncoder()); return authProvider; } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(authenticationProvider()); } @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/usuarios").authenticated() .anyRequest().permitAll() .and() .formLogin() .loginPage("/login") .defaultSuccessUrl("/") .permitAll() .and() .logout() .logoutSuccessUrl("/") .permitAll(); } }
package br.edu.iftm.autenticaautoriza.security; import java.util.Collection; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import br.edu.iftm.autenticaautoriza.model.Usuario; public class MyUserDetails implements UserDetails { private Usuario usuario; public MyUserDetails(Usuario usuario) { this.usuario = usuario; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return null; } @Override public String getPassword() { return usuario.getSenha(); } @Override public String getUsername() { return usuario.getEmail(); } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }
package br.edu.iftm.autenticaautoriza.security; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import br.edu.iftm.autenticaautoriza.model.Usuario; import br.edu.iftm.autenticaautoriza.repository.UsuarioRepository; public class MyUserDetailsService implements UserDetailsService { @Autowired private UsuarioRepository usuarioRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { Usuario usuario = usuarioRepository.buscaPorEmail(username); if (usuario == null) { throw new UsernameNotFoundException("User not found"); } return new MyUserDetails(usuario); } }
@GetMapping("/login") public String login() { return "login"; }
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" th:replace="fragmentos/layout"> <div th:fragment="conteudo"> <!-- insira seu código aqui --> <div class="container col-6"> <h1>Autenticação (login)</h1> <div th:if="${param.error}"> Usuario/senha inválidos</div> <div th:if="${param.logout}"> Você foi logged out</div> <form th:action="@{/login}" method="post"> <div class="form-group"> <input class="form-control" type="text" name="username" placeholder="E-mail" /> </div> <div class="form-group"> <input class="form-control" type="password" name="password" placeholder="Senha" /> </div> <input class="btn btn-primary mt-3" type="submit" value="Log in" /> </form> </div> </div> </html>
<div class="d-flex col-2" sec:authorize="isAuthenticated()"> <span class="text-white small" sec:authentication="name"></span> </div> <div> <form th:action="@{/logout}" method="post" class="ml-4" sec:authorize="isAuthenticated()"> <input class="btn btn-secondary btn-sm p-1 small" type="submit" value="Sair" /> </form> </div>
Uma característica comum em todos programas de computador é a necessidade de tratar exceções. Em Java as exceções são tratadas por um mecanismo de lançamento de exceções quando o erro é identificado. Spring Boot oferece diversas maneiras de tratar exceções. Uma forma simples e poderosa que usaremos aqui neste tutorial é a anotação @ControllerAdvice
.
Esta anotação facilita nossa vida para lidar com todos os tipos de exceções em um local central em nossa aplicação. A facilidade está em não precisar capturar exceções em cada método ou classe separadamente, bastando apenas lançar a exceção no método e, em seguida, ela será capturada na classe central de tratamento de exceções anotada por @ControllerAdvice
.
Divisão por zero - java.lang.ArithmeticException: / by zero
Depois de executar o programa acima teremos o seguinte:
Como tratar este erro? Vamos modificar o código anterior para exemplificar
Bloco try/catch não deixa o programa parar, tratando o erro no local onde aconteceu. Se as exceções capturadas forem de mesma hierarquia, as classes mais especializadas devem aparecer primeiro. Para capturar qualquer exceção use a classe Exception (não indicado):
catch (
Exception
e) { ... }
Podemos também delegar o tratamento do erro para outro método na pilha de execução, para isso podemos lançar uma exceção .
A classe que será criada para fazer o papel de central de tratamento de exceções deverá ser anotada com a anotação @ControllerAdvice
. Nesta classe vamos usa a seguinte anotação em seus métodos:
@ExceptionHandler
: você deverá fornecer as exceções tratadas pelo métodoEsta anotação vai funcionar como um filtro para capturar uma exceção específica, possibilitando o tratamento do erro.
Classe @ControllerAdvice
package br.edu.iftm.autenticaautoriza.controller; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.mvc.support.RedirectAttributes; @ControllerAdvice public class MyControllerAdvice { @ExceptionHandler(java.sql.SQLIntegrityConstraintViolationException.class) public String loginNameDuplicado(java.sql.SQLIntegrityConstraintViolationException ex, final RedirectAttributes redirectAttributes) { String errorMessage = ex.getMessage(); if (errorMessage.contains("Duplicate entry")) { redirectAttributes.addFlashAttribute("mensagem", "E-mail já cadastrado. Tente cadastrar outro e-mail"); return "redirect:/novo-usuario"; } return "error"; } }
Esta classe deve estar no mesmo pacote das classes controladoras (pacote controller)
.
Referência:
A validação de dados obtidos através de formulários HTML deve ser realizada para garantir a integridade dos dados em aplicações web.
É necessário garantir que intervalos de valores são respeitados ou que formatos são seguidos. Por exemplo, queremos ter certeza de que o usuário não tem -345 anos ou que seu endereço de e-mail é válido.
Existem muitas maneiras de validar os dados do formulário - e o método que você usa depende do seu aplicativo. Em geral, você desejará realizar validação do lado do cliente, bem como validação do lado do servidor. A validação do lado do cliente garante que os dados não filtrados nem cheguem ao back-end, enquanto a validação do lado do servidor garante que os dados errados não sejam processados posteriormente.
Começamos com os modelos de domínio, com a aplicação de anotações nos campos das classes que queremos validar.
No canto superior direito da tela principal, clique em New e escolha Create new app.
Depois de preencher os campos App name e Choose a region, clique no botão Create App
Escolha o deployment method GitHub, que permitirá a automação dos próximos deployments quando o repositório GitHub for atualizado.
Você deve indicar o endereço do repositório GitHub do projeto, veja figura abaixo. Informe o nome do repositório e clique em Search
Após informar o repositório GitHub, Heroku informará o estabelecimento da conexão conforme figura abaixo:
Na mesma tela clique no botão Enable Automatic Deploys (veja figura abaixo)
Você pode verificar a situação de sua app na aba Overview:
Você já pode acessar a app usando o botão Open app no canto superior direito da página:
Você verá a sua app sendo executada na Web no endereço: :
No seu DashBoard , vá em Resources e no campo Add-ons digite mysql e adicione o recurso ClearDB Mysql:
Com o seu projeto aberto no vscode, vá em MAVEN/nome do projeto/Lifecycle e escolha package
Quando terminar de empacotar o projeto em um arquivo JAR, você verá no terminal:
Na pasta \target você verá o arquivo JAR gerado
Copie o nome do arquivo gerado (neste exemplo foi demo-0.0.1-SNAPSHOT.jar).
Após a criação do pacote JAR você deve criar um arquivo chamado Procfile na raiz do seu projeto, veja figura abaixo:
Neste arquivo você vai inserir a linha abaixo
web: java -Dserver.port=$PORT -Dspring.profiles.active=prod -jar target/[nome do arquivo .jar gerado no passo anterior]
Vá ao dashboard do Heroku e selecione a aba Settings:
Em Settings role a tela para baixo até exibir o botão Reveal Config Vars:
Clique neste botão e veremos a url de conexão do BD:
Agora vamos copiar o arquivo de configuração da app src\main\resources\application.properties e renomear a cópia para application-prod.properties
Você vai copiar o texto da caixa mostrada em vermelho na figura acima e colar no arquivo src\main\resources\application-prod.properties, conforme mostrado abaixo (a url de conexão é o valor da propriedade spring.datasource.url
:
#MySQL spring.datasource.url = mysql://a129f37d8b1a49:a99f4aa6@us-cdbr-east-05.cleardb.net/heroku_a5fef719c15dc8d?reconnect=true spring.jpa.show-sql=false spring.jpa.hibernate.ddl-auto=update
O seu arquivo application-prod.properties ficará assim:
spring.datasource.url = mysql://a129f37d8b1a49:a99f4aa6@us-cdbr-east-05.cleardb.net/heroku_a5fef719c15dc8d?reconnect=true spring.jpa.show-sql=false spring.jpa.hibernate.ddl-auto=update # logs logging.level.root=info logging.level.org.springframework.jdbc.core.JdbcTemplate=debug logging.level.web=DEBUG # retire o comentário da linha abaixo para criar as tabelas do banco de # dados. O banco de dados deve ser criado antes de executar o servidor. # O nome do banco de dados deverá ser autentica-autoriza spring.sql.init.mode=always # página de erro server.error.whitelabel.enabled=false server.error.include-stacktrace=always server.error.include-exception=true server.error.include-message=always server.error.include-binding-errors=always
Com os dois novos arquivos criados, o seu projeto ficará assim:
Você pode atualizar o GitHub com as novas alterações no projeto e após um breve espaço de tempo você verá que o Heroku realizou o deploy da app automaticamente.
Referência:
Em desenvolvimento