spring cloud 做微服务时关于RestTemplate中的各种请求方法的使用总结
spring cloud 做微服务时关于RestTemplate的使用总结先看看RestTemplate类的Outline:最上面的成员和下面的几个构造方法可以先大概看一下。就是选择性的去构造消息转换器,用来接收和传递相应的类型的HTTP请求数据。下面的是重点,各种HTTP请求如下:GET请求的有:先看前端3个getForObject方法。重载的3个方法,其区别就是传参...
spring cloud 做微服务时关于RestTemplate的使用总结
先看看RestTemplate类的Outline:
最上面的成员和下面的几个构造方法可以先大概看一下。就是选择性的去构造消息转换器,用来接收和传递相应的类型的HTTP请求数据。
下面的是重点,各种HTTP请求如下:
GET请求的有:
先看前端3个getForObject方法。重载的3个方法,其区别就是传参数不一样,返回的内容都是一样的。我们可以看一下这3个方法的接口定义(RestTemplate类是实现了RestOperations接口)可以看到返回的是一样的,都是 返回 the converted object。
对于get方法,如果有请求参数的话,一般是2种传参数的方式:
1、参数是url路径上,如:http://www.xxx.com/testAccept/{id}/kid/{name}。此时使用restTemplate.getForObject可以写成
String url = "http://www.xxx.com/testAccept/{id}/kid/{name}";
Map<String, Object> resultMap = restTemplate.getForObject(url, Map.class,"123456","xiao ming");
调用的是<T> T getForObject(String url, Class<T> responseType, Object... uriVariables)
方法。
接收http请求的地方写成:
@GetMapping(value = "/testAccept/{id}/kid/{name}")
public Map<String, Object> testAccept(@PathVariable String id,@PathVariable String name){
Map<String, Object> ret = Maps.newHashMap();
ret.put("id", id);
ret.put("name", name);
return ret;
}
@PathVariable注解表示2个参数都是url路径上的参数。
2、参数跟在url路径后面,如http://www.xxx.com/testAccept/kid?id={id}&name={name}。此时使用restTemplate.getForObject可以写成:
Map<String, Object> requestMap = Maps.newHashMap();
requestMap.put("id", "123456");
requestMap.put("name", "xiao ming");
String url = "http://www.xxx.com/testAccept/kid?id={id}&name={name}";
Map<String, Object> resultMap = restTemplate.getForObject(url, Map.class,requestMap);
调用的是<T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables)
方法。
接收http请求的地方写成:
@GetMapping(value = "/testAccept/kid")
public Map<String, Object> testAccept(@RequestParam String id,@RequestParam String name){
Map<String, Object> ret = Maps.newHashMap();
ret.put("id", id);
ret.put("name", name);
return ret;
}
@RequestParam注解表示2个都是请求的参数。
对于不带参数的get请求,三种getForObject方法都可以调用:
String url = "http://www.xxx.com/testAccept/kid";
Map<String, Object> resultMap = restTemplate.getForObject(url, Map.class);
Map<String, Object> resultMap1 = restTemplate.getForObject(url, Map.class);
URI uri = URI.create(url);
Map<String, Object> resultMap2 = restTemplate.getForObject(uri, Map.class);
区别就是后面一个getForObject方法第一个参数传的是个uri,前面2个传的都是String。简单的将String转成URI也是一样。
注意:第三个方法虽然少一个接收参数,但是不代表这个方法不能传参数,因为第一个参数是个URI是要自己去构造的。
然后是3个getForEntity方法,这三个重载的方法与上面3个getForObject方法大同小异,我们可以看一下这3个方法的接口定义,与上面的一样:
getForEntity与getForObject最大的区别就在于返回内容不一样:
getForEntity返回的是一个ResponseEntity,而getForObject返回的就只是返回内容。getForObject的返回相当于只返回http的body部份而getForEntity的返回是返回全部信息:
一般来说一是取status判断请求是否成功,成功则去取body里面的内容。
getForEntity请求参数的写法和接收参数的写法与getForObject是一样的,不赘述。
HEADER请求的有:
接下来是headForHeaders的几个方法,取head里面的信息,head里面的信息可以参看上面的那个图,都是一些基础数据,一般情况不会去主动发head请求。略过。
POST请求的有:
再接下来是POST请求。POST请求的9个方法,3个一组,postForLocation、postForObject、postForEntity:
看一看这些方法的接口定义:
首先是三个postForLocation,注意这三个方法返回的是一个URI
。看看接口定义:
三个接口都通过给定的数据发送一个post请求来创建资源,然后返回一个HTTP 头。三个接口可以用不同的方式去给URI传参数,与上面的传参方法一样。每个接口都有这样一句解释:The {@code request} parameter can be a {@link HttpEntity} in order to add additional HTTP headers to the request.
解释了参数Object request的通常用法。如果想要在HTTP header里面加点什么东西(比如编码方式等)可以在此处传一个HttpEntity
对象。如:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8); // 在header里面设置编码方式
String body = "xiao ming";
HttpEntity<String> requestEntity = new HttpEntity<String>(body , headers);
restTemplate.postForLocation(URI.create("http://www.xxx.com/test"), requestEntity);
当然,不需要传HttpEntity对象的时候一样可以传其他对象。
再看这三个方法的实现:
都是解析不同的参数,然后调用RestTemplate的execute方法,返回一个HttpHeaders,然后取其中的Location返回。Location字段一般用于重定向接受者到一个新的位置。例如:客户端所请求的页面已不存在原先的位置,为了让客户端重定向到这个页面新的位置,服务 器端可以发回Location响应报头后使用重定向语句,让客户端去访问新的域名所对应的服务器上的资源。当我们在使用重定向语句(如HttpServletResponse的sendRedirect方法)的时候,服务器 端向客户端发回的响应报头中,就会有Location响应报头域。顺便看一下HttpHeaders中的getLocation方法:
就是将重定向的那个新的位置转成一个URI地址,没有重定向就返回一个null。
接下来是三个postForObject方法,接口定义:
与上面的post请求一样,都是psot来创建资源,请求参数与上面的三个接口也是类似的,唯一一个不同就是多一个指定HTTP请求的返回的数据类型(这一点与getForObject是一样的)。返回的是一个泛型,具体类型根据请求参数Class<T> responseType
来确定。
postForObject方法的重点来了:
对于post方法来说,请求参数可以放到请求url里面(uriVariables),也可以放到http的body里面,当然一般来说post的数据放到body里面比较正规,也比较好,因为这样数据相对不会暴露。但是有些比较简单的无关紧要的数据放到url里面传也不是不可以。所以下面重点说一下postForObject的传参方式:
1. 参数放在url里面
调用` <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables) throws RestClientException;`方法。
Map<String, Object> object = restTemplate.postForObject("http://www.xxx.com/testSend?name={name}&gender={gender}", null, Map.class,"xiao ming",1);
放在uriVariables里面传参数,与getForObject其实是类似的,一定要将指定参数,如此处的name和gender。因为body里面不传值,所以此处的第二个参数传个null,这个null不能省略。
接收http请求的地方:
@PostMapping(value = "/testAccept")
public Map<String, Object> testAccept(@RequestParam String name,@RequestParam int gender){
Map<String, Object> ret = Maps.newHashMap();
User user= new User ();
user.setName(name);
user.setGender(gender);
userService.save(user);
ret.put("user", user);
return ret;
}
或者参数放在url路径上,如:
Map<String, Object> object = restTemplate.postForObject("http://www.xxx.com/testSend/{name}/gender/{gender}", null, Map.class,"xiao ming",1);
接收http请求的地方:
@PostMapping(value = "/testSend/{name}/gender/{gender}")
public Map<String, Object> testAccept(@PathVariable String name,@PathVariable int gender){
Map<String, Object> ret = Maps.newHashMap();
User user= new User ();
user.setName(name);
user.setGender(gender);
userService.save(user);
ret.put("user", user);
return ret;
}
注意两点, 一是请求的url的地方加占位,二是参数的注解是@PathVariable
,表示是路径上的参数。
调用<T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;
方法。
与调用上一个方法其实是一样的,唯一的区别就是上面的参数是直接一个个列出来,面这个方法是将参数封到一个map里面,注意,map里面的key一定要与参数里面的占位参数一致。如上面2种参数的写法,如果调用这个方法就是:
Map<String,Object> requestMap = Maps.newHashMap();
requestMap.put("name", "xiao ming");
requestMap.put("gender", 1);
Map<String, Object> object = restTemplate.postForObject("http://www.xxx.com/testSend?name={name}&gender={gender}", null, Map.class,requestMap);
或者参数在路径上:
Map<String,Object> requestMap = Maps.newHashMap();
requestMap.put("name", "xiao ming");
requestMap.put("gender", 1);
Map<String, Object> object = restTemplate.postForObject("http://www.xxx.com/testSend/{name}/gender/{gender}", null, Map.class,requestMap);
对于这两种写法,在接收HTTP请求的地方写法与上面对应的2种写法是一样的。
2.参数只放在body里面
参数只放在body里面的话,三个postForObject方法就没有多大区别了:
第一个和第二个一样了,与第三个的区别就是一个是String的url一个是url,区别不计。看写法:
HttpHeaders headers = new HttpHeaders(); // http请求头
headers.setContentType(MediaType.APPLICATION_JSON_UTF8); // 请求头设置属性
Map<String,Object> body = Maps.newHashMap(); // 请求body
body.put("name", "xiao ming");
body.put("gender", 1);
HttpEntity<Map<String,Object>> requestEntity = new HttpEntity<Map<String,Object>>(body,headers);
Map<String, Object> object = restTemplate.postForObject("http://www.xxx.com/testSend", requestEntity, Map.class);
注意点我们看HttpEntity类的构造方法:
4个构造方法可以选择要或不要headers和body,花式构造HttpEntity。且body参数是泛型,可以传各种类型的body。
在接收此HTTP请求的地方,只需要用@RequestBody来接收参数即可:
@PostMapping(value = "/testSend")
public Map<String, Object> testAccept(@RequestBody Map<String, Object> request){
Map<String, Object> ret = Maps.newHashMap();
User user= new User();
user.setName((String)request.getOrDefault("name", null));
user.setGender((int)request.getOrDefault("gender", 0));
userService.save(user);
ret.put("user", user);
return ret;
}
注意:@RequestBody
注解的接收参数的类型要与请求的HttpEntity<T>
的T的类型一致。上面的例子都是用的Map<String,Object>
。
3.混合传值url和body一个地方放一点参数。
直接上例子:
HttpHeaders headers = new HttpHeaders(); // http请求头
headers.setContentType(MediaType.APPLICATION_JSON_UTF8); // 请求头设置属性
HttpEntity<Integer> requestEntity = new HttpEntity<Integer>(1,headers);
Map<String, Object> object = restTemplate.postForObject("http://www.xxx.com/testSend?name={name}", requestEntity, Map.class,"xiao ming");
接收HTTP请求的地方 :
@PostMapping(value = "/testSend")
public Map<String, Object> testAccept(@RequestBody int request,@RequestParam String name){
Map<String, Object> ret = Maps.newHashMap();
User user= new User();
user.setGender(request);
user.setName(name);
userService.save(user);
ret.put("user", user);
return ret;
}
一个参数从url上取,一个参数从httpEntity的body里面取,是不是感觉怪怪的?but这样写一样可以取到值。但是不建议这样传值。
此外,postForObject方法的返回类型也是在发请求时自己定义的(方法的Class<T> responseType
参数),一般来说用Map的比较多。
再接下来是三个postForEntity方法,接口定义:
与上面的postForObject的传参方法是一模一样的,唯一不同的就是postForObject返回的是自己指定的数据,相当于只返回body,而postForEntity返回的是一个ResponseEntity对象,对象包括了http响应的headers、body等信息。
至于写法,和postForEntity一毛一样,不再写了。至此post方法的写完了
PUT、PATCH、DELETE、OPTIONS请求的有:
put、patch、delete请求比较简单,就放在一起了。
先看put的接口定义:
通过put方法用给定的对象来创建或者更新一个资源,请求参数与postForObject也是一毛一样,写法也是一样,不多说了,只说一句这3个put方法都是无返回,所以建议不用于创建操作。
还有一点要说明的是PUT操作是幂等的。关于幂等性,定义是:Methods can also have the property of "idempotence" in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.
说白了就是不管你请求多少次结果都是一样的。
再是patch的接口定义:
通过patch方法用给定的对象来更新一个资源并返回。与上面的方法一样,参数和写法都是一样的,不用多说了。强调一下的是patch操作不是幂等的,所以不能反复的去调用此方法来做某一个更新,另外patch方法与put方法做更新的区别需要说明一下:put是将对象丢过去,没有就创建,有就全体更新,面patch是只将对象的一个部份丢过去,并更新丢过去的那一部份。patch方法目前用的不多。
再一个是delete的方法了,看接口定义:
感觉delete的没什么说的,与上面的一样,3种传参方式去删除指定的URL上的资源。
最后再提一嘴OPTIONS请求。
OPTIONS 方法请求 Web 服务器告知其支持的各种功能。可以询问服务器通常支持哪些方法,或者对某些特殊资源支持哪些方法。因为有些服务器可能只支持对一些特殊类型的对象使用特定的操作。
看例子:
Set<HttpMethod> allow = restTemplate.optionsForAllow("http://www.xxx.com/testSend");
方法返回服务器支持的方法。这几个方法一般不会主动去调用,大概知道用处就行。
下面要重点说看一下exchange的系列方法了
exchange的请求的有:
总共有8个exchange方法!!!所有的方法全部都是返回的ResponseEntity<T>
,与前面的一样,就不多说了。
先看前面3个exchange方法
感觉与前面的postForEntity
和getForEntity
很类似,是吧。对比一下:
// GET
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)
// POST
<T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)
// EXCHANGE
<T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,Class<T> responseType, Object... uriVariables)
可以看出来相同点:
1、返回类型都是ResponseEntity<T>
;
2、第一个参数都是String类型的url;
3、最后的2个参数都是Class<T> responseType, Object... uriVariables
分别是返回值的具体类型和可变参数。
不同点:
1、对于post请求,中间可以多传一下Object request
;
2、对于ecchange方法,此方法的http请求类型要自己去指定HttpMethod method
,且可以多传一个HttpEntity
。
结论:1、exchange方法可以当get请求发也可以当post请求来发,甚至可以当其他的(put、patch等)请求来发。HttpMethod是一个枚举,如下所示,可以看到exchange方法可以发常见的8种http请求。例如get请求:HttpMethod.GET
。
2、exchange的传参方式除了指定HttpMethod之外,其他参数传递与getForEntity、postForEntity基本是一样的。如:
ResponseEntity<Map> exchange = restTemplate.exchange("http://www.xxx.com/testSend", HttpMethod.GET, null, Map.class, 90,2019);
再看中间的3个exchange方法:
可以看到中间的3个方法与前面的3个方法唯一不相同的就是指定返回类型了,前面3个方法都是Class<T> responseType
而此处的3个方法都是ParameterizedTypeReference<T> responseType
,其他的地方完全没有区别。这样的话我们就来看看这个ParameterizedTypeReference<T>
是个什么东西(将光标放到类上面,然后按F2键):
该类的目的是启用泛型类型的捕获和传递。为了捕获泛型类型并在运行时保留它,您需要创建一个子类如下:
ParameterizedTypeReference<List<String>> typeRef = new ParameterizedTypeReference<List<String>>(){};
顺便说一下ParameterizedTypeReference<T>
是一个抽象类,另外这个类根据名字我们其实可以大概可以猜到他的作用:参数化类型引用。于是我们可以这样写:
ParameterizedTypeReference<List<UserVO>> myBean = new ParameterizedTypeReference<List<UserVO>>() {};
ResponseEntity<List<UserVO>> exchange = restTemplate.exchange("http://www.xxx.com/testSend", HttpMethod.GET, null, myBean, 90,2019);
当要查询一个List类型的bean对象的时候,之前的Class<T> responseType
类型的返回就有点力不从心了,因为我们在使用Class<T> responseType
的时候只能定义到List层面,不知道List里面的是什么结构。使用ResponseEntity<List<UserVO>>
后,我们可以在接收请求处定义一个与请求的bean数据结构一模一样的一个VO(value object),例如此处是查询一个User
的List,我们在发送请求处定义一个与User
数据结构一模一样的UserVO
来接收List<User>
,这样结果就更好处理。
最后剩下的2个exchange方法:
可以看到最后这2个exchange接口都只传2个参数,第一个都是RequestEntity<?> requestEntity
,第二个参数是Class<T> responseType
或ParameterizedTypeReference<T> responseType
。第二个参数的区别与作用和前面的2组exchange方法的参数一样不用多说了。而第一个参数,我们看他的说明:@param requestEntity the entity to write to the request
是一个Http请求的entity。
看到RequestEntity的几个成员:
private final HttpMethod method;
private final URI url;
private final Type type;
和他的这几个构造方法就知道,其实这里的RequestEntity<?>
实际上就是将前面的几个exchange方法中的url、method、responseType等几个参数封装起来了,本质上还是一样的。
至此RestTemplate
中的Http请求方法的使用就都介绍完了。
下面将这些方法大概的做一个总结,总结就根据实际需求来分类,分为4类查询,新增、修改、删除:
约定:所有的方法中只对url是String类型的方法做分类和总结,因为一个是请求过程中的url常见的都是String,二是String类型的url可以手动的转在URI.所以所有的URI为参数的方法都当重复方法,不再做分类和总结。
- 一、查询
对于查询方法,一般来说都是用GET方法,在RestTamplate中,可以用多个方法发Http的Get请求,
请求方法 | 请求参数 | 返回类型 | 注意事项这里写代码片 |
---|---|---|---|
getForObject | 请求url:url ;指定返回类型:Class<T> ;最后的就是url上的参数,可以用可变参数传多个,也可以将多个参数放到Map中传。这个参数可以传空 | 由请求参数Class<T> 决定,只返回Http的Body的内容 | 当返回数据内容不多,且不关心响应状态、请求头等信息的时候可以用此方法,请求和返回都比较简单明了。 |
getForEntity | 同上 | 返回一个由请求参数Class<T> responseType 决定的ResponseEntity<T> ,包含了响应的headers、body、status等信息 | 当返回数据结构比较单一、且关心响应状态或响应头信息的时候可以用此方法 |
exchange | 请求url:String url ;指定Http请求类型HttpMethod ;请求体HttpEntity<?> 此处的get请求一般只传HttpEntity 里面的HttpHeaders ;指定返回类型:Class<T> 或ParameterizedTypeReference<T> ;最后一个是url参数,同上 | 返回一个由请求参数Class<T> responseType 或ParameterizedTypeReference<T> responseType 决定的ResponseEntity<T> ,包含了响应的headers、body、status等信息 | 当返回数据结构相对复杂(常见的如List<Bean> 或等)且关心响应状态或响应头信息的时候可以用此方法,且返回的泛型用ParameterizedTypeReference<T> 来指定 |
重点注意,不论是那个请求方式,都不能在可变参数上传数组,如果可变参数传数组restTemplate会认为数组中的每一个值分别是一个参数。
- 二、新增
请求方法 | 请求参数 | 返回类型 | 注意事项 |
---|---|---|---|
postForObject | 请求url:url ;请求对象:Object ,可以传空,常见的传一个HttpEntity 对象;指定返回类型:Class<T> ;最后的就是url上的参数,可以用可变参数传多个,也可以将多个参数放到Map中传。这个参数可以传空 | 由请求参数Class<T> 决定,只返回Http的Body的内容 | 当返回数据内容不多,且不关心响应状态、请求头等信息的时候可以用此方法,请求和返回都比较简单明了。 |
postForEntity | 请求参数同上 | 返回一个ResponseEntity<T> 对象,泛型T由讲求参数Class<T> 决定 | 当返回数据结构比较单一、且关心响应状态或响应头信息的时候可以用此方法 |
put | 请求url:url ;请求对象:Object ,可以传空,常见的传一个HttpEntity 对象;最后的就是url上的参数,可以用可变参数传多个,也可以将多个参数放到Map中传。这个参数可以传空 | 无返回 | PUT操作是幂等的,所以用来做更新操作非常合适,当然也可以做新增操作 |
exchange | 请求url:String url ;指定Http请求类型HttpMethod ;请求体HttpEntity<?> 此处的post请求HttpEntity 里面的HttpHeaders 放请求头信息body 放请求体信息;指定返回类型:Class<T> 或ParameterizedTypeReference<T> ;最后一个是url参数,同上 | 返回一个由请求参数Class<T> responseType 或ParameterizedTypeReference<T> responseType 决定的ResponseEntity<T> ,包含了响应的headers、body、status等信息 | 当返回数据结构相对复杂(常见的如List<Bean> 或等)且关心响应状态或响应头信息的时候可以用此方法,且返回的泛型用ParameterizedTypeReference<T> 来指定 |
- 三、修改
请求方法 | 请求参数 | 返回类型 | 注意事项这里写代码片 |
---|---|---|---|
put | 请求url:url ;请求对象:Object ,可以传空,常见的传一个HttpEntity 对象;最后的就是url上的参数,可以用可变参数传多个,也可以将多个参数放到Map中传。这个参数可以传空 | 无返回 | PUT操作是幂等的 |
patchForObject | 请求url:url ;请求对象:Object ,可以传空,常见的传一个HttpEntity 对象;指定返回类型:Class<T> ;最后的就是url上的参数,可以用可变参数传多个,也可以将多个参数放到Map中传。这个参数可以传空 | 返回一个ResponseEntity<T> 对象,泛型T由讲求参数Class<T> 决定 | Patch操作不是幂等的。NOTE: The standard JDK HTTP library does not support HTTP PATCH.You need to use the Apache HttpComponents or OkHttp request factory. |
- 四、删除
请求方法 | 请求参数 | 返回类型 | 注意事项 |
---|---|---|---|
delete | 请求url:url ;url上的参数,可以用可变参数传多个,也可以将多个参数放到Map中传。这个参数可以传空 | 无返回 | - |
总结:
- 请求参数中常用的
url
一般都是String
,所以以url
参数类型是URI
的可以不管,用的少。 - 可变参数
Object... uriVariables
和Map<String, ?>
里面传的参数都是
uriVariablesurl
上的参数,本质上没有什么区别,重点是url
上一定要占位。 - 请求参数
Class<T> responseType
和ParameterizedTypeReference<T>
都是用来指定返回数据的类型的,对于简单类型的用
responseTypeClass<T>
就可以了,但是如果返回类型太过复杂(如对象嵌套之类的)可以用ParameterizedTypeReference<T>
来指定返回类型。 - 关于返回,XXXForObject的方法都只返回具体内容,不返回Http的头信息和状态信息,要返回头信息和状态信息的时候就要使用XXXForEntity,这一类方法都是返回
ResponseEntity<T>
。 - exchange方法可以发多种类型的Http请求,本质上与具体的某一类的请求没有太大差别,唯一的是exchange方法中有可以参数化响应类型的请求参数:
ParameterizedTypeReference<T>
,在特定的时候比较合用。
更多推荐
所有评论(0)