zoukankan      html  css  js  c++  java
  • RestTemplate详解

    在SpringCloud项目中,当我们需要远程调用一个HTTP接口时,我们经常会用到RestTemplate这个类。这个类是Spring框架提供的一个工具类。它是一个同步的Rest API客户端,提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。

    说明:

    在项目架构中,我们基本都可以通过它来进行不同服务与服务之间的调用。在SpringCloud中我们依然可以使用HttpClient进行服务与服务调用,只不过如果采用HttpClient调用的话,会有一些弊端。例如:如果同一个服务有多个负载的话,采用HttpClient调用时,没有办法处理负载均衡的问题。还有另一个问题就是HttpClient只是提供了核心调用的方法并没有对调用进行封装,所以在使用上不太方便,需要自己对HttpClient进行简单的封装。

    在SpringCloud中为了解决服务与服务调用的问题,于是提供了两种方式来进行调用:RestTemplate和Feign。

    一、RestTemplate常用方法

    上面的方法我们大致可以分为三组:

    • getForObject --- optionsForAllow分为一组,这类方法是常规的Rest API(GET、POST、DELETE等)方法调用;
    • exchange:接收一个RequestEntity 参数,可以自己设置HTTP method, URL, headers和body。返回ResponseEntity。
    • execute:通过callback 接口,可以对请求和返回做更加全面的自定义控制。

     一般情况下,我们使用第一组和第二组方法就够了。

    二、RestTemplate简单使用

    1. RestTemplate的创建

    使用RestTemplate前,必须先创建实例化。

    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate;
    }
    
    @Bean
    public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setReadTimeout(5000);
        factory.setConnectTimeout(15000);
        //设置代理
        //factory.setProxy(null);
        return factory;
    }
    在内部,RestTemplate默认使用SimpleClientHttpRequestFactoryDefaultResponseErrorHandler来分别处理HTTP的创建和错误,但也可以通过setRequestFactorysetErrorHandler来覆盖。

    通过上面代码配置后,我们直接在代码中注入RestTemplate就可以使用了。

    2. 服务间的调用

    服务端Controller:

    @RestController
    @RequestMapping("/server")
    public class Controller {
    
        @GetMapping("/get")
        public Object get() {
            Map<String, String> map = new HashMap<String, String>();
            map.put("code", "0");
            map.put("msg", "success");
            map.put("data", "吉林乌拉");
            return map;
        }
    }

    服务端配置文件:application.yml

    eureka:
      client:
        service-url:
          defaultZone: http://127.0.0.1:8761/eureka/
    spring:
      application:
        name: test-server
    server:
      port: 8082

    客户端Controller:

    @RestController
    @RequestMapping("/client")
    public class Controller {
    
        @GetMapping("/get")
        public Object get() {
            Map<String, String> map = new HashMap<String, String>();
            map.put("code", "0");
            map.put("msg", "success");
            map.put("data", "吉林乌拉");
            return map;
        }
    }

    客户端配置文件application.yml:

    eureka:
      client:
        service-url:
          defaultZone: http://127.0.0.1:8761/eureka/
    spring:
      application:
        name: test-client
    server:
      port: 8081

    注意:需要在启动类上添加@EnableEurekaClient注解,将两个服务注册到eureka中。

    (1) 调用方式一

    @RestController
    @RequestMapping("/client")
    public class Controller {
    
        @Autowired
        private RestTemplate template;
    
        @GetMapping("/get")
        public Object get() {
            String result = template.getForObject("http://127.0.0.1:8082/server/get", String.class);
            return result;
        }
    }

    弊端:server端的接口地址直接写死了,这样当服务接口变更时,是需要更改客户端代码的。

    (2) 调用方式二:

    注册中心这时可以派上用场了。因为注册中心知道所有服务的地址,我们通过注册中心先知道server端的接口地址,然后再调用。

    @RestController
    @RequestMapping("/client")
    public class Controller {
    
        @Autowired
        private RestTemplate template;
    
        @Autowired
        private LoadBalancerClient loadBalancerClient;
    
        @GetMapping("/get")
        public Object get() {
            ServiceInstance serviceInstance = loadBalancerClient.choose("test-server");
            String url = String.format("http://%s:%s/server/get", serviceInstance.getHost(), serviceInstance.getPort());
            String result = template.getForObject(url, String.class);
            return result;
        }
    }

    SpringClourd中提供了LoadBalancerClient接口。通过这个接口我们可以通过用户中心的Application的名字(即application.yml中的spring.application.name属性值)来获取该服务的地址和端口。

    弊端:每次调用服务时都要先通过Application的名字来获取ServiceInstance对象,然后才可以发起接口调用。实际上在SpringCloud中为我们提供了@LoadBalanced注解,只要将该注解添加到RestTemplate中的获取的地方就可以了。

    (3) 调用方式三

    在RestTemplate实例化时添加@LoadBalanced注解

    @Bean
    @LoadBalanced
    public RestTemplate initRestTemplate() {
         return new RestTemplate();
    }

    客户端Controller:

    @RestController
    @RequestMapping("/client")
    public class Controller {
    
        @Autowired
        private RestTemplate template;
    
        @GetMapping("/get")
        public Object get() {
            //注意,应用可能存在上下文配置(server.context-path属性值), 如果有则需要加上, 假如上下文是web-test,则url为
            //http://TEST-SERVER/web-test/server/get
            //String url = String.format("http://%s/server/get", "test-server");
            String result = template.getForObject(url, String.class);
            return result;
        }
    }

    三、RestTemplate请求参数传递

    使用RestTemplate post请求的时候主要可以通过三种方式实现:

    • 调用postForObject方法
    • 使用postForEntity方法
    • 调用exchange方法

    postForObject和postForEntity方法的区别主要在于可以在postForEntity方法中设置header的属性,当需要指定header的属性值的时候,使用postForEntity方法。exchange方法和postForEntity类似,但是更灵活,exchange还可以调用get、put、delete请求。使用这三种方法调用post请求传递参数,Map不能定义为以下两种类型(url使用占位符进行参数传递时除外):

    Map<String, Object> paramMap = new HashMap<String, Object>();
    Map<String, Object> paramMap = new LinkedHashMap<String, Object>();

    前人经过测试,发现以上两种类型不能被后台接收到,当把Map类型换成LinkedMultiValueMap后,参数成功传递到后台:

    MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<String, Object>();

    正确传递参数如下:

    RestTemplate template = new RestTemplate();
    String url = "http://192.168.2.40:8081/channel/channelHourData/getHourNewUserData";
    // 封装参数,千万不要替换为Map与HashMap,否则参数无法传递
    MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<String, Object>();
    paramMap.add("dt", "20180416");
    
    // 1、使用postForObject请求接口
    //第一参数表示要调用的服务的地址 第二个参数表示请求参数 第三个参数表示返回的消息体的数据类型
    String result = template.postForObject(url, paramMap, String.class); System.out.println("result1==================" + result); // 2、使用postForEntity请求接口 HttpHeaders headers = new HttpHeaders(); HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<MultiValueMap<String, Object>>(paramMap,headers); ResponseEntity<String> response2 = template.postForEntity(url, httpEntity, String.class); System.out.println("result2====================" + response2.getBody()); // 3、使用exchange请求接口 ResponseEntity<String> response3 = template.exchange(url, HttpMethod.POST, httpEntity, String.class); System.out.println("result3====================" + response3.getBody());

    get方式传参说明:如果是get请求,又想要把参数封装到map里面进行传递的话,Map需要使用HashMap,且url需要使用占位符

    RestTemplate restTemplate2 = new RestTemplate();
    String url = "http://127.0.0.1:8081/interact/getData?dt={dt}&ht={ht}";
       
        // 封装参数,这里是HashMap
    Map<String, Object> paramMap = new HashMap<String, Object>();
    paramMap.put("dt", "20181116");
    paramMap.put("ht", "10");
    
    //1、使用getForObject请求接口
    String result1 = template.getForObject(url, String.class, paramMap);
    System.out.println("result1====================" + result1);
    
    //2、使用exchange请求接口
    HttpHeaders headers = new HttpHeaders();
    headers.set("id", "lidy");
    HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<MultiValueMap<String, Object>>(null,headers);
    ResponseEntity<String> response2 = template.exchange(url, HttpMethod.GET, httpEntity, String.class,paramMap);
    System.out.println("result2====================" + response2.getBody());

    delete请求, 请求方式使用 HttpMethod.DELETE

    StringBuffer url = new StringBuffer(baseUrl).append("/user/delete/{id}");
     
    Map<String, Object> paramMap = new HashMap<>();
    paramMap.put("id", id);
    
    ResponseEntity<String > response = restTemplate.exchange(url.toString(), HttpMethod.DELETE, null, String .class, paramMap);
    String result = response.getBody();

    put请求, 请求方式使用 HttpMethod.PUT

    StringBuffer url = new StringBuffer(baseUrl)
                    .append("/user/edit?tmp=1")
                    .append("&id={id}")
                    .append("&userName={userName}")
                    .append("&nickName={nickName}")
                    .append("&realName={realName}")
                    .append("&sex={sex}")
                    .append("&birthday={birthday}");
     
    Map<String, Object> paramMap = new HashMap<>();
    paramMap.put("userId", userInfoDTO.getId());
    paramMap.put("userName", userInfoDTO.getUserName());
    paramMap.put("nickName", userInfoDTO.getNickName());
    paramMap.put("realName", userInfoDTO.getRealName());
    paramMap.put("sex", userInfoDTO.getSex());
    paramMap.put("birthday", userInfoDTO.getBirthday());
    
    ResponseEntity<String > response = restTemplate.exchange(url.toString(), HttpMethod.PUT, null, String .class, paramMap);
    String result = response.getBody();
  • 相关阅读:
    非常适合新手的一个Python爬虫项目: 打造一个英文词汇量测试脚本!
    代码遇到异常怎么办?不要慌,来看看这个!
    老司机要开车了!用Selenium+PhantomJS来抓取煎蛋网妹子图
    Python基础之格式化输出函数format()功能详解
    Python基础之常用格式化输出字符详解
    新手学python,如何才能更快升职加薪,迎娶白富美,走上人生巅峰
    ASP.NET MVC学习笔记 第二天
    ASP.NET MVC学习笔记 第一天
    ActiveMQ相关:
    WPF中使用定时器 DispatcherTimer 做TCP连接中的心跳 HeartBeat
  • 原文地址:https://www.cnblogs.com/myitnews/p/12296939.html
Copyright © 2011-2022 走看看