前面一篇介紹瞭如何使用RestTemplate
發起post和get請求,然而也只能知足一些基本的場景,對於一些特殊的如須要設置請求頭,添加認證信息等場景,卻沒有說起能夠怎麼作,這一篇則至關於進階版,將主要介紹java
exchange
方法的使用姿式<!-- more -->git
首先一個問題就是爲何要設置請求頭?github
咱們經過瀏覽器正常訪問的接口,可能經過代碼直接訪問時,就會提示403spring
而這樣的緣由,較多的一個可能就是後端的請求作了限制,好比根據請求的agent,判斷是否爲爬蟲;根據referer判斷是否要返回數據等等;然後端進行校驗的條件中,每每會拿請求頭的數據,所以這也就要求咱們在使用時,主動的塞入一些請求頭信息json
直接看RestTemplate
提供的幾個Get請求接口,並無發現有設置請求頭的地方,是否是就代表無法設置請求頭了?後端
答案檔案是能設置了,具體的使用思路有點相似mvc中的攔截器,自定義一個攔截器,而後在你實際發起請求時,攔截並設置request的請求頭瀏覽器
注意到 RestTemplate
的父類InterceptingHttpAccessor
提供了一個接收Interceptor的接口org.springframework.http.client.support.InterceptingHttpAccessor#setInterceptors
,這個就是咱們所須要的關鍵點(講道理,除非事先就知道有這麼個玩意,否則真讓你去找,還不必定能找到)安全
因此第一步就是寫一個ClientHttpRequestInterceptor
類,添加請求頭網絡
public class UserAgentInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { HttpHeaders headers = request.getHeaders(); headers.add(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36"); return execution.execute(request, body); } }
下一步就是在建立RestTemplate對象以後,聲明解釋器並測試使用了mvc
@Test public void testGetHeader() { String url = "http://localhost:8080/agent?name=一灰灰Blog"; RestTemplate restTemplate = new RestTemplate(); restTemplate.setInterceptors(Collections.singletonList(new UserAgentInterceptor())); ResponseEntity<String> response = restTemplate.getForEntity(url, String.class); System.out.println(response.getStatusCode() + " | " + response.getBody()); }
首先在測試以前,先搭一個服務,簡單判斷agent,不知足條件的直接403, 後端mock代碼以下
@ResponseBody @RequestMapping(path = "agent") public String agent(HttpServletRequest request, HttpServletResponse response, @RequestParam(value = "name", required = false) String name) throws IOException { String agent = request.getHeader(HttpHeaders.USER_AGENT); if (StringUtils.isEmpty(agent) || !agent.contains("WebKit")) { response.sendError(403, " illegal agent "); } return "welcome " + name; }
上面執行後輸出以下,添加請求頭後正常返回
固然也須要對比下不設置agent的狀況了,直接拋了一個異常出來了(說明,不顯示覆蓋User-Agent時,後端接收到的agent爲: Java/1.8.0_171
上面雖然只給了設置User-Agent的例子,可是其餘的請求頭,都是能夠放在自定義的Interceptor
中添加進去的
固然get請求使用的這種姿式,對於post而言或者對於其餘的http請求方法而言,都是通用的,而對於post請求來講,還有另一種方式,就是requset參數,能夠攜帶request headers
首先mock一個後端接口
@ResponseBody @RequestMapping(path = "post", method = {RequestMethod.GET, RequestMethod.OPTIONS, RequestMethod.POST}, produces = "charset/utf8") public String post(HttpServletRequest request, HttpServletResponse response, @RequestParam(value = "email", required = false) String email, @RequestParam(value = "nick", required = false) String nick) throws IOException { String agent = request.getHeader(HttpHeaders.USER_AGENT); if (StringUtils.isEmpty(agent) || !agent.contains("WebKit")) { response.sendError(403, " illegal agent "); return null; } return "success email=" + email + "&nick=" + URLEncoder.encode(nick, "UTF-8") + "&status=success"; }
簡單的使用姿式以下
@Test public void testPostHeader() { String url = "http://localhost:8080/post"; String email = "test@hhui.top"; String nick = "一灰灰Blog"; MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); params.add("email", email); params.add("nick", nick); HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36"); HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers); RestTemplate restTemplate = new RestTemplate(); ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class); System.out.println(response.getStatusCode() + " | " + response.getBody()); }
從上面代碼能夠看出,具體的使用姿式相比於不添加請求頭時,只是多了一個封裝
HttpHeaders
對象中MultiValueMap
中HttpEntity
對象,將這個做爲post的請求request參數傳入固然做爲對比,當不加入headers時,看下返回什麼鬼, 406異常,可是咱們後端定義的是403,爲何會返回406呢?
另外還會關注到RestTemplate還提供了一個exchange方法,這個至關於一個公共的請求模板,使用姿式和get/post沒有什麼區別,只是能夠由調用發本身來選擇具體的請求方法
使用exchange對上面的post請求進行簡單的替換以下, 基本上除了多一個參數以外沒有什麼區別了
@Test public void testPostHeader() { String url = "http://localhost:8080/post"; String email = "test@hhui.top"; String nick = "一灰灰Blog"; MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); params.add("email", email); params.add("nick", nick); HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36"); HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers); RestTemplate restTemplate = new RestTemplate(); ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, request, String.class); System.out.println(response.getStatusCode() + " | " + response.getBody()); }
那麼問題來了,爲何要有這個東西?或者說這個接口的提供能夠帶來什麼好處?
前面的post參數提交,其實默認採用的是 application/x-www-form-urlencoded
方式,便是咱們最多見的表單提交方式,在瀏覽器中的表現形式以下
此外,還有一種直接提交json串的方式,在前文 《180730-Spring之RequestBody的使用姿式小結》中有說明,具體瀏覽器中表現形式爲
因此接下來的問題就是,RestTemplate要怎麼處理呢?
建議在看下面的內容以前,先看一下上面的那篇博文,理解下RequestBody
是什麼東西
首先搭建一個後端
@Data @NoArgsConstructor @AllArgsConstructor public static class Req { private String key; private Integer size; } @ResponseBody @RequestMapping(value = "/body", method = {RequestMethod.POST, RequestMethod.GET, RequestMethod.OPTIONS}) public String body(@RequestBody Req req) { return req.toString(); }
而後使用方式,無非就是在請求頭中添加下Content-Type爲Application/json
@Test public void testPostRequestBody() { String url = "http://localhost:8080/body"; String email = "test@hhui.top"; String nick = "一灰灰Blog"; Map<String, String> params = new HashMap<>(); params.put("email", email); params.put("nick", nick); HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_TYPE, "application/json"); HttpEntity<Map<String, String>> request = new HttpEntity<>(params, headers); RestTemplate restTemplate = new RestTemplate(); ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class); System.out.println(response.getStatusCode() + " | " + response.getBody()); }
注意下post參數,是放在Map容器中,而不是以前的MultiValueMap
運行時截圖以下
post除了傳表單數據(json串)以外,還有一個常見的就是上傳文件了,實際上使用RestTemplate來實現文件上傳,算是比較簡單的了,和前面的使用基本上也沒有什麼差異,只是將文件做爲params參數而已
首先搭建一個Controller後端服務,簡單的獲取文件內容,並返回
@ResponseBody @PostMapping(path = "file") public String file(MultipartHttpServletRequest request) throws IOException { MultipartFile file = request.getFile("file"); if (file == null) { return "no file!"; } BufferedReader reader = new BufferedReader(new InputStreamReader(file.getInputStream())); StringBuilder builder = new StringBuilder(); String line = reader.readLine(); while (line != null) { builder.append(line); line = reader.readLine(); } reader.close(); return builder.toString(); }
而後就是實際的測試用例,將文件包裝在FileSystemResource
對象中,而後塞入MultiValueMap
中,注意下面的使用並無顯示添加請求頭,而這種時候,content-type 經過斷點查看實際爲 content-type = multipart/form-data;
@Test public void testPostFile() { String url = "http://localhost:8080/file"; FileSystemResource resource = new FileSystemResource(new File("/tmp/test.txt")); MultiValueMap<String, Object> params = new LinkedMultiValueMap<>(); params.add("file", resource); params.add("fileName", "test.txt"); RestTemplate restTemplate = new RestTemplate(); ResponseEntity<String> response = restTemplate.postForEntity(url, params, String.class); System.out.println(response); }
本篇主要介紹如何給RestTemplate發起的請求,添加請求頭,以及完成某些特定的請求,下面小結一下使用姿式
兩種方式
Interceptors
,在攔截器中主動添加上對應的請求頭便可,適用於爲全部的請求添加統一的請求頭的場景
postForXXX
和 exchange
兩種請求方式而言,一樣本身設置請求頭HttpHeader
,而後將請求頭和params封裝到HttpEntity
,做爲request參數提交便可json串的提交
Applicaiton/json
HttpEntity
中,發起請求便可文件上傳
MultiValueMap
中便可,和普通的請求方式沒有什麼區別初級篇介紹瞭如何使用RestTemplate發起簡單的GET/POST請求;
中級篇則介紹請求的過程當中添加設置請求頭,以及某些特殊的請求能夠怎麼處理
顯然還會有高級篇,除了上面的東西,咱們還須要知道些什麼呢?
關於高級篇,坐等更新
一灰灰的我的博客,記錄全部學習和工做中的博文,歡迎你們前去逛逛
盡信書則不如,已上內容,純屬一家之言,因我的能力有限,不免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激
一灰灰blog
知識星球