2.6. 执行requestCallback.doWithRequest(request);
RequestCallback 封装了请求体和请求头对象,既在RequstCallback可以输入需要传输的head数据
在执行 doWithRequest 时,与Connection发送请求体有着密切关系,请求头就是 SimpleBufferingClientHttpRequest.addHeaders 方法,那么请求体 bufferedOutput 是如何赋值的呢?就是在 doWithRequest 里面,如下 StringHttpMessageConverter (其他 MessageConvert 也一样,这里也是经常乱码的原因;
RequestCallback 用于操作请求头和body,在请求发出前执行。以下是RequestCallback的 两个实现类
- AcceptHeaderRequestCallback:只处理请求头,用于getXXX()方法
- HttpEntityRequestCallback: 继承于AcceptHeaderRequestCallback可以处理请求头和body,用于putXXX()、postXXX()和exchange()方法。
2.7. 接着执行 response = request.execute();
调用接口ClientHttpRequest
public interface ClientHttpRequest extends HttpRequest, HttpOutputMessage { ClientHttpResponse execute() throws IOException; }
然后使用实例 SimpleBufferingClientHttpRequest 封装请求体和请求头
@Override protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException { addHeaders(this.connection, headers); // JDK <1.8 doesn't support getOutputStream with HTTP DELETE if (getMethod() == HttpMethod.DELETE && bufferedOutput.length == 0) { this.connection.setDoOutput(false); } if (this.connection.getDoOutput() && this.outputStreaming) { this.connection.setFixedLengthStreamingMode(bufferedOutput.length); } this.connection.connect(); if (this.connection.getDoOutput()) { FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream()); } else { // Immediately trigger the request in a no-output scenario as well this.connection.getResponseCode(); } return new SimpleClientHttpResponse(this.connection); }
Delete通过请求方式和是否有请求体对象来判断是否需要发送请求体如果是delete请求,首先设置 DoOutput = true,然后根据是否有请求体数据,然后封装请求体FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());
2.8. 解析response
接着就是 response 的解析了,主要还是 Error 的解析。this.handleResponse(url, method, response);
内部处理多是解析error处理
protected void handleResponse(URI url, HttpMethod method, ClientHttpResponse response) throws IOException { ResponseErrorHandler errorHandler = getErrorHandler(); boolean hasError = errorHandler.hasError(response); if (logger.isDebugEnabled()) { try { HttpStatusCode statusCode = response.getStatusCode(); logger.debug("Response " + statusCode); } catch (IOException ex) { logger.debug("Failed to obtain response status code", ex); } } if (hasError) { errorHandler.handleError(url, method, response); } }
三、RestTemplate常用方法及示例
3.1. RestTemplate中Get方法使用
3.1.1 getForObject(URI url, Class responseType)
@Test public void TestRestGetMethod() throws Exception { RestTemplate restTemplate = new RestTemplate(); URI url = URI.create("http://api.goyeer.com:8888/user"); String response = restTemplate.getForObject(url, String.class); }
3.1.2 getForObject(String url, Class responseType, Object… uriVariables)
@Test public void TestGetForObjcetByArgument() throws Exception { RestTemplate restTemplate = new RestTemplate(); String appkey="Goy_20200034_MES"; String appSecret="GY202305-0801-41d4-a716-9998880"; String url = "http://api.goyeer.com:8888/getToken?appkey={1}&appsecret={2}"; String responseToken = restTemplate.getForObject(url,String.class, appkey,appSecret); }
3.1.3 getForObject(String url, Class responseType, Map<String, ?> uriVariables)
@Test public void TestGetForObjectByMap() throws Exception{ Map<String,Object> map=new HashMap(); map.put("appkey","Goy_20200034_MES"); map.put("appSecret","GY202305-0801-41d4-a716-9998880"); RestTemplate restTemplate = new RestTemplate(); String url = "http://api.goyeer.com:8888/user?appkey={appkey}&appsecret={appSecret}"; String response = restTemplate.getForObject(url,String.class, map); }
3.1.4 ResponseEntity getForEntity(URI url, Class responseType)
@Test public void TestGetForEntity() throws Exception { RestTemplate restTemplate = new RestTemplate(); URI url = URI.create("http://api.goyeer.com:8888/user"); ResponseEntity<String> response = restTemplate.getForEntity(url, String.class); int httpCode =response.getStatusCode().value(); String resp= response.getBody(); System.out.println(httpCode); System.out.println(resp); }
3.1.5 ResponseEntity getForEntity(String url, Class responseType, Object… uriVariables)
@Test public void TestGetForEntityByArgument() throws Exception { RestTemplate restTemplate = new RestTemplate(); String appkey="Goy_20200034_MES"; String appSecret="GY202305-0801-41d4-a716-9998880"; String url = "http://api.goyeer.com:8888/getToken?appkey={1}&appsecret={2}"; ResponseEntity<String> response = restTemplate.getForEntity(url,String.class, appkey,appSecret); int httpCode =response.getStatusCode().value(); String resp= response.getBody(); System.out.println(httpCode); System.out.println(resp); }
3.1.6 getForEntity(String url, Class responseType, Map<String, ?> uriVariables)
@Tes public void TestGetForEntityByMap() throws Exception { RestTemplate restTemplate = new RestTemplate(); Map<String,Object> map=new HashMap(); map.put("appkey","Goy_20200034_MES"); map.put("appSecret","GY202305-0801-41d4-a716-9998880"); String url = "http://api.goyeer.com:8888/getToken?appkey={appkey}&appsecret={appsecret}"; ResponseEntity<String> response = restTemplate.getForEntity(url,String.class, map); int httpCode =response.getStatusCode().value(); String resp= response.getBody(); System.out.println(httpCode); System.out.println(resp); }
3.1.7 getForEntity与getForObject区别
GetForEntity和GetForObject用法几乎完全一致,区别在于前者可以查看请求状态码,请求头信息。
getForEntity返回的是一个ResponseEntity,而getForObject返回的就只是返回内容。getForObject的返回相当于只返回http的body部份而getForEntity的返回是返回全部信息
3.2. RestTemplate中POST方法使用
3.2.1. postForObject(URI url, @Nullable Object request, Class responseType)
@Test public void TestPostForObject() { try { RestTemplate restTemplate = new RestTemplate(); URI url = URI.create("http://api.goyeer.com:7777/user/create"); User user = new User(); user.setId(1110L); user.setLoginName("Goy"); user.setDepartment("Department"); User respUser = restTemplate.postForObject(url, user, User.class); System.out.println(respUser.getLoginName()); } catch (Exception exception) { throw exception; } }
3.2.2. postForObject(String url, @Nullable Object request, Class responseType, Object… uriVariables)
@Test public void TestPostForObejectByArgument()throws Exception{ RestTemplate restTemplate = new RestTemplate(); try { String uri="http://api.goyeer.com:7777/{1}/{2}"; String controllerName="user"; String operateName="create"; User user = new User(); user.setId(1110L); user.setLoginName("Goy"); user.setDepartment("Department"); User respUser = restTemplate.postForObject(uri, user, User.class,controllerName,operateName); System.out.println(respUser.getLoginName()); } catch (Exception e) { throw new RuntimeException(e);
3.2.3. postForObject(String url, @Nullable Object request, Class responseType,Map<String, ?> uriVariables)
@Test public void TestPostForObjectByMap() { RestTemplate restTemplate = new RestTemplate(); try { String uri="http://localhost:7777/{controller}/{operate}"; Map map=new HashMap(); map.put("controller","user"); map.put("operate","create"); User user = new User(); user.setId(1110L); user.setLoginName("Goy"); user.setDepartment("Department"); User respUser = restTemplate.postForObject(uri, user, User.class,map); System.out.println(respUser.getLoginName()); } catch (Exception e) { throw new RuntimeException(e); } }
3.2.4. 其他常用POST的方法
其它提供POST的方法有postForLocation、postForEntity用法如postForObject。
相比于postForObject()方法, postForEntity() 返回响应体为 ResponseEntity 类型,其他两个方法功能一致。
与postForObject 和 postForEntity 方法类型, postForLocation 也发送post请求至特定uri并创建新的对象。唯一的差异是返回值为Location头信息。
四、常用RestTemplate方法
4.1. Head请求使用headForHeaders方法
headForHeaders方法 是HTTP中请求的一种。HEAD方法跟GET方法相同,只不过服务器响应时不会返回消息体。一个HEAD请求的响应中,HTTP头中包含的元信息应该和一个GET请求的响应消息相同。这种方法可以用来获取请求中隐含的元信息,而不用传输实体本身。也经常用来测试超链接的有效性、可用性和最近的修改。
一个HEAD请求的响应可被缓存,也就是说,响应中的信息可能用来更新之前缓存的实体。如果当前实体跟缓存实体的阈值不同(可通过Content-Length、Content-MD5、ETag或Last-Modified的变化来表明),那么这个缓存就被视为过期了。
- headForHeaders(String url, Object… uriVariables)
- headForHeaders(String url, Map<String, ?> uriVariables)
- headForHeaders(URI url)
以上三个方法使用方法同getForObject类似
4.2. Put请求使用put方法
PUT请求是向服务器端发送数据的,从而改变信息,该请求就像数据库的update操作一样,用来修改数据的内容,但是不会增加数据的种类等,也就是说无论进行多少次PUT操作,其结果并没有不同。
- put(String url, @Nullable Object request, Object… uriVariables)
- put(String url, @Nullable Object request, Map<String, ?> uriVariables)
- put(URI url, @Nullable Object request)
以上三个方法使用方法同getForObject类似
4.2. PATCH请求使用patchForObject方法
HTTP中为了提高交互操作性与防止错误,确实需要一种新的修改方法,而PUT方法已经被定义为用一个请求体去修改一个完整的资源。并且不能重复做部分更改,否则代理和缓存、甚至服务器或者客户端都会得到有问题的操作结果。
至此,PATCH方法有了被完全定义的必要。
PATCH在请求中定义了一个描述修改的实体集合,如果被请求修改的资源不存在,服务器可能会创建一个新的资源。
- patchForObject(URI url, @Nullable Object request, Class responseType)
- patchForObject(String url, @Nullable Object request, Class responseType,Map<String, ?> uriVariables)
- patchForObject(String url, @Nullable Object request, Class responseType,Object… uriVariables)
以上三个方法使用方法同getForObject类似
4.2. Delete请求使用patchForObject方法
向服务器端提交数据,请求数据在报文body里;发送一个删除数据的请求。
- delete(String url, Object… uriVariables)
- delete(String url, Map<String, ?> uriVariables)
- delete(URI url)
以上三个方法使用方法同getForObject类似
五、总结
RestTemplate是Spring自带的一个调用rest服务的客户端,它提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。在一般项目中完全可以替代HttpClient和OkHttp。
在后续Spring cloud系列文章中会多次使用到。