一、简介
RestTemplate是spring内置的http请求封装,使用RestTemplate可以轻松的实现HTTP请求。本文将通过RestTemplate在Spring Cloud项目中跨服务调用的示例来讲解RestTempate中常用的GET和POST请求方法,其他方法因为不常用,所以用到的时候再做查阅即可。
二、工程准备
本文将通过在Spring Cloud中跨服务调用示例来说明RestTemplate的使用方法,并且通过服务名称调用。首先需要准备三个工程,分别如下:
【a】Eureka Server:服务注册中心,端口1111;
【b】Service A: 服务提供者A,端口2222;
【c】Service B: 服务提供者B,端口3333;
至于Eureka Server服务注册中心的搭建本文就不做详解,可以参见前面的文章。本文将通过服务提供者A调用服务提供者B的接口实现Rest调用。
三、创建Service-B
该工程主要暴露一些接口给外部调用:
【a】pom.xml,具体依赖如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.springcloud.wsh</groupId>
<artifactId>springcloud_service_b</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springcloud_service_b</name>
<description>服务提供者B</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Camden.SR6</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
【b】配置文件:
server:
port: 3333
spring:
application:
name: service-b
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1111/eureka/
【c】User实体类
/**
* @Title: User
* @ProjectName springcloud_resttemplate
* @Description: 用户实体类
* @Author WeiShiHuai
* @Date 2018/10/12 14:07
*/
public class User {
/**
* 用户ID
*/
private String id;
/**
* 用户姓名
*/
private String username;
/**
* 用户密码
*/
private String password;
public User() {
}
public User(String id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
【d】ServiceBController接口
主要提供一些接口给外部调用。
@RestController
public class ServiceBController {
//模拟数据
private static List<User> userList = new ArrayList<>();
static {
userList.add(new User(UUID.randomUUID().toString(), "zhangsan", "123456"));
userList.add(new User(UUID.randomUUID().toString(), "lisi", "654321"));
userList.add(new User(UUID.randomUUID().toString(), "wangwu", "wang123456"));
}
@RequestMapping("/getUserList")
public List<User> getUserList() {
return userList;
}
@RequestMapping("/getByUserId")
public User getByUserId(String id) {
return userList.get(Integer.parseInt(id));
}
@RequestMapping("/saveUser")
public String saveUser(@RequestBody User user) {
return "save success! >>>" + user.toString();
}
@RequestMapping("/saveUserAndType")
public String saveUserAndType(@RequestBody User user, String type) {
return "save success! >>>" + user.toString() + ", type >>>" + type;
}
@RequestMapping("/postByMap")
public String postByMap(String name) {
return "name >>> " + name;
}
}
四、Service-B工程创建
【a】pom.xml,注意引入Ribbon依赖,具体依赖如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.springcloud.wsh</groupId>
<artifactId>springcloud_service_a</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springcloud_service_a</name>
<description>服务提供者A</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Camden.SR6</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
【b】启动类
启动类需要注入RestTemplate Bean对象,并加上@LoadBalance注解实现负载均衡调用
package com.springcloud.wsh;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableEurekaClient
public class SpringcloudServiceAApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudServiceAApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
【c】配置文件
server:
port: 2222
spring:
application:
name: service-a
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1111/eureka/
【d】User实体类
public class User {
/**
* 用户ID
*/
private String id;
/**
* 用户姓名
*/
private String username;
/**
* 用户密码
*/
private String password;
public User() {
}
public User(String id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
【e】ServiceAController.java:
package com.springcloud.wsh.controller;
import com.springcloud.wsh.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* @Title: ServiceAController
* @ProjectName springcloud_resttemplate
* @Description: RestTemplate的使用方法
* @Author WeiShiHuai
* @Date 2018/10/12 13:55
*/
@RestController
public class ServiceAController {
@Autowired
private RestTemplate restTemplate;
/*********************************************getForEntity****************************************************/
/**
* 无参数情况下的getForEntity
*/
@RequestMapping("/getUserList")
public String getUserList() {
ResponseEntity<List> responseEntity = restTemplate.getForEntity("http://service-b/getUserList", List.class);
List<User> list = responseEntity.getBody();
return list.toString();
}
/**
* 有参数情况下的getForEntity
* 传参方式1: 使用占位符方式
*/
@RequestMapping("/getByUserId")
public String getByUserId(String id) {
ResponseEntity<User> userResponseEntity = restTemplate.getForEntity("http://service-b/getByUserId?id={id}", User.class, id);
User userBody = userResponseEntity.getBody();
return userBody.toString();
}
/**
* 有参数情况下的getForEntity
* 传参方式1: 使用map方式
*/
@RequestMapping("/getByUserIdByMap")
public String getByUserIdByMap(String id) {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("id", id);
ResponseEntity<User> userResponseEntity = restTemplate.getForEntity("http://service-b/getByUserId?id={id}", User.class, paramMap);
User userBody = userResponseEntity.getBody();
return userBody.toString();
}
/*********************************************getForObject****************************************************/
/**
* 无参数情况下的getForObject
*/
@RequestMapping("/getUserList2")
public String getUserList2() {
//直接使用getForObject,就不用使用getBody()方法再转一次
List userList = restTemplate.getForObject("http://service-b/getUserList", List.class);
return userList.toString();
}
/**
* 有参数情况下的getForObject
* 传参方式1: 使用占位符方式
*/
@RequestMapping("/getByUserId2")
public String getByUserId2(String id) {
//直接使用getForObject,就不用使用getBody()方法再转一次,直接就是User对象
User user = restTemplate.getForObject("http://service-b/getByUserId?id={id}", User.class, id);
return user.toString();
}
/**
* 有参数情况下的getForObject
* 传参方式1: 使用map方式
*/
@RequestMapping("/getByUserIdByMap2")
public String getByUserIdByMap2(String id) {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("id", id);
//直接使用getForObject,就不用使用getBody()方法再转一次,直接就是User对象
User user = restTemplate.getForObject("http://service-b/getByUserId?id={id}", User.class, paramMap);
return user.toString();
}
/*********************************************postForEntity****************************************************/
/**
* 无参数情况下的postForEntity
*/
@RequestMapping("/saveUser")
public String saveUser() {
User user = new User(UUID.randomUUID().toString(), "zhaoliu", "123");
ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity("http://service-b/saveUser", user, String.class);
return stringResponseEntity.getBody();
}
/**
* 有参数情况下的postForEntity
* 传参方式1: 使用占位符方式
*/
@RequestMapping("/saveUserAndType")
public String saveUserAndType(String type) {
User user = new User(UUID.randomUUID().toString(), "zhaoliu", "123");
ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity("http://service-b/saveUserAndType?type={type}", user, String.class, type);
return stringResponseEntity.getBody();
}
/**
* 有参数情况下的postForEntity
* 传参方式2: 使用map方式
*/
@RequestMapping("/saveUserAndTypeMap")
public String saveUserAndTypeMap(String type) {
User user = new User(UUID.randomUUID().toString(), "zhaoliu", "123");
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("type", type);
ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity("http://service-b/saveUserAndType?type={type}", user, String.class, paramMap);
return stringResponseEntity.getBody();
}
/*********************************************postForObject****************************************************/
/**
* 无参数情况下的postForObject
*/
@RequestMapping("/saveUser2")
public String saveUser2() {
User user = new User(UUID.randomUUID().toString(), "zhaoliu", "123");
return restTemplate.postForObject("http://service-b/saveUser", user, String.class);
}
/**
* 有参数情况下的postForObject
* 传参方式1: 使用占位符方式
*/
@RequestMapping("/saveUserAndType2")
public String saveUserAndType2(String type) {
User user = new User(UUID.randomUUID().toString(), "zhaoliu", "123");
return restTemplate.postForObject("http://service-b/saveUserAndType?type={type}", user, String.class, type);
}
/**
* 有参数情况下的postForObject
* 传参方式2: 使用map方式
*/
@RequestMapping("/saveUserAndTypeMap2")
public String saveUserAndTypeMap2(String type) {
User user = new User(UUID.randomUUID().toString(), "zhaoliu", "123");
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("type", type);
return restTemplate.postForObject("http://service-b/saveUserAndType?type={type}", user, String.class, paramMap);
}
/*********************************************post方式传参数注意点****************************************************/
@RequestMapping("/postByMap")
public String postByMap(String name) {
//使用post方式传递参数不能使用map,后台接收不到参数,要使用MultiValueMap
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("name", name);
return restTemplate.postForObject("http://service-b/postByMap", paramMap, String.class);
}
@RequestMapping("/postByMultiValueMap")
public String post(String name) {
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
paramMap.add("name", name);
return restTemplate.postForObject("http://service-b/postByMap", paramMap, String.class);
}
}
五、ServiceAController详解
主要通过restTemplate访问服务提供者B暴露的一些接口,下面通过一个个示例讲解RestTemplate相关操作方法。
- (1). getForEntity方法:getForEntity()方法返回ResponseEntity对象,通过需要调用ResponseEntity.getBody()获取我们需要的返回结果。下面讲解getForEntity的一些常用方法:
【a】无参数的getForEntity
第一个参数:请求的地址
第二个参数:返回结果类型
/**
* 无参数情况下的getForEntity
*/
@RequestMapping("/getUserList")
public String getUserList() {
ResponseEntity<List> responseEntity = restTemplate.getForEntity("http://service-b/getUserList", List.class);
List<User> list = responseEntity.getBody();
return list.toString();
}
浏览器访问: http://localhost:2222/getUserList
测试结果:
【b】无参数的getForEntity(占位符方式)
第一个参数:请求的地址
第二个参数:返回结果类型
第三个参数:传递的参数
/**
* 有参数情况下的getForEntity
* 传参方式1: 使用占位符方式
*/
@RequestMapping("/getByUserId")
public String getByUserId(String id) {
ResponseEntity<User> userResponseEntity = restTemplate.getForEntity("http://service-b/getByUserId?id={id}", User.class, id);
User userBody = userResponseEntity.getBody();
return userBody.toString();
}
浏览器访问: http://localhost:2222/getByUserId?id=1
测试结果:
【c】无参数的getForEntity(map方式)
第一个参数:请求的地址
第二个参数:返回结果类型
第三个参数:传递的参数
/**
* 有参数情况下的getForEntity
* 传参方式1: 使用map方式
*/
@RequestMapping("/getByUserIdByMap")
public String getByUserIdByMap(String id) {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("id", id);
ResponseEntity<User> userResponseEntity = restTemplate.getForEntity("http://service-b/getByUserId?id={id}", User.class, paramMap);
User userBody = userResponseEntity.getBody();
return userBody.toString();
}
浏览器访问: http://localhost:2222/getByUserIdByMap?id=2
测试结果:
- (2). getForObject方法:getForObject()方法直接返回我们需要的对象类型,下面讲解getForObject的一些常用方法:(建议使用getForObject)
【a】无参数的getForObject
第一个参数:请求的地址
第二个参数:返回结果的类型
/**
* 无参数情况下的getForObject
*/
@RequestMapping("/getUserList2")
public String getUserList2() {
//直接使用getForObject,就不用使用getBody()方法再转一次
List userList = restTemplate.getForObject("http://service-b/getUserList", List.class);
return userList.toString();
}
浏览器访问: http://localhost:2222/getUserList2
测试结果:
【b】有参数的getForObject(占位符方式)
第一个参数:请求的地址
第二个参数:返回结果的类型
第三个参数:传递的参数
/**
* 有参数情况下的getForObject
* 传参方式1: 使用占位符方式
*/
@RequestMapping("/getByUserId2")
public String getByUserId2(String id) {
//直接使用getForObject,就不用使用getBody()方法再转一次,直接就是User对象
User user = restTemplate.getForObject("http://service-b/getByUserId?id={id}", User.class, id);
return user.toString();
}
浏览器访问: http://localhost:2222/getByUserId2?id=0
测试结果:
【c】有参数的getForObject(map方式)
第一个参数:请求的地址
第二个参数:返回结果的类型
第三个参数:传递的参数
/**
* 有参数情况下的getForObject
* 传参方式1: 使用map方式
*/
@RequestMapping("/getByUserIdByMap2")
public String getByUserIdByMap2(String id) {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("id", id);
//直接使用getForObject,就不用使用getBody()方法再转一次,直接就是User对象
User user = restTemplate.getForObject("http://service-b/getByUserId?id={id}", User.class, paramMap);
return user.toString();
}
浏览器访问: http://localhost:2222/getByUserIdByMap2?id=1
测试结果:
- (3). postForEntity方法:postForEntity()方法返回ResponseEntity对象,通过需要调用ResponseEntity.getBody()获取我们需要的返回结果。下面讲解postForEntity的一些常用方法:
【a】无参数postForEntity
第一个参数:请求的地址
第二个参数:请求发送的参数
第三个参数:返回结果的类型
/**
* 无参数情况下的postForEntity
*/
@RequestMapping("/saveUser")
public String saveUser() {
User user = new User(UUID.randomUUID().toString(), "zhaoliu", "123");
ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity("http://service-b/saveUser", user, String.class);
return stringResponseEntity.getBody();
}
浏览器访问: http://localhost:2222/saveUser
测试结果:
【b】有参数的postForEntity(占位符方式)
第一个参数:请求的地址
第二个参数:请求发送的参数
第三个参数:返回结果的类型
第四个参数:请求传递的参数
/**
* 有参数情况下的postForEntity
* 传参方式1: 使用占位符方式
*/
@RequestMapping("/saveUserAndType")
public String saveUserAndType(String type) {
User user = new User(UUID.randomUUID().toString(), "zhaoliu", "123");
ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity("http://service-b/saveUserAndType?type={type}", user, String.class, type);
return stringResponseEntity.getBody();
}
浏览器访问: http://localhost:2222/saveUserAndType?type=add
测试结果:
【c】有参数的postForEntity(map方式)
第一个参数:请求的地址
第二个参数:请求发送的参数
第三个参数:返回结果的类型
第四个参数:请求传递的参数
/**
* 有参数情况下的postForEntity
* 传参方式2: 使用map方式
*/
@RequestMapping("/saveUserAndTypeMap")
public String saveUserAndTypeMap(String type) {
User user = new User(UUID.randomUUID().toString(), "zhaoliu", "123");
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("type", type);
ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity("http://service-b/saveUserAndType?type={type}", user, String.class, paramMap);
return stringResponseEntity.getBody();
}
浏览器访问: http://localhost:2222/saveUserAndTypeMap?type=update
测试结果:
- (4). postForObject方法:postForObject()方法直接返回结果。下面讲解postForObject的一些常用方法:
【a】无参数postForObject
第一个参数:请求的地址
第二个参数:请求体
第三个参数:返回结果的类型
/**
* 无参数情况下的postForObject
*/
@RequestMapping("/saveUser2")
public String saveUser2() {
User user = new User(UUID.randomUUID().toString(), "zhaoliu", "123");
return restTemplate.postForObject("http://service-b/saveUser", user, String.class);
}
浏览器访问: http://localhost:2222/saveUser2
测试结果:
【b】有参数postForObject(占位符)
第一个参数:请求的地址
第二个参数:请求体
第三个参数:返回结果的类型
第四个参数:请求传递的参数
/**
* 有参数情况下的postForObject
* 传参方式1: 使用占位符方式
*/
@RequestMapping("/saveUserAndType2")
public String saveUserAndType2(String type) {
User user = new User(UUID.randomUUID().toString(), "zhaoliu", "123");
return restTemplate.postForObject("http://service-b/saveUserAndType?type={type}", user, String.class, type);
}
浏览器访问: http://localhost:2222/saveUserAndType2?type=save
测试结果:
【c】有参数postForObject(map)
第一个参数:请求的地址
第二个参数:请求体
第三个参数:返回结果的类型
第四个参数:请求传递的参数
/**
* 有参数情况下的postForObject
* 传参方式2: 使用map方式
*/
@RequestMapping("/saveUserAndTypeMap2")
public String saveUserAndTypeMap2(String type) {
User user = new User(UUID.randomUUID().toString(), "zhaoliu", "123");
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("type", type);
return restTemplate.postForObject("http://service-b/saveUserAndType?type={type}", user, String.class, paramMap);
}
浏览器访问: http://localhost:2222/saveUserAndTypeMap2?type=save
测试结果:
六、注意点:
我们在使用post方式传递参数的时候不能使用hashmap的方式进行传参,否则接口接收到的值为空,这个时候我们应该使用MultiValueMap进行传参,接口就能正常获取到参数值。具体示例如下:
@RequestMapping("/postByMap")
public String postByMap(String name) {
//使用post方式传递参数不能使用map,后台接收不到参数,要使用MultiValueMap
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("name", name);
return restTemplate.postForObject("http://service-b/postByMap", paramMap, String.class);
}
@RequestMapping("/postByMultiValueMap")
public String post(String name) {
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
paramMap.add("name", name);
return restTemplate.postForObject("http://service-b/postByMap", paramMap, String.class);
}
【a】HashMap方式:
浏览器访问: http://localhost:2222/postByMap
使用hashmap方式测试结果:
由此可见,POST方式使用hashmap方式传递参数接收到的值为null。
【b】MultiValueMap方式:
浏览器访问: http://localhost:2222/postByMultiValueMap?name=weishihuai
使用multiValueMap方式测试结果:
七、总结
至此,我们对RestTemplate中常用的getForObjcet/getForEntity/postForObject/postForEntity进行了讲解,并通过简单的示例说明了其用法,同时还讲到了一个注意点。本文是作者在学习RestTemplate时的一些总结,仅供大家学习参考,一起学习一起进步。