RestTemplate

发布时间 2023-03-25 22:38:01作者: shigp1

服务之间是通过http协议调用,虽然效率较低但是具有良好的可扩展性和可维护性。可以通过httpclient,okhttp等调用但是有重复代码,较难维护,使用不便。现在流行的方式有RestTemplate和Feign两种方式。
RestTemplate是同步客户端执行HTTP请求,暴露出一个简单的、模板等方法在基本HTTP客户机API库JDK HttpURLConnection,Apache HttpComponents等等。

 
 

RestTemplate常用的方法:

get 请求处理

getForEntity

getForEntity方法的返回值是一个ResponseEntity,ResponseEntity是Spring对HTTP请求响应的封装,包括了几个重要的元素,如响应码、contentType、contentLength、响应消息体等。

调用方:

@RequestMapping("/getEntity")
public ResponseEntity<String> getEntity() {
    List<InstanceInfo> list = eurekaClient.getInstancesByVipAddress("producer", false);

    if (list != null && !list.isEmpty()) {
        InstanceInfo instanceInfo = list.get(0);
        String url = "http://"+instanceInfo.getIPAddr()+":"+ instanceInfo.getPort();
        ResponseEntity<String> forEntity = restTemplate.getForEntity(url + "/hello", String.class);
        System.out.println(forEntity);
        return forEntity;
    }
    return null;
}

forEntity为:

<200,hello,8000,[Content-Type:"text/plain;charset=UTF-8", Content-Length:"10", Date:"Sat, 25 Mar 2023 11:57:24 GMT", Keep-Alive:"timeout=60", Connection:"keep-alive"]>

传参调用

以下例子都是调用方。
使用占位符:

@RequestMapping("/getEntityForParam")
public ResponseEntity<String> getEntityForParam() {
    List<InstanceInfo> list = eurekaClient.getInstancesByVipAddress("producer", false);

    if (list != null && !list.isEmpty()) {
        InstanceInfo instanceInfo = list.get(0);
        String url = "http://"+instanceInfo.getIPAddr()+":"+ instanceInfo.getPort();
        ResponseEntity<String> forEntity = restTemplate.getForEntity(url + "/hello?name={1}", String.class,"张三");
        System.out.println(forEntity);
        return forEntity;
    }
    return null;
}

 

使用map:

@RequestMapping("/getEntityForMap")
public ResponseEntity<String> getEntityForMap() {
    List<InstanceInfo> list = eurekaClient.getInstancesByVipAddress("producer", false);

    if (list != null && !list.isEmpty()) {
        InstanceInfo instanceInfo = list.get(0);
        String url = "http://"+instanceInfo.getIPAddr()+":"+ instanceInfo.getPort();
        Map<String,Object> map = Collections.singletonMap("name","李四");
        ResponseEntity<String> forEntity = restTemplate.getForEntity(url + "/hello?name={name}", String.class,map);
        System.out.println(forEntity);
        return forEntity;
    }
    return null;
}

/hello?name={name}里面的name要和Map里面的key相同.

 
 

getForObject

getForObject和getForEntity使用方式和传参方式都是相同的,只是返回值有区别。

post 请求处理

使用postForObject和postForEntity发送post请求,使用方式和传参和使用get请求相同。

 
 

postForLocation

postForLocation的返回值是URI。

 

调用方:

@RequestMapping("/postForLocation")
public URI postForLocation() {
    List<InstanceInfo> list = eurekaClient.getInstancesByVipAddress("producer", false);

    if (list != null && !list.isEmpty()) {
        InstanceInfo instanceInfo = list.get(0);
        String url = "http://"+instanceInfo.getIPAddr()+":"+ instanceInfo.getPort();
        URI forEntity = restTemplate.postForLocation(url + "/hello?name={1}",null, String.class,"张三");
        System.out.println(forEntity);
        return forEntity;
    }
    return null;
}

 
服务提供方:

@RequestMapping("/hello")
public String hello(String name, HttpServletResponse response) {
    response.addHeader("Location", "/hello?123");
    return "hello," + name + "," + port;
}

可以得到postForLocation的结果是response设置的Location头,结果如下:
 

 

如果服务提供方不设置头信息则postForLocation返回值为null。

 

exchange

exchange可以自定义http请求的头信息,同时保护get和post方法.

 

调用方:

@RequestMapping("/exchange")
public String exchange() {
    List<InstanceInfo> list = eurekaClient.getInstancesByVipAddress("producer", false);

    if (list != null && !list.isEmpty()) {
        InstanceInfo instanceInfo = list.get(0);
        String url = "http://"+instanceInfo.getIPAddr()+":"+ instanceInfo.getPort();
        ResponseEntity<String> responseEntity = restTemplate.exchange(url + "/hello?name={1}", HttpMethod.POST,null , String.class, "张三");
        System.out.println(responseEntity);
        return responseEntity.getBody();
    }
    return null;
}

参数传递和postForObject,postForEntity相同,可以指定http方法,比如用GET方式调用。第三个参数是请求头,这里为null。

 
 

拦截RestTemplate请求

如果需要拦截RestTemplate调用的请求,实现ClientHttpRequestInterceptor这个接口后将实现类注册到RestTemplate中。

 

例子:

public class LoggingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {

@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {

	System.out.println("拦截啦!!!");
	System.out.println(request.getURI());

	ClientHttpResponse response = execution.execute(request, body);

	System.out.println(response.getHeaders());
	return response;
}

}


@Bean
public RestTemplate restTemplate() {
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.getInterceptors().add(new LoggingClientHttpRequestInterceptor());
    return restTemplate;
}

访问http://localhost:8001/exchange,看到:
 

 

这里的拦截范围是从restTemplate调用开始到返回为止。上面用restTemplate调用必须写清服务的IP地址和端口,这在单个服务时还行,但是对于微服务来说就不够了,可能会增加服务和删除服务,服务地址会动态变化,而且对于负载均衡来说也不好,要直接使用restTemplate基本是和ribbon结合起来使用的。下次再看ribbon是怎么使用的。