SpringBoot使用RestTemplate发送HTTP请求
RestTemplate
是Spring
提供的用于访问Rest
服务的客户端,RestTemplate
提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。我之前的HTTP
开发是用apache
的HttpClient
开发,代码复杂,还得操心资源回收等。代码很复杂,冗余代码多。
本教程将带领大家实现Spring
生态内RestTemplate
的GET
请求和POST
请求还有exchange
指定请求类型的实践和RestTemplate
核心方法源码的分析,看完你就会用优雅的方式来发HTTP
请求。
RestTemplate
是Spring
提供的用于访问Rest
服务的客户端,RestTemplate
提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。我之前的HTTP
开发是用apache
的HttpClient
开发,代码复杂,还得操心资源回收等。代码很复杂,冗余代码多。
本教程将带领大家实现Spring
生态内RestTemplate
的GET
请求和POST
请求还有exchange
指定请求类型的实践和RestTemplate
核心方法源码的分析,看完你就会用优雅的方式来发HTTP
请求。
一、简述RestTemplate
RestTemplate
是Spring Boot
用于同步client
端的核心类,简化了与http
服务的通信,并满足RestFul
原则,程序代码可以给它提供URL
,并提取结果。默认情况下,RestTemplate
默认依赖jdk
的HTTP
连接工具。当然你也可以 通过setRequestFactory
属性切换到不同的HTTP
源,比如Apache HttpComponents
、Netty
和OkHttp
。
RestTemplate能大幅简化了提交表单数据的难度,并且附带了自动转换JSON数据的功能,但只有理解了HttpEntity的组成结构(header与body),且理解了与uriVariables之间的差异,才能真正掌握其用法。这一点在Post请求更加突出,该类的入口主要是根据HTTP的六个方法制定:
在内部,RestTemplate默认使用HttpMessageConverter实例将HTTP消息转换成POJO或者从POJO转换成HTTP消息。默认情况下会注册主mime类型的转换器,但也可以通过setMessageConverters注册其他的转换器。其实这点在使用的时候是察觉不到的,很多方法有一个responseType 参数,它让你传入一个响应体所映射成的对象,然后底层用HttpMessageConverter将其做映射
在内部,RestTemplate默认使用SimpleClientHttpRequestFactory和DefaultResponseErrorHandler来分别处理HTTP的创建和错误,但也可以通过setRequestFactory和setErrorHandler来覆盖。
二、get请求实践
1. getForObject()
方法
public <T> T getForObject (String url, Class<T> responseType, Object... uriVariables) {}public <T> T getForObject (String url, Class<T> responseType, Map<String, ?> uriVariables) public <T> T getForObject (URI url, Class<T> responseType)
getForObject()其实比getForEntity()多包含了将HTTP转成POJO的功能,但是getForObject没有处理response的能力。因为它拿到手的就是成型的pojo。省略了很多response的信息。
@SpringBootTest class RestTemplateApplicationTests { @Test public void restTemplateGetTest () { RestTemplate restTemplate = new RestTemplate(); Notice notice = restTemplate.getForObject("http://xxx.top/notice/list/1/5" , Notice.class ) ; System.out.println(notice); } @Test public void restTemplateGetTest () { RestTemplate restTemplate = new RestTemplate(); Notice notice = restTemplate.getForObject("http://fantj.top/notice/list/{1}/{2}" , Notice.class , 1, 5) ; System.out.println(notice); } @Test public void restTemplateGetTest () { RestTemplate restTemplate = new RestTemplate(); Map<String,String> map = new HashMap(); map.put("start" ,"1" ); map.put("page" ,"5" ); Notice notice = restTemplate.getForObject("http://fantj.top/notice/list/{start}/{page}" , Notice.class , map ) ; System.out.println(notice); } }
2. getForEntity()
方法
public <T> ResponseEntity<T> getForEntity (String url, Class<T> responseType, Object... uriVariables) {}public <T> ResponseEntity<T> getForEntity (String url, Class<T> responseType, Map<String, ?> uriVariables) {}public <T> ResponseEntity<T> getForEntity (URI url, Class<T> responseType) {}
与getForObject()方法不同的是返回的是ResponseEntity对象,如果需要转换成pojo,还需要json工具类的引入,这个按个人喜好用。
@SpringBootTest class RestTemplateApplicationTests { @Test public void restTemplateGetTest () { RestTemplate restTemplate = new RestTemplate(); ResponseEntity<Notice> entity = restTemplate.getForEntity("http://xxx.top/notice/list/1/5" , Notice.class ) ; System.out.println("statusCode.is2xxSuccessful(): " +entity.getStatusCode().is2xxSuccessful()); System.out.println("entity.getStatusCode(): " +entity.getStatusCode()); System.out.println("entity.getStatusCodeValue(): " +entity.getStatusCodeValue()); System.out.println("entity.getHeaders(): " +entity.getHeaders().toString()); System.out.println("entity.getBody(): " +entity.getBody()); } @Test public void restTemplateGetTest () { RestTemplate restTemplate = new RestTemplate(); ResponseEntity<Notice> entity = restTemplate.getForEntity("http://fantj.top/notice/list/{1}/{2}" , Notice.class , 1, 5) ; System.out.println("statusCode.is2xxSuccessful(): " +entity.getStatusCode().is2xxSuccessful()); System.out.println("entity.getStatusCode(): " +entity.getStatusCode()); System.out.println("entity.getStatusCodeValue(): " +entity.getStatusCodeValue()); System.out.println("entity.getHeaders(): " +entity.getHeaders().toString()); System.out.println("entity.getBody(): " +entity.getBody()); } @Test public void restTemplateGetTest () { RestTemplate restTemplate = new RestTemplate(); Map<String,String> map = new HashMap(); map.put("start" ,"1" ); map.put("page" ,"5" ); ResponseEntity<Notice> entity = restTemplate.getForEntity("http://fantj.top/notice/list/{start}/{page}" , Notice.class , map ) ; System.out.println("statusCode.is2xxSuccessful(): " +entity.getStatusCode().is2xxSuccessful()); System.out.println("entity.getStatusCode(): " +entity.getStatusCode()); System.out.println("entity.getStatusCodeValue(): " +entity.getStatusCodeValue()); System.out.println("entity.getHeaders(): " +entity.getHeaders().toString()); System.out.println("entity.getBody(): " +entity.getBody()); } }
三、POST
请求实践
1. postForObject()
方法
public <T> T postForObject (String url, @Nullable Object request, Class<T> responseType, Object... uriVariables) throws RestClientException {}public <T> T postForObject (String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {}public <T> T postForObject (URI url, @Nullable Object request, Class<T> responseType) throws RestClientException {}
2. postForEntity()
方法
public <T> T postForEntity (String url, @Nullable Object request, Class<T> responseType, Object... uriVariables) throws RestClientException {}public <T> T postForEntity (String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {}public <T> T postForEntity (URI url, @Nullable Object request, Class<T> responseType) throws RestClientException {}
3. 事例
private static void createEmployee () { final String uri = "http://localhost:8080/springrestexample/employees" ; EmployeeVO newEmployee = new EmployeeVO(-1 , "Adam" , "Gilly" , "test@email.com" ); RestTemplate restTemplate = new RestTemplate(); EmployeeVO result = restTemplate.postForObject( uri, newEmployee, EmployeeVO.class ) ; System.out.println(result); } private static void postEmployee () { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap<String, String> map= new LinkedMultiValueMap<>(); map.add("id" , "1" ); HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers); RestTemplate restTemplate = new RestTemplate(); EmployeeVO result = restTemplate.postForObject( uri, request, EmployeeVO.class ) ; System.out.println(result); }
4. 发送 @RequestBody JSONObject
请求
public static void main (String[] args) { JSONObject jsonObject = new JSONObject(); jsonObject.put("deviceId" , "1345" ); jsonObject.put("commandCode" , "553339299121" ); try { RestTemplate restTemplate = new RestTemplate(); restTemplate.getMessageConverters().add(new FastJsonHttpMessageConverter()); restTemplate.postForObject("http://localhost:8080/Lock/master/sendCmd" , jsonObject, void .class ) ; } catch (RestClientException e) { e.printStackTrace(); } } @RequestMapping (value = "/sendCmd" , method = RequestMethod.POST)@ResponseBody public void sendCommnand (@RequestBody JSONObject json) { logger.info(json.toJSONString()); }
如果不需要返回值,两边可以直接配成 void。
两边的参数的结果要相同,否则会报415错误,即不支持的类型。
如果服务端有返回值,但是请求端是用void接收,会报错。(返回值怎么可能转化成void)
如果服务端没有返回值,请求端如果用其他类型接收,都是null。
即便 RestTemplate 中的返回值和服务端的返回值不同,也可以请求到,只是客户端会报异常:org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type ……
服务端的参数要加 @RequestBody 的注解
四、PUT
请求实践
private static void updateEmployee () { final String uri = "http://localhost:8080/springrestexample/employees/{id}" ; Map<String, String> params = new HashMap<String, String>(); params.put("id" , "2" ); EmployeeVO updatedEmployee = new EmployeeVO(2 , "New Name" , "Gilly" , "test@email.com" ); RestTemplate restTemplate = new RestTemplate(); restTemplate.put ( uri, updatedEmployee, params); }
五、DELETE
请求实践
private static void deleteEmployee () { final String uri = "http://localhost:8080/springrestexample/employees/{id}" ; Map<String, String> params = new HashMap<String, String>(); params.put("id" , "2" ); RestTemplate restTemplate = new RestTemplate(); restTemplate.delete ( uri, params ); }
六、使用exchange指定调用方式
exchange()方法跟上面的getForObject()、getForEntity()、postForObject()、postForEntity()等方法不同之处在于它可以指定请求的HTTP类型。但是你会发现exchange的方法中似乎都有@Nullable HttpEntity requestEntity这个参数,这就意味着我们至少要用HttpEntity来传递这个请求体,之前说过源码所以建议就使用HttpEntity提高性能。
@Test public void rtExchangeTest () throws JSONException { RestTemplate restTemplate = new RestTemplate(); String url = "http://xxx.top/notice/list" ; HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); JSONObject jsonObj = new JSONObject(); jsonObj.put("start" ,1 ); jsonObj.put("page" ,5 ); HttpEntity<String> entity = new HttpEntity<>(jsonObj.toString(), headers); ResponseEntity<JSONObject> exchange = restTemplate.exchange(url, HttpMethod.GET, entity, JSONObject.class ) ; System.out.println(exchange.getBody()); }
七、excute()指定调用方式
excute()的用法与exchange()大同小异了,它同样可以指定不同的HttpMethod,不同的是它返回的对象是响应体所映射成的对象,而不是ResponseEntity。需要强调的是,execute()方法是以上所有方法的底层调用。
@Override @Nullable public <T> T postForObject (String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger); return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables); }
八、上传文件
public static void transFile () { String url = "http://localhost:8080/Lock/nbdc/remoteUpdate" ; RestTemplate restTemplate = new RestTemplate(); FileSystemResource resource = new FileSystemResource(new File("C:\\Users\\Administrator\\Desktop\\40.txt" )); MultiValueMap<String, Object> param = new LinkedMultiValueMap<>(); param.add("deviceId" , "123424" ); param.add("file" , resource); Boolean recv = restTemplate.postForObject(url, param, Boolean.class ) ; System.out.println(recv); }
如果有 @RequestParam 注解,MultiValueMap中的名字必须与它对应,否则请求不到。
如果没有 @RequestParam 注解,MultiValueMap中的名字与它不对应,可以请求到,但是file为null。