转自:https://blog.csdn.net/jinjiniao1/article/details/100849237
本文归纳自 慕课网 江南一点雨 老师的学习笔记,大家可以购买他的课程学习。
侵权请联系作者删除
GET请求
API:getForEntity 与 getForObject
相同:可传递参数
区别:返回值类型
-
getForEntity:返回ResponseEntity实例,内部包含响应数据与响应头
-
getForObject:返回响应数据
getForEntity的使用
provider提供接口
@RestController
public class HelloController {
@GetMapping("/hello")
public String sayHello(String name) {
return "hello " + name + " !";
}
}
consumer定义一个接口,调用provider的服务
@RestController
public class UseHelloController {
@Autowired
DiscoveryClient discoveryClient;
@Autowired
RestTemplate restTemplate;
@GetMapping("/hello")
public String hello(String name) {
List<ServiceInstance> list = discoveryClient.getInstances("provider");
ServiceInstance instance = list.get(0);
String host = instance.getHost();
int port = instance.getPort();
String url = "http://" + host + ":" + port + "/hello?name={1}";
ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class, name);
StringBuffer sb = new StringBuffer();
HttpStatus statusCode = responseEntity.getStatusCode();
String body = responseEntity.getBody();
sb.append("statusCode:")
.append(statusCode)
.append("</br>")
.append("body:")
.append(body)
.append("</br>");
// 获取响应头信息
HttpHeaders headers = responseEntity.getHeaders();
Set<String> keySet = headers.keySet();
for (String s : keySet) {
sb.append(s)
.append(":")
.append(headers.get(s))
.append("</br>");
}
return sb.toString();
}
}
重点关注:
1、getForEntity()的三个参数
参数一:url,含有占位符{1}
参数二:接口返回的数据类型
参数三:可变长度参数,为占位符填充值
2、获取响应头信息
responseEntity.getHeaders()
3、其他
// 获取响应状态码
HttpStatus statusCode = responseEntity.getStatusCode();
// 获取响应数据
String body = responseEntity.getBody();
补充:getForEntity()传递占位符参数的另外两种方式
1、Map传递
map中的key和占位符的名称对应,value就是要填充的值
Map<String, Object> map = new HashMap<>();
String url = "http://" + host + ":" + port + "/hello?name={name}";
map.put("name", name);
ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class, map);
2、Uri对象传递
将参数直接拼接到地址中,中文参数需要编码
String url = "http://" + host + ":" + port + "/hello?name="
+ URLEncoder.encode(name,"UTF-8");
URI uri = URI.create(url);
ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class);
getForObject的使用
和getForEntity类似,可传递参数相同,只是返回值类型不同
getForObject 返回值就是 服务提供者返回的数据,没有响应头
String url = "http://" + host + ":" + port + "/hello?name=" + URLEncoder.encode(name, "UTF-8");
URI uri = URI.create(url);
String s = restTemplate.getForObject(uri, String.class);
POST请求
API:postForEntity(),postForObject (),postForLocation()
相同:可传递参数
区别:返回值类型
postForEntity的使用
在 POST 请求中,参数的传递可以是 key/value 的形式,也可以是 JSON 数据,分别来看:
1、传递 key/value 形式的参数
provider添加接口
@PostMapping("/hello2")
public String sayHello2(String name) {
return "Hello " + name + " !";
}
consumer调用
@GetMapping("/hello5")
public String hello5(String name) {
List<ServiceInstance> list = discoveryClient.getInstances("provider");
ServiceInstance instance = list.get(0);
String host = instance.getHost();
int port = instance.getPort();
String url = "http://" + host + ":" + port + "/hello2";
MultiValueMap map = new LinkedMultiValueMap();
map.add("name", name);
ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, map, String.class);
return responseEntity.getBody();
}
postForEntity的参数
参数一:请求地址
参数二:存放请求参数的map对象
参数三:返回的数据类型
第一个参数 url 地址也可以换成一个 Uri 对象,效果是一样的。
这种方式传递的参数是以 key/value 形式传递的,
在 post 请求中,可以按照 get 请求的方式去传递 key/value 形式的参数,
传递方式和 get 请求的传参方式基本一致,例如下面这样:
@GetMapping("/hello6")
public String hello6(String name) {
List<ServiceInstance> list = discoveryClient.getInstances("provider");
ServiceInstance instance = list.get(0);
String host = instance.getHost();
int port = instance.getPort();
String url = "http://" + host + ":" + port + "/hello2?name={1}";
// 此时第二个参数可以直接传一个 null。
ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, null, String.class,name);
return responseEntity.getBody();
}
2、传递 JSON 数据
上面介绍的是 post 请求传递 key/value 形式的参数,post 请求也可以直接传递 json 数据,在 post 请求中,可以自动将一个对象转换成 json 进行传输,数据到达 provider 之后,再被转换为一个对象。具体操作步骤如下:
provider添加接口
@Controller
@ResponseBody
public class UserController {
@PostMapping("/user")
public User hello(@RequestBody User user) {
return user;
}
}
consumer调用
@GetMapping("/hello7")
public User hello7() {
List<ServiceInstance> list = discoveryClient.getInstances("provider");
ServiceInstance instance = list.get(0);
String host = instance.getHost();
int port = instance.getPort();
String url = "http://" + host + ":" + port + "/user";
User u1 = new User();
u1.setUsername("牧码小子");
u1.setAddress("深圳");
ResponseEntity<User> responseEntity = restTemplate.postForEntity(url, u1, User.class);
return responseEntity.getBody();
}
和前面的一样,唯一的区别就是第二个参数的类型不同
参数如果是一个 MultiValueMap 的实例,则以 key/value 的形式发送
参数如果是一个 普通对象,则会被转成 json 发送
postForObject的使用
postForObject 和 postForEntity 基本一致,就是返回类型不同而已
之间的区别和 getForEntity跟getForObject的区别 相同
postForLocation的使用
postForLocation 方法的返回值是一个 Uri 对象,因为 POST 请求一般用来添加数据,有的时候需要将刚刚添加成功的数据的 URL 返回来,此时就可以使用这个方法
常见的使用场景如用户注册功能,用户注册成功之后,自动跳转到登录页面。此时就可以使用该方法。
在 provider 中提供一个用户注册接口,再提供一个用户登录接口,如下:
// 注册接口
@RequestMapping("/register")
public String register(User user) throws UnsupportedEncodingException {
return "redirect:/loginPage?username=" + URLEncoder.encode(user.getUsername(),"UTF-8") + "&address=" + URLEncoder.encode(user.getAddress(),"UTF-8");
}
// 登录页面
@GetMapping("/loginPage")
@ResponseBody
public String loginPage(User user) {
return "loginPage:" + user.getUsername() + ":" + user.getAddress();
}
然后在 consumer 中来调用注册接口,如下:
@GetMapping("/hello8")
public String hello8() {
List<ServiceInstance> list = discoveryClient.getInstances("provider");
ServiceInstance instance = list.get(0);
String host = instance.getHost();
int port = instance.getPort();
String url = "http://" + host + ":" + port + "/register";
MultiValueMap map = new LinkedMultiValueMap();
map.add("username", "牧码小子");
map.add("address", "深圳");
URI uri = restTemplate.postForLocation(url, map);
String s = restTemplate.getForObject(uri, String.class);
return s;
}
这里首先调用 postForLocation 获取 Uri 地址,然后再利用 getForObject 请求 Uri
注意:postForLocation 方法返回的 Uri 实际上是指响应头的 Location 字段,所以,provider 中 register 接口的响应头必须要有 Location 字段(即请求的接口实际上是一个重定向的接口),否则 postForLocation 方法的返回值为null,初学者很容易犯这个错误,如果这里出错,大家可以参考下老师的源代码。
PUT 请求
PUT 请求本身方法只有三个,如下:
这三个重载的方法的参数和 POST 是一样的,可以用 key/value 的形式传参,也可以用 JSON 的形式传参,无论哪种方式,都是没有返回值的,
举两个例子:
provider添加两个数据更新接口:
@PutMapping("/user/name")
@ResponseBody
public void updateUserByUsername(User User) {
System.out.println(User);
}
@PutMapping("/user/address")
@ResponseBody
public void updateUserByAddress(@RequestBody User User) {
System.out.println(User);
}
这里两个接口,一个接收 key/value 形式的参数,另一个接收 JSON 参数。
因为这里没有返回值,直接把数据打印出来了。
consumer 中添加接口调用这里的服务,如下:
@GetMapping("/hello9")
public void hello9() {
List<ServiceInstance> list = discoveryClient.getInstances("provider");
ServiceInstance instance = list.get(0);
String host = instance.getHost();
int port = instance.getPort();
String url1 = "http://" + host + ":" + port + "/user/name";
String url2 = "http://" + host + ":" + port + "/user/address";
MultiValueMap map = new LinkedMultiValueMap();
map.add("username", "牧码小子");
map.add("address", "深圳");
restTemplate.put(url1, map);
User u1 = new User();
u1.setAddress("广州");
u1.setUsername("江南一点雨");
restTemplate.put(url2, u1);
}
访问 /hello9
接口,即可看到 provider 上有日志打印出来,这里比较简单,老师没有演示。
DELETE 请求
DELETE 请求也是只有三个方法,如下:
不同于 POST 和 PUT ,DELETE 请求的参数只能在地址栏传送,可以是直接放在路径中,也可以用 key/value 的形式传递,当然,这里也是没有返回值的。
老师举的两个例子:
provider 的 UserController 中添加两个接口,如下:
@DeleteMapping("/user/{id}")
@ResponseBody
public void deleteUserById(@PathVariable Integer id) {
System.out.println(id);
}
@DeleteMapping("/user/")
@ResponseBody
public void deleteUserByUsername(String username) {
System.out.println(username);
}
两个接口,一个的参数在路径中,另一个的参数以 key/value 的形式传递
在 consumer 中添加一个方法调用这两个接口,如下:
@GetMapping("/hello10")
public void hello10() {
List<ServiceInstance> list = discoveryClient.getInstances("provider");
ServiceInstance instance = list.get(0);
String host = instance.getHost();
int port = instance.getPort();
String url1 = "http://" + host + ":" + port + "/user/{1}";
String url2 = "http://" + host + ":" + port + "/user/?username={username}";
Map<String,String> map = new HashMap<>();
map.put("username", "牧码小子");
restTemplate.delete(url1, 99);
restTemplate.delete(url2, map);
}
这里参数的传递和 GET 请求基本一致,老师没有赘述
其他
设置请求头
有时会有一些特殊的需求,例如模拟 cookie ,此时就需要自定义请求头了。
自定义请求头可以通过拦截器的方式来实现(下篇文章老师会详细的说这个拦截器)。
定义拦截器、自动修改请求数据、一些身份认证信息等,都可以在拦截器中来统一处理。
具体操作步骤如下:
首先,provider 中定义一个接口,在接口中获取客户端传来的 cookie 数据,如下:
@GetMapping("/customheader")
public String customHeader(HttpServletRequest req) {
return req.getHeader("cookie");
}
这里简单处理,将客户端传来的 cookie 拿出来后再返回给客户端
然后,在 consumer 中添加如下接口来测试:
@GetMapping("/hello11")
public void hello11() {
List<ServiceInstance> list = discoveryClient.getInstances("provider");
ServiceInstance instance = list.get(0);
String host = instance.getHost();
int port = instance.getPort();
String url = "http://" + host + ":" + port + "/customheader";
restTemplate.setInterceptors(Collections.singletonList(new ClientHttpRequestInterceptor() {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
HttpHeaders headers = request.getHeaders();
headers.add("cookie","justdojava");
return execution.execute(request,body);
}
}));
String s = restTemplate.getForObject(url, String.class);
System.out.println(s);
}
这里通过调用 RestTemplate 的 setInterceptors 方法来给它设置拦截器,拦截器也可以有多个,老师这里只有一个。在拦截器中,将请求拿出来,给它设置 cookie ,然后调用 execute 方法让请求继续执行。此时,在 /customheader
接口中,就能获取到 cookie了。
通用方法 exchange
在 RestTemplate 中还有一个通用的方法 exchange。为什么说它通用呢?因为这个方法需要在调用的时候去指定请求类型,即它既能做 GET 请求,也能做 POST 请求,也能做其它各种类型的请求。如果开发者需要对请求进行封装,使用它再合适不过了,举个简单例子:
@GetMapping("/hello12")
public void hello12() {
List<ServiceInstance> list = discoveryClient.getInstances("provider");
ServiceInstance instance = list.get(0);
String host = instance.getHost();
int port = instance.getPort();
String url = "http://" + host + ":" + port + "/customheader";
HttpHeaders headers = new HttpHeaders();
headers.add("cookie","justdojava");
HttpEntity<MultiValueMap<String,String>> request = new HttpEntity<>(null,headers);
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, request, String.class);
System.out.println(responseEntity.getBody());
}
这里的参数和前面的也都差不多,注意就是多了一个请求类型的参数,然后创建一个 HttpEntity 作为参数来传递。 HttpEntity 在创建时候需要传递两个参数,第一个参数上文给了一个 null ,这个参数实际上就相当于 POST/PUT 请求中的第二个参数,有需要可以自行定义。HttpEntity 创建时的第二个参数就是请求头了,也就是说,如果使用 exchange 来发送请求,可以直接定义请求头,而不需要使用拦截器。