Mostrando postagens com marcador Spring. Mostrar todas as postagens
Mostrando postagens com marcador Spring. Mostrar todas as postagens

segunda-feira, 1 de abril de 2024

Diferença entre Forward e Redirect no retorno do Controller do SpringMVC

        Nesse artigo vou mostrar a diferença entre o Forward e o Redirect em uma chamada para um Controller do SpringMVC que retorna a String com o endereço a ser chamado depois da ação. Além de retornar a String com a página a ser carregada você pode especificar como esse retorno vai ser feito usando as opções Forward e o Redirect concatenadas ao endereço: 

Forward:

- O forward é um encaminhamento interno, onde a mesma requisição é reenviada para outro controller ou endpoint no servidor.
- O forward é executado do lado do servidor, sem envolver uma nova requisição do cliente.
- O URL no navegador permanece o mesmo.
- Os dados da requisição original são preservados e podem ser acessados pelo controller ou endpoint de destino.
- É útil quando você deseja processar dados adicionais ou realizar ações no backend antes de exibir a próxima página.

Redirect:
- O redirect é uma resposta do servidor ao cliente para redirecioná-lo para um novo URL.
- O redirect envolve uma nova requisição do cliente para o novo URL.
- O URL no navegador é atualizado para o novo URL.
- Os dados da requisição original não são preservados. Eles devem ser passados através de parâmetros de URL, atributos de sessão ou armazenados em algum outro local persistente.
- É útil quando você deseja redirecionar o cliente para uma nova página, como após um cadastro bem-sucedido ou para evitar reenvio de formulários quando atualizar a página.

        No contexto específico onde você quer permanecer na mesma página de cadastro com os dados pré-preenchidos em caso de erro, o uso de forward é mais adequado. Ao retornar a String que representa a mesma página de cadastro, você está realizando um forward interno, mantendo os dados da requisição original e exibindo a mesma página com os dados pré-preenchidos.

quinta-feira, 12 de agosto de 2021

Consumindo API Rest com Spring Boot usando a classe WebClient

           Nesse artigo vou mostrar como fazer um cliente para uma API Rest com Spring 5 usando a classe WebClient que é parte do novo WebFlux Framework. Esse cliente vai consumir uma API de Livros. Aproveitando o post, já vou criar esse cliente em uma classe de teste usando JUnit para mostrar o teste de APIs com ele. Mas a forma de uso aqui não se limita a testes.

         

Como o foco é a construção de um cliente para consumir uma API, vou omitir o código da API, trazendo só a construção do cliente. Então vamos ao código:

 

import java.util.List;

import org.junit.jupiter.api.Assertions

import org.junit.jupiter.api.BeforeAll;

import org.junit.jupiter.api.Test;

import org.springframework.core.ParameterizedTypeReference;

import org.springframework.http.HttpHeaders;

import org.springframework.http.HttpMethod;

import org.springframework.http.MediaType;

import org.springframework.web.reactive.function.client.WebClient;

import org.springframework.web.reactive.function.client.WebClient.Builder;

import org.springframework.web.reactive.function.client.WebClient.ResponseSpec;

 

import br.com.socialbooks.model.Livro;

import reactor.core.publisher.Mono;

 

class LivroControllerTest {

 

       @BeforeAll

       static void inserirLivro() {

             // configura uma requisição

             Builder builder = WebClient.builder();

             builder.baseUrl("http://localhost:8080");

 

             Livro livro = new Livro("Aprendendo Spring boot");

 

             // prepara o cliente

             WebClient client = builder.build();

             ResponseSpec response = client.method(HttpMethod.POST).uri("/livros").bodyValue(livro).retrieve();

 

             // recebendo o recurso salvo

             Mono<Livro> monoLivro = response.bodyToMono(Livro.class);

             livro = monoLivro.block();

       }

 

       @Test

       void testGetListaLivros() {

             // configura uma requisição

             Builder builder = WebClient.builder();

             builder.baseUrl("http://localhost:8080");

             builder.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);

 

             // prepara o client

             WebClient client = builder.build();

             ResponseSpec response = client.method(HttpMethod.GET).uri("/livros").retrieve();

 

             /*

              * recebe o Mono que executa de forma assíncrona. OBS: Para carregar uma lista

              * de objetos, é preciso usar o ParameterizedTypeReference.

              */

