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();

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM