当前位置:网站首页>Implementing declarative rest calls using feign

Implementing declarative rest calls using feign

2022-06-11 10:37:00 chentian114

Use Feign Implement declarative REST call

Preface

In the foreword 《 Use Ribbon Achieve client load balancing 》https://mp.weixin.qq.com/s/YwwCsUKoUA1UsaarZWVMHw in , Inter service calls use RestTemplate:

User entity = restTemplate.getForObject("http://micro-provider-user/user/v1/"+id, User.class);

From the code , It is mainly constructed by splicing strings URL To make the call , This makes the code difficult to maintain .

Feign brief introduction

Feign yes Netflix Developed declarative 、 templated HTTP client ,Feign Can help us more convenient 、 Call... Gracefully HTTP API .

stay Spring Cloud in , Use Feign It's simple – Create an interface , And add some comments to the interface , The code is done . Feign Support for multiple annotation , for example Feign Own notes or JAX-RS Annotations etc. .

Spring Cloud Yes Feign Enhanced , send Feign Support SpringMVC annotation , And integrated Ribbon and Eureka , So that Feign It is more convenient to use .

Spring Cloud Feign In essence, it is a dynamic agent mechanism , You just give one RESTful API Corresponding Java Interface , It can dynamically assemble the strong type client of the corresponding interface at run time , The simplified structure of the assembled client and the process of request response , Here's the picture :

33.Spring Feign

Although the services we developed are weakly typed RESTful Service for , But because of Spring Cloud Feign Support for , Let's simply give a strongly typed Java API Interface , You automatically get a strongly typed client .

For details of strong / weak type interfaces, see :《 How to base on Spring Cloud Feign Implement strongly typed interface calls RESTful service 》https://mp.weixin.qq.com/s/bYZXhf9BfAfL5aC3bSyiHQ

Integrate for service consumers Feign

  1. Create project , Copy project micro-consumer-movie , take artifactId It is amended as follows micro-consumer-movie-feign .

  2. add to Feign Dependence

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
  1. Create a Feign Interface , And add @FeignClient annotation .
@FeignClient(name = "micro-provider-user")
public interface UserFeignClient {
    
    @RequestMapping(value = "/user/v1/{id}", method = RequestMethod.GET)
    User findById(@PathVariable("id") Long id);
}

@FeignClient In the annotations micro-provider-user Is an arbitrary client name , be used for Ribbon Load Balancer .

In this case , Due to the use of Eureka , therefore Ribbon Will be able to micro-provider-user It can be interpreted as Eureka Server Services in the service registry .

Of course , If you don't want to use it Eureka You can use service.ribbon.listOfServers Property to configure the server list .

You can also use url Property specifies the requested URL (URL It can be complete URL Or the Lord name ), for example @FeignClient(name = "micro-provider-user", url="http://localhost:8000/")".

  1. modify Controller Code , Let it call Feign Interface .
@RestController
@RequestMapping("/movie/v1")
public class MovieController {
    
    @Resource
    private UserFeignClient userFeignClient;

    @GetMapping("/{id}")
    public User findById(@PathVariable Long id){
    
        User entity = userFeignClient.findById(id);
        return entity;
    }
}
  1. Modify the startup class , To add @EnableFeignClients annotation .
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class MicroConsumerMovieFeignApplication {
    
    public static void main(String[] args) {
    
        SpringApplication.run(MicroConsumerMovieFeignApplication.class, args);
    }
}
  1. Start service test . start-up Eureka Server: micro-discovery-eureka 、 Service providers :micro-provider-user 、 Serving consumers :micro-consumer-movie-feign ; visit :http://localhost:8010/movie/v1/1 .

Manually create Feign

In some cases , You can use Feign Builder API Manually create Feign .

For example, the following scenario :

  • The user microservice interface can only be called after logging in , And for the same API , Users in different roles have different behaviors .
  • Let the movie micro service be the same Feign The interface uses different accounts to log in , And call the user microservice interface .

One 、 Modify the user microservice , Make it accessible only after login and authentication

  1. Create project , Copy project micro-provider-user , take artifactId It is amended as follows micro-provider-user-with-auth .

  2. Add dependencies to the project .

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
  1. establish Spring Scurity Configuration class
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Resource
    private UserDetailsService userDetailsService;

    @Bean
    public PasswordEncoder passwordEncoder(){
    
        //  Plaintext encoder .Spring  Provide clear text testing 
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
    
        //  All requests , All need to go through  HTTP basic  authentication 
        http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
}