             Mono<List<Livro>> monoLivro = response.bodyToMono(new                  ParameterizedTypeReference<List<Livro>>() {

             });

 

             // como funciona de forma assincrona, usamos o block para forçar ele esperar a resposta.

             List<Livro> livros = monoLivro.block();

 

              Assert.assertTrue(livros.get(0).getTituloLivro().equals("Aprendendo Spring boot"));

 

       }

 

       @Test

       void testGetLivro() {

             // configura uma requisição

             Builder builder = WebClient.builder();

             builder.baseUrl("http://localhost:8080");

             builder.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);

 

             // prepara o client

             WebClient client = builder.build();

             ResponseSpec response =             client.method(HttpMethod.GET).uri("/livros/1").retrieve();


             // recebe o Mono que executa de forma assíncrona

             Mono<Livro> monoLivro = response.bodyToMono(Livro.class);

 

             // como funciona de forma assincrona, usamos o block para forçar ele esperar 

             // a resposta.

             Livro livro = monoLivro.block();

             Assert.assertTrue(livro.getTituloLivro().equals("Aprendendo Spring boot"));

       }

}

 

Pra esse artigo é só.


Esse artigo foi baseado estudos feitos em:

sexta-feira, 17 de julho de 2020

Resolvendo problema CORS no Spring

Olá, hoje vou falar sobre o CORS – Cross-Origin Resource Sharing que significa compartilhamento de recursos de origem cruzada.

Antes era muito comum o uso de iFrames ou similares para fazer com que um site acessasse diretamente conteúdo de outros o que também era válido para aplicações web mais complexas, o problema é que isso começou a ser explorado por pessoas mal intencionadas para se passarem por um site ou serviço que não são e roubar informações ou outras atividades ilícitas. Então os navegadores começaram a “bloquear” esse tipo de coisa, validando antes se a origem tem acesso ao backend vindo de um IP diferente ou não configurado em uma digamos “White list”.

         Trarar o CORS é obrigação do backend, e vamos ver como fazer isso no Spring. No caso, podemos fazer de duas formas: liberando geral ou especificando as URLs que terão o acesso garantido.

    Para liberar qualquer endereço, podemos desabilitar o CORS na classe de configuração de segurança:

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/v2/api-docs",
                "/configuration/ui",
                "/swagger-resources/**",
                "/configuration/security",
                "/swagger-ui.html",
                "/webjars/**");
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests().antMatchers("/xxx/**").authenticated();
        http.authorizeRequests().anyRequest().permitAll();
        http.csrf().disable();

    }

}


        Ou podemos criar um bean na classe principal da aplicação e passar o padrão de URL como "/**":
       
@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/greeting-javaconfig").allowedOrigins("http://localhost:9000");
        }
    };
}


    Para liberar endereços específicos, o que é mais recomendado, basta passar o endereço no lugar do "/**":

@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/greeting-javaconfig").allowedOrigins("http://localhost:9000");
        }
    };
}


     Outra forma de fazer é ao invés de criar o bean na classe principal, é criar uma classe específica com a configuração, deixando-a mais explícita:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
public class CorsConfiguration implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**");
    }

}

 



 FOnte: https://spring.io/guides/gs/rest-service-cors/ 

quinta-feira, 4 de junho de 2020

Configurando o Spring Security 5 com autenticação em memória

    Do Spring Security 4 para o 5 configurar um usuário e senha em memória não é mais tão simples, porém nesse link abaixo tem a configuração necessária para fazê-lo basicamente como antes:

https://spring.io/guides/gs/securing-web/

    Basicamente o que muda é a classe WebSecurityConfig que passa a retornar um Bean do tipo UserDetailsService ao invés de criarmos o método: 

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("danieloliveira").password("oliveira").roles("USER");
}

    Ficando assim:

