做为一个Java后端,须要经过HTTP请求其余的网络资源能够说是一个比较常见的case了;通常怎么作呢?java
可能大部分的小伙伴直接捞起Apache的HttpClient开始作,或者用其余的一些知名的开源库如OkHttp, 固然原生的HttpURLConnection也是没问题的git
本篇博文则主要关注点放在Sprig的生态下,利用RestTemplate
来发起Http请求的使用姿式github
<!-- more -->web
在介绍如何使用RestTemplate以前,咱们先抛出一些小目标,至少须要知道经过RestTemplate能够作些什么,以及咱们要用它来干些什么spring
简单的给出了一下常见的问题以下json
上面的问题比较多,目测不是一篇博文能够弄完的,所以对这个拆解一下,本篇主要关注在RestTemplate的简单Get/Post请求的使用方式上后端
捞出源码,看一下其给出的一些经常使用接口,基本上能够分为下面几种跨域
// get 请求 public <T> T getForObject(); public <T> ResponseEntity<T> getForEntity(); // head 请求 public HttpHeaders headForHeaders(); // post 请求 public URI postForLocation(); public <T> T postForObject(); public <T> ResponseEntity<T> postForEntity(); // put 请求 public void put(); // pathch public <T> T patchForObject // delete public void delete() // options public Set<HttpMethod> optionsForAllow // exchange public <T> ResponseEntity<T> exchange()
上面提供的几个接口,基本上就是Http提供的几种访问方式的对应,其中exchange却又不同,后面细说数组
从上面的接口命名上,能够看出可使用的有两种方式 getForObject
和 getForEntity
,那么这两种有什么区别?服务器
ResponseEntity
封装类中GetForObject
便可getForEntit
为了验证RestTemplate的使用姿式,固然得先提供一个后端的REST服务,这了直接用了我我的的一个古诗词的后端接口,来做为简单的Get测试使用
请求链接: https://story.hhui.top/detail?id=666106231640
返回结果:
{ "status": { "code": 200, "msg": "SUCCESS" }, "result": { "id": 666106231640, "title": "西塞山二首(今谓之道士矶,即兴国军大冶县", "author": "王周", "content": "西塞名山立翠屏,浓岚横入半江青。\n千寻铁锁无由问,石壁空存道者形。\n匹妇顽然莫问因,匹夫何去望千春。\n翻思岵屺传诗什,举世曾无化石人。", "explain": "", "theme": "无", "dynasty": "唐诗" } }
首先看下完整的接口签名
@Nullable public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException ; @Nullable public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException ; @Nullable public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException;
有三个重载的方法,从接口上也比较容易看出如何使用,其中有点疑惑的则是第一钟,参数应该怎么传了,下面给出上面几种的使用姿式
public class RestTestmplateTest { private RestTemplate restTemplate; @Before public void init() { restTemplate = new RestTemplate(); } @lombok.Data static class InnerRes { private Status status; private Data result; } @lombok.Data static class Status { int code; String msg; } @lombok.Data static class Data { long id; String theme; String title; String dynasty; String explain; String content; String author; } @Test public void testGet() { // 使用方法一,不带参数 String url = "https://story.hhui.top/detail?id=666106231640"; InnerRes res = restTemplate.getForObject(url, InnerRes.class); System.out.println(res); // 使用方法一,传参替换 url = "https://story.hhui.top/detail?id={?}"; res = restTemplate.getForObject(url, InnerRes.class, "666106231640"); System.out.println(res); // 使用方法二,map传参 url = "https://story.hhui.top/detail?id={id}"; Map<String, Object> params = new HashMap<>(); params.put("id", 666106231640L); res = restTemplate.getForObject(url, InnerRes.class, params); System.out.println(res); // 使用方法三,URI访问 URI uri = URI.create("https://story.hhui.top/detail?id=666106231640"); res = restTemplate.getForObject(uri, InnerRes.class); System.out.println(res); } }
看上面的testcase,后面两个方法的使用没什么好说的,主要看一下org.springframework.web.client.RestTemplate#getForObject(java.lang.String, java.lang.Class<T>, java.lang.Object...)
的使用姿式
{?}
来表明坑位,根据实际的传参顺序来填充{xx}
, 而这个xx,对应的就是map中的key上面执行后的截图以下
既然getForObject有三种使用方法,那么getForEntity理论上也应该有对应的三种方式
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) throws RestClientException ; public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException; public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException;
由于使用姿式和上面一致,所以只拿一个进行测试
@Test public void testGetForEntity() { String url = "https://story.hhui.top/detail?id=666106231640"; ResponseEntity<InnerRes> res = restTemplate.getForEntity(url, InnerRes.class); System.out.println(res); }
对这个,咱们主要关注的就是ResponseEntity封装中,多了哪些东西,截图以下
从上面能够看出,多了两个东西
从上面的接口说明上看,post请求除了有forObject 和 forEntity以外,还多了个forLocation;其次post与get一个明显的区别就是传参的姿式问题,get的参数通常会待在url上;post的则更常见的是经过表单的方式提交
所以接下来关注的重点在于forLocation是什么,以及如何传参
首先建立一个简单的提供POST请求的REST服务,基于Spring-boot简单搭建一个,以下
@ResponseBody @RequestMapping(path = "post", method = {RequestMethod.GET, RequestMethod.OPTIONS, RequestMethod.POST}) public String post(HttpServletRequest request, @RequestParam(value = "email", required = false) String email, @RequestParam(value = "nick", required = false) String nick) { Map<String, Object> map = new HashMap<>(); map.put("code", "200"); map.put("result", "add " + email + " # " + nick + " success!"); return JSON.toJSONString(map); }
首先看一下接口签名
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 ;
上面的三个方法,看起来和前面并无太大的区别,只是多了一个request参数,那么具体的使用如何呢?
下面分别给出使用用例
@Test public void testPost() { String url = "http://localhost:8080/post"; String email = "test@hhui.top"; String nick = "一灰灰Blog"; MultiValueMap<String, String> request = new LinkedMultiValueMap<>(); request.add("email", email); request.add("nick", nick); // 使用方法三 URI uri = URI.create(url); String ans = restTemplate.postForObject(uri, request, String.class); System.out.println(ans); // 使用方法一 ans = restTemplate.postForObject(url, request, String.class); System.out.println(ans); // 使用方法一,可是结合表单参数和uri参数的方式,其中uri参数的填充和get请求一致 request.clear(); request.add("email", email); ans = restTemplate.postForObject(url + "?nick={?}", request, String.class, nick); System.out.println(ans); // 使用方法二 Map<String, String> params = new HashMap<>(); params.put("nick", nick); ans = restTemplate.postForObject(url + "?nick={nick}", request, String.class, params); System.out.println(ans); }
上面分别给出了三种方法的调用方式,其中post传参区分为两种,一个是uri参数即拼接在url中的,还有一个就是表单参数
MultiValueMap
封装,一样是kv结构和前面的使用姿式同样,无非是多了一层包装而已,略过不讲
这个与前面有点区别,从接口定义上来讲,主要是
POST 数据到一个URL,返回新建立资源的URL
一样提供了三个接口,分别以下,须要注意的是返回结果,为URI对象,即网络资源
public URI postForLocation(String url, @Nullable Object request, Object... uriVariables) throws RestClientException ; public URI postForLocation(String url, @Nullable Object request, Map<String, ?> uriVariables) throws RestClientException ; public URI postForLocation(URI url, @Nullable Object request) throws RestClientException ;
那么什么样的接口适合用这种访问姿式呢?
想一下咱们通常登陆or注册都是post请求,而这些操做完成以后呢?大部分都是跳转到别的页面去了,这种场景下,就可使用 postForLocation
了,提交数据,并获取返回的URI,一个测试以下
首先mock一个后端接口
@ResponseBody @RequestMapping(path = "success") public String loginSuccess(String email, String nick) { return "welcome " + nick; } @RequestMapping(path = "post", method = {RequestMethod.GET, RequestMethod.OPTIONS, RequestMethod.POST}) public String post(HttpServletRequest request, @RequestParam(value = "email", required = false) String email, @RequestParam(value = "nick", required = false) String nick) { return "redirect:/success?email=" + email + "&nick=" + nick + "&status=success"; }
访问的测试用例,基本上和前面的同样,没有什么特别值得一说的
@Test public void testPostLocation() { String url = "http://localhost:8080/post"; String email = "test@hhui.top"; String nick = "一灰灰Blog"; MultiValueMap<String, String> request = new LinkedMultiValueMap<>(); request.add("email", email); request.add("nick", nick); // 使用方法三 URI uri = restTemplate.postForLocation(url, request); System.out.println(uri); }
执行结果以下
获取到的就是302跳转后端url,细心的朋友可能看到上面中文乱码的问题,如何解决呢?
一个简单的解决方案就是url编码一下
@RequestMapping(path = "post", method = {RequestMethod.GET, RequestMethod.OPTIONS, RequestMethod.POST}, produces = "charset/utf8") public String post(HttpServletRequest request, @RequestParam(value = "email", required = false) String email, @RequestParam(value = "nick", required = false) String nick) throws UnsupportedEncodingException { return "redirect:/success?email=" + email + "&nick=" + URLEncoder.encode(nick, "UTF-8") + "&status=success"; }
上面目前只给出了Get/Post两种请求方式的基本使用方式,并无涉及到更高级的如添加请求头,添加证书,设置代理等,高级的使用篇等待下一篇出炉,下面小结一下上面的使用姿式
get请求中,参数通常都是带在url上,对于参数的填充,有两种方式,思路一致都是根据实际的参数来填充url中的占位符的内容;根据返回结果,也有两种方式,一个是只关心返回对象,另外一个则包含了返回headers信心
参数填充
http://story.hhui.top?id={0}
的 urlgetForObject(String url, Class<T> responseType, Object... uriVariables)
http://story.hhui.top?id={id}
的 urlgetForObject(String url, Class<T> responseType, Map<String, ?> uriVariables)
其实还有一种传参方式,就是path参数,填充方式和上面同样,并无什么特殊的玩法,上面没有特别列出
返回结果
getForObject
getForEntity
MultiValueMap
中,做为第二个参数 Request
来提交postForLocation
,返回的是一个URI对象,即适用于返回网络资源的请求方式最前面提了多点关于网络请求的常见case,可是上面的介绍,明显只处于基础篇,咱们还须要关注的有
上面可能还停留在应用篇,对于源码和实现有兴趣的话,问题也就来了
小小的一个工具类,其实东西还挺多的,接下来的小目标,就是针对上面提出的点,逐一进行研究
一灰灰的我的博客,记录全部学习和工做中的博文,欢迎你们前去逛逛
尽信书则不如,已上内容,纯属一家之言,因我的能力有限,不免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
小灰灰Blog&公众号
知识星球