@Component
public class CustomUserDetailsService implements UserDetailsService {
    
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    
        if("user".equalsIgnoreCase(username)){
    
            return new SecurityUser("user", "password1", "user-role");
        } else if("admin".equalsIgnoreCase(username)){
    
            return new SecurityUser("admin", "password2", "admin-role");
        } else {
    
            return null;
        }
    }
}

public class SecurityUser implements UserDetails {
    
    private Long id;
    private String username;
    private String password;
    private String role;
    
    public SecurityUser(String username, String password, String role){
    
        super();
        this.username = username;
        this.password = password;
        this.role = role;
    }
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
    
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        SimpleGrantedAuthority authority = new SimpleGrantedAuthority(this.role);
        authorities.add(authority);
        return authorities;
    }
    
    //...
    
    @Override
    public boolean isAccountNonExpired() {
    
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
    
        return true;
    }
    @Override
    public boolean isCredentialsNonExpired() {
    
        return true;
    }
    @Override
    public boolean isEnabled() {
    
        return true;
    }
}

Simulated two accounts : user and admin , Their passwords are password1 and password2 , The roles are user-role and admin-role .

  1. modify Controller , Print the currently logged in user information in the log .
@RestController
@RequestMapping("/user/v1")
public class UserController {
    
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private UserRepository userRepository;

    @GetMapping("/{id}")
    public User findById(@PathVariable Long id){
    
        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if(principal instanceof UserDetails){
    
            UserDetails user = (UserDetails) principal;
            Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
            for(GrantedAuthority authority : authorities){
    
                logger.info(" The current user is :{},  The role is :{}", user.getUsername(), authority.getAuthority());
            }
        }
        User entity = userRepository.findOne(id);
        return entity;
    }
}
  1. Start service test , start-up Eureka Server: micro-discovery-eureka 、 Service providers :micro-provider-user-auth , Access address : http://localhost:8000/user/v1/1 .

Two 、 Modify the movie microservice , Make it possible to achieve the same Feign The interface uses different accounts to log in

  1. Copy project micro-consumer-movie-feign , modify artifactId by micro-consumer-movie-feign-manual .

  2. Get rid of Feign Interface UserFeignClient Of @FeignClient annotation .

  3. Remove the on the startup class @EnableFeignClients annotation .

  4. modify Controller as follows :

@Import(FeignClientsConfiguration.class)
@RestController
@RequestMapping("/movie/v1")
public class MovieController {
    
    private UserFeignClient userFeignClient;
    private UserFeignClient adminFeignClient;

    @Autowired
    public MovieController(Decoder decoder, Encoder encoder, Client client, Contract contract){
    
        this.userFeignClient = Feign.builder().client(client).encoder(encoder).decoder(decoder).contract(contract)
                .requestInterceptor(new BasicAuthRequestInterceptor("user", "password1"))
                .target(UserFeignClient.class, "http://micro-provider-user");

        this.adminFeignClient = Feign.builder().client(client).encoder(encoder).decoder(decoder).contract(contract)
                .requestInterceptor(new BasicAuthRequestInterceptor("admin", "password2"))
                .target(UserFeignClient.class, "http://micro-provider-user");
    }

    @GetMapping("/user-user/{id}")
    public User findByIdUser(@PathVariable Long id){
    
        User entity = userFeignClient.findById(id);
        return entity;
    }

    @GetMapping("/user-admin/{id}")
    public User findByIdAdmin(@PathVariable Long id){
    
        User entity = adminFeignClient.findById(id);
        return entity;
    }
}

among ,@Import Imported FeignClientsConfiguration yes Spring Cloud by Feign The configuration class provided by default .

userFeignClient Login account user, adminFeignClient Login account admin , They use the same interface UserFeignClient .

  1. Start the test , start-up Eureka Server: micro-discovery-eureka 、 Service providers :micro-provider-user-auth 、 Serving consumers :micro-consumer-movie-feign-manual ; visit http://localhost:8010/movie/v1/user-user/1 and http://localhost:8010/movie/v1/user-admin/1 , see user Logs of services , You can see that different user accounts have been printed .

Code warehouse

https://gitee.com/chentian114/spring-cloud-practice

official account

 originally chen

Reference resources

《Spring Cloud And Docker Microservice architecture practice 》 Zhou Li

原网站

版权声明
本文为[chentian114]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/162/202206111031525240.html