import org.springframework.context.annotation.Bean;
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.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/h2-console/**").permitAll().anyRequest().authenticated().and()
            .httpBasic().and().csrf().disable();
}

@Bean
@Override
public UserDetailsService userDetailsService() {
     UserDetails user = User.withDefaultPasswordEncoder().username("seu_usuario").password("sua_senha")
        .roles("USER").build();

    return new InMemoryUserDetailsManager(user);
}

}

sexta-feira, 27 de outubro de 2017

Cuidado com Try/Catch no EJB e Spring!

Ao criarmos um método geralmente colocamos blocos de código dentro de Try/Catch para podermos tratar os possíveis erros e o sistema continuar seu trabalho. Este é talvez o principal problema de integridade e consistência de dados quando as transações são usadas. Veja o exemplo a seguir:

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public TradeData placeTrade(TradeData trade) throws Exception {
    try {
        insertTrade(trade);
        updateAcct(trade);
        return trade;
    } catch (Exception up) {
       //log the error
       throw up;
    }
}

Suponha que a conta não tenha fundos suficientes para comprar o estoque em questão ou não está configurado para comprar ou vender estoque ainda e lança uma exceção verificada (por exemplo, FundsNotAvailableException). A ordem comercial – insertTrade(trade) - persistiu no banco de dados ou a unidade lógica inteira do trabalho foi revertida? A resposta, surpreendentemente, é que, após uma exceção verificada (no Spring Framework ou EJB), a transação compromete qualquer trabalho que ainda não tenha sido comitado. Isso significa que se uma exceção verificada ocorrer durante o método updateAcct() , a ordem comercial foi persistida, mas a conta não foi atualizada para refletir a transação financeira.

As exceções de tempo de execução (ou seja, exceções não verificadas) forçam automaticamente a transação EJB/Spring a reverter completamente, mas as exceções verificadas não. Isso não é de todo ruim ou errado, pois muitas vezes precisamos tratar os erros e deixar o sistema prosseguir. Por exemplo, se em um processo, precisamos enviar um e-mail com as operações salvas no banco, mas o servidor de e-mail estiver fora, qual o mais importante? Salvar os dados ou enviar o e-mail? Então tratamos o envio do e-mail e deixamos os dados serem salvos.

Mas e quando for o caso em que precisarmos abortar tudo?

A anotação @TransactionAttribute encontrada na especificação EJB não inclui diretivas para especificar o comportamento de reversão. Em vez disso, você deve usar o método SessionContext.setRollbackOnly() para marcar a transação para rollback em um local específico, ou, o mais aconselhavel é criar uma exceção que force esse rollback com a anotação @ApplicationException setando  a propriedade rollback = true. Vamos ver primeiro ver como fazer a exceção funcionar apenas para um método:

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public TradeData placeTrade(TradeData trade) throws Exception {
    try {
       insertTrade(trade);
       updateAcct(trade);
       return trade;
    } catch (Exception up) {
        //log the error
        sessionCtx.setRollbackOnly();
        throw up;
    }
}

Uma vez que o método setRollbackOnly() é invocado, você não pode mudar de idéia; O único resultado possível é reverter a transação após a conclusão do método que iniciou a transação.

           Para pegar a sessão em um EJB faz-se daseguinte maneira:

@Resource
SessionContext context;
 

           A segunda opção que é criar uma Exceção e anotá-la segue abaixo:

@ApplicationException(rollback=true) 
public class RegraNegocioException extends Exception
{
   ...
}
 
Já no Spring Framework você especifica esse comportamento do rollback através do parâmetro rollbackFor na anotação @Transactional, conforme mostrado a seguir:

@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
public TradeData placeTrade(TradeData trade) throws Exception {
    try {
           insertTrade(trade);
updateAcct(trade);
           return trade;
    } catch (Exception up) {
           //log the error
           throw up;
    }
}

Esses são problemas difíceis de serem detectados no momento do desenvolvimento e de testes, pois geralmente ocorrem quando o processamento da aplicação e o acesso ao banco de dados estão muito altos.

Mas atenção! Essa solução funciona se você tomar cuidado com as armadilhas do REQUIRED_NEW, como mostro nesse outro post:

http://olamundo-java.blogspot.com.br/2017/10/armadilhas-do-requirednew-transaction.html

        Espero que esse post ajude. A fonte pela qual me baseei para escrever esse post segue abaixo:



quarta-feira, 25 de outubro de 2017

Armadilhas do REQUIRED_NEW transaction no EJB e Spring

Pesquisando sobre um erro de duplicação de dados e problemas com transações no EJB encontrei essa explicação muito interessante. O atributo REQUIRED_NEW tanto no EJB quanto no Spring podem ter resultados inesperados e levar a dados corrompidos e inconsistentes. Isso pode ocorrer devido o fato de ele sempre iniciar uma nova transação quando o método é chamado, mesmo já existindo uma transação ou não.

Veja o Exemplo:

@Transactional(propagation=Propagation.REQUIRES_NEW)
public long insertTrade(TradeData trade) throws Exception
{
          ...
}

@Transactional(propagation=Propagation.REQUIRES_NEW)
public void updateAcct(TradeData trade) throws Exception
{
   ...
}

Nesse caso os 2 métodos são públicos e estão anotados com REQUIRED_NEW. Quando chamados de forma separada, não há problema. Porém, se um for usado os dois métodos dentro de uma mesma unidade de trabalho transacionada, como por exemplo um outro método, isso pode causar inconsistências. Por exemplo, digamos que dentro do método insertTrade() você chame o método updadeAcct(), se ocorrer um rolled back depois do chamada do updateAcct(), ele já vai ter efetuado o commit no banco de dados, sendo desfeito apenas o que pertence ao insertTrade().

@Transactional(propagation=Propagation.REQUIRES_NEW)
public long insertTrade(TradeData trade) throws Exception {
       em.persist(trade);
       updateAcct(trade);
      //exception occurs here! Trade rolled back mas updateAcct(trade) não!
      ...
}



Isso acontece porque uma nova transação é iniciada no método updateAcct() criando uma árvore de transações, e houve um commit quando ele terminou sua execução, independentemente do que ocorra em outra transação da árvore. Devido a esse comportamento, o atributo de transação REQUIRES_NEW deve ser usado somente se a ação do banco de dados no método que está sendo invocado precisa ser salva no banco de dados independentemente do resultado da transação do topo da árvore (como uma funcionalidade de auditoria por exemplo).

O ponto principal é sempre usar o atributo MANDATORY ou REQUIRED vez de REQUIRES_NEW menos que você tenha um motivo para usá-lo.

         Esses são problemas difíceis de serem detectados no momento do desenvolvimento e de testes, pois geralmente ocorrem quando o processamento da aplicação e o acesso ao banco de dados estão muito altos.

          Aconselho uma passada por esse outro post que deixo o link logo abaixo também, pois traz um outro problema que pode acarretar em erros semelhantes de inconsistência de dados na aplicação.


quinta-feira, 7 de abril de 2016

Garantindo o encoding UTF-8 em aplicações com o Spring Boot

       Uma das formas mais eficientes de tratar a questão de caracteres estranhos e setar o encoding UTF-8 é através de um Filtro que intercepta a requisição ao servidor e antes de qualquer coisa atribui o encoding na request, e faz o mesmo com o response. O Spring Boot tem um filter para isso e basta usarmos esse filter para garantir que a nossa aplicação irá tratar corretamente os dados.

       Ao criar um projeto usando o Spring Boot duas classes são criadas, uma o o nome ServletInitializer.java e outra com o nome-da-aplicaçãoApplication.java. Esta segunda classe, se o nome da minha aplicação fosse teste, seu nome seria então TesteApplication.java, que é o que vamos considerar para o exemplo.

        Esta classe é responsável por configurações do projeto antes feitas no arquivo web.xml, de forma que declarações de filtros, servlets e listeners são declarados nela.

         No momento da criação do projeto, ele é muito simples, veja o código:

@SpringBootApplication
public class CobrancaApplication {

      public static void main(String[] args) {
            SpringApplication.run(CobrancaApplication.class, args);
      }
     
}

O que precisa ser feito é adicionar o seguinte trecho de código:

@Bean
public Filter getCharacterEncodingFilter() {

    CharacterEncodingFilter encodingFilter = new encodingFilter();

    encodingFilter.setEncoding("UTF-8");
    encodingFilter.setForceEncoding(true);

    return encodingFilter;

}

Os pacotes relacionados a esse trecho de código são:

import javax.servlet.Filter;
import org.springframework.context.annotation.Bean;
import org.springframework.web.filter.CharacterEncodingFilter;


Pronto! Dessa forma  sua aplicação com o Spring Boot está livre dos caracteres estranhos...