先看看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是一样的,不赘述。
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为参数的方法都当重复方法,不再做分类和总结。
本文是自己学习restemplate时,网上看到的一篇博文,coopy过来当做笔记,读者有兴趣可以查看原文,原文地址为: https://blog.csdn.net/u012843361/article/details/79893638