Http请求之优雅的RestTemplate
前言
本篇博客為對RestTemplate總結
HttpURLConnection
在講RestTemplate之前我們來看看再沒有RestTemplate之前是怎么發送http請求的。
private String httpRequest(String api){BufferedReader in = null;StringBuffer result;try {URL url = new URL(api);//打開和url之間的連接HttpURLConnection connection = (HttpURLConnection) url.openConnection();connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");connection.setRequestProperty("Charset", "utf-8");connection.connect();result = new StringBuffer();//讀取URL的響應in = new BufferedReader(new InputStreamReader(connection.getInputStream()));String line;while ((line = in.readLine()) != null) {result.append(line);}return result.toString(); //返回json字符串} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {try {if (in != null) {in.close();}} catch (IOException e) {e.printStackTrace();}}return null;}
可以看到需要使用HttpURLConnection去打開連接,然后再設置一堆請求參數,通過流讀取URL的響應結果后還要遍歷流封裝數據,最后還要關閉連接,可謂非常繁瑣。如果使用RestTemplate發送同樣的一個請求的話,只需要一步:
String str = restTemplate.getForObject(api,String.class);
跟進RestTemplate源碼,可以看到RestTemplate底層就是對HttpURLConnection的封裝,幫我們解決了那些繁瑣的過程
@Overridepublic ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);prepareConnection(connection, httpMethod.name());if (this.bufferRequestBody) {return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming);}else {return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming);}}
RestTemplate簡介
RestTemplate 是從 Spring3.0 開始支持的一個 HTTP 請求工具,它提供了常見的REST請求方案的模版,例如 GET 請求、POST 請求、PUT 請求、DELETE 請求以及一些通用的請求執行方法 exchange 以及 execute。RestTemplate 繼承自 InterceptingHttpAccessor 并且實現了 RestOperations 接口,其中 RestOperations 接口定義了基本的 RESTful 操作,這些操作在 RestTemplate 中都得到了實現。說白了RestTemplate就是Spring提供的一個訪問Http服務的客戶端類,在微服務之間的調用,接口調用就需要使用的RestTemplate
Get請求
可以看到,使用RestTemplate發送get請求主要有兩類方法,分別是getForEntity和getForObject,兩類方法又分別有三個重載方法,接下來我們看看這兩個方法。
getForEntity
getForEntity返回類型是ResponseEntity,也就是說如果開發者需要獲取響應頭的話,那么就需要使用 getForEntity 來發送 HTTP 請求,此時返回的對象是一個 ResponseEntity 的實例。這個實例中包含了響應數據以及響應頭。看下它的三個重載方法:
@Overridepublic <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)throws RestClientException {RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables));}@Overridepublic <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables)throws RestClientException {RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables));}@Overridepublic <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException {RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor));}
可以看到有兩個必填的參數,第一個參數是請求接口的url,第二個參數是響應結果中響應體的類型。第三是選填的參數,http請求攜帶的參數,有兩種類型,一種是以map的形式,另一種則為占位符的格式,類似于sql中的占位符。
private JSONObject sendGetRequest(Map<String,Object> map){ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class, map); return JSONObject.parseObject(json);}
String url = "http://" + host + ":" + port + "/sayHello?name={1}&sex={2}";ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class, name,sex);
當然我們也可以直接在url后面拼接參數。
private JSONObject sendGetRequest(Map<String,Object> map){StringBuilder accessRequestUrl = new StringBuilder(cspProperties.getUrl() + "?");Set<String> keys = map.keySet();List<String> list = new ArrayList<>(keys);for (String key : list) {accessRequestUrl.append(key).append("=").append(map.get(key).toString()).append("&");}accessRequestUrl.subSequence(0,accessRequestUrl.length() - 1);ResponseEntity<String> json = restTemplate.getForEntity(accessRequestUrl.toString(),String.class);return JSONObject.parseObject(json);}
getForObject
getForObject和getForEntity類似,唯一的區別就是getForObject返回參數就是接口返回的數據,它不會返回響應頭等信息。如果只關心數據本身而不關心響應頭等信息就可以使用該方法。
@Override@Nullablepublic <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);HttpMessageConverterExtractor<T> responseExtractor =new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);}@Override@Nullablepublic <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);HttpMessageConverterExtractor<T> responseExtractor =new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);}@Override@Nullablepublic <T> T getForObject(URI url, Class<T> responseType) throws RestClientException {RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);HttpMessageConverterExtractor<T> responseExtractor =new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);return execute(url, HttpMethod.GET, requestCallback, responseExtractor);}
POST 請求
post 請求的方法類型除了 postForEntity 和 postForObject 之外,還有一個 postForLocation。這里的方法類型雖然有三種,但是這三種方法重載的參數基本是一樣的。
postForEntity
@Overridepublic <T> ResponseEntity<T> postForEntity(String url, @Nullable Object request,Class<T> responseType, Object... uriVariables) throws RestClientException {RequestCallback requestCallback = httpEntityCallback(request, responseType);ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);return nonNull(execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables));}@Overridepublic <T> ResponseEntity<T> postForEntity(String url, @Nullable Object request,Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {RequestCallback requestCallback = httpEntityCallback(request, responseType);ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);return nonNull(execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables));}@Overridepublic <T> ResponseEntity<T> postForEntity(URI url, @Nullable Object request, Class<T> responseType)throws RestClientException {RequestCallback requestCallback = httpEntityCallback(request, responseType);ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);return nonNull(execute(url, HttpMethod.POST, requestCallback, responseExtractor));}
post中的postForEntity和postForObject返回類型和get方法一樣,這里就不敘述,我們來看看參數。其它參數沒啥好說的,上面都提到了,可以看出出現了一個新的參數 Object request,這個是什么?跟進源碼:
public HttpEntityRequestCallback(@Nullable Object requestBody, @Nullable Type responseType) {super(responseType); // requestBody 就是 request參數if (requestBody instanceof HttpEntity) {this.requestEntity = (HttpEntity<?>) requestBody;}else if (requestBody != null) {this.requestEntity = new HttpEntity<>(requestBody);}else {this.requestEntity = HttpEntity.EMPTY;}}
可以看到,request是一個HttpEntity類型,HttpEntity其實相當于一個消息實體,內容是http傳送的報文(主要是json文件)。這里只需要知道它是用來表征一個http報文的實體就行了,用來發送或接收。舉個例子:
private JSONObject sendPostRequest(Map<String,String> map){HttpHeaders headers = new HttpHeaders();headers.set("Content-Type","application/json;charset=UTF-8");headers.set("Accept","application/json");headers.set("Accept-Encoding","");String url = osyProperties.getUrl();Map<String,Object> requestMessage = new HashMap<>();// 請求報文頭參數Map<String, Object> head = getRequestHeadersParams();requestMessage.put("head",head);// 請求報文體參數HashMap<String, Map<String,String>> body = new HashMap<>();requestMessage.put("body",map);HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestMessage, headers);ResponseEntity<String> entity = restTemplate.postForEntity(url, request, String.class);System.out.println(entity);String body = entity.getBody();return JSONObject.parseObject(body);}
上面代碼發送的報文形式如下(json格式,接口的參數應對應也是json接受,即參數前加上 @RequestBody注解):
{"head": {報文頭參數:值},"body": {報文體參數:值}},
}
HttpEntity實例化的源碼
public HttpEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers) {this.body = body;HttpHeaders tempHeaders = new HttpHeaders();if (headers != null) {tempHeaders.putAll(headers);}this.headers = HttpHeaders.readOnlyHttpHeaders(tempHeaders);}
postForObject
postForObject和postForEntity就是返回類型不同。
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 "username:" + user.getUsername();
}
這里一個注冊接口,一個是登錄頁面,不過這里的登錄頁面我就簡單用一個字符串代替了。然后在 consumer 中來調用注冊接口,如下:
@GetMapping("/hello")
public String sayHello() {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", "dave");URI uri = restTemplate.postForLocation(url, map);String s = restTemplate.getForObject(uri, String.class);return s;
}
這里首先調用 postForLocation 獲取 Uri 地址,然后再利用 getForObject 請求 Uri,界面結果為:username:dave。
注意:postForLocation 方法返回的 Uri 實際上是指響應頭的 Location 字段,所以,provider 中 register 接口的響應頭必須要有 Location 字段(即請求的接口實際上是一個重定向的接口),否則 postForLocation 方法的返回值為null。
總結
以上是生活随笔為你收集整理的Http请求之优雅的RestTemplate的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java锁(公平锁和非公平锁、可重入锁(
- 下一篇: eclipse中导入web项目详细配置