RestTemplate 使用详解

一、简介

常见的http客户端请求工具:

  1. JDK 自带 HttpURLConnection
  2. Apache HttpClient
  3. OKHttp

以上 工具虽然常用,但对于 RESTful 操作相对不是太友好。所以,从 Spring3.0开始支持的一个 HTTP 请求工具RestTemplate,提供了常见的 REST请求方案的模板。
RestTemplate只是提供了 Http请求模板,其底层默认是使用HttpURLConnection作为真正的请求工具。它可以通过构造方法替换底层的执行引擎,常见的引擎又HttpClientNettyOkHttp

二、简单的请求

1、GET请求

1. 直接拼接参数

  String params = "http://localhost:10011/user/params?name=诸葛亮&age=100";
  String result = template.getForObject(HOST  + params, String.class);
  LOGGER.info("请求返回结果:{}", result);

2. 占位符参数

  // 1、可以直接在方法中指定参数, 此种方式还可以使用下标的形式。
  // 请求路径还可以为:name={0}&age={1}
 String url = "http://localhost:10011/user/params?name={name}&age={age}";
 String result = template.getForObject(url , String.class, "李四", 20);
 LOGGER.info("请求返回结果:{}", result);

// 2、 通过 Map 传递参数,此种方式必须使用占位符,并且 map 中的 key 值,要与请求
// 路径中的 占位符一致
Map<String, Object> map = new HashMap<>();
map.put("name", "王五");
map.put("age", 30);
ResponseEntity<String> forEntity = template.getForEntity(HOST + url , String.class, map);

2、POST请求

接口实例
如下的接口实例中两个方法的唯一区别为 一个参数中加了注解@RequestBody,一个没有加,两者在 POST请求中传值方式不同。

    // 接口 1
    @RequestMapping("/obj")
    @ResponseBody
    public User getObj(User user){
        return user;
    }
    // 接口 2
    @RequestMapping("/body")
    @ResponseBody
    public User getBody(@RequestBody User user){
        return user;
    }

1、带有 @RequestBody 注解的请求

此种情况下,请求参数可以为 设置User对象,也可以说使用Map

   String url = "http://localhost:10011/user/body";
   
   User user = new User().setName("张三")
            .setAge(40);
    String result = template.postForObject(url, user, String.class);
    LOGGER.info("请求返回结果:{}", result);

    Map<String, Object> map = new HashMap<>();
    map.put("name", "王五");
    map.put("age", 30);

    ResponseEntity<String> forEntity = template.postForEntity(url, map, String.class);

    LOGGER.info("请求返回结果:{}", forEntity.getBody());

2、不带@RequestBody 注解的请求

此种情况下,传的参数需要使用MultiValueMap

  MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
  map.add("name", "王五");
  map.add("age", 30);
  ResponseEntity<String> forEntity = template.postForEntity(url, map, String.class);

   LOGGER.info("请求返回结果:{}", forEntity.getBody());

三、带请求头的请求

1、GET 请求

GET请求中的getForObjectgetForEntity 方法不支持传递头信息,需要使用 通用方法exchange,如下所示:

  String url = "http://localhost:10011/user/params?name={name}&age={age}";
   // 定义头信息
   HttpHeaders headers = new HttpHeaders();
   headers.add("token", UUID.randomUUID().toString());
   // 请求参数
   Map<String, Object> map = new HashMap<>();
   map.put("name", "王五");
   map.put("age", 30);

   // 使用 HttpEntity 对 头信息进行封装
   HttpEntity<Map<String, Object>> entity = new HttpEntity<>(headers);
   // 请求方法 exchange
   ResponseEntity<String> response = template.exchange(url, HttpMethod.GET, entity, String.class, map);
   LOGGER.info("请求返回结果:{}", response.getBody());

2、POST请求

POST 请求添加头信息比较简单,只需要使用 HttpEntity 对请求对象进行一次封装即可。

   // 定义头信息
   HttpHeaders headers = new HttpHeaders();
   headers.add("token", UUID.randomUUID().toString());
   User user = new User().setName("张三")
            .setAge(40);
    // 使用 HttpEntity 把请求对象user 和 header 进行组装
   HttpEntity<User> userHttpEntity = new HttpEntity<>(user, headers);
    // 把请求参数改为 httpEntity 对象即可
   String result = template.postForObject(url, userHttpEntity, String.class);
   LOGGER.info("请求返回结果:{}", result);

四、form 表单请求

form表单的提交只需要需要把头信息ContentType 设置为MediaType.APPLICATION_FORM_URLENCODED ,同时参数需要使用 MultiValueMap 封装。如下所示:

     String url = "http://localhost:10011/user/obj";
      
     HttpHeaders headers = new HttpHeaders();
     headers.add("token", UUID.randomUUID().toString());
     // 设置请求类型
     headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
     // 组装参数
     MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
     multiValueMap.add("name", "王五");
     multiValueMap.add("age", 30);
     // 发送请求
     HttpEntity< MultiValueMap<String, Object>> request = new HttpEntity<>(multiValueMap, headers);
     ResponseEntity<String> exchange = template.exchange(url, HttpMethod.POST, request, String.class);
     LOGGER.info("请求返回结果:{}", exchange.getBody());

五、上传下载

1、上传

  String url = "http://localhost:10011/user/upload";
  HttpHeaders headers = new HttpHeaders();

  // 模拟读取文件
  File file = new File("F:/run.bat");
  //从File句柄创建一个新的FileSystemResource
  FileSystemResource resource = new FileSystemResource(file);

  MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
  multiValueMap.add("name", "update.bat");
  multiValueMap.add("file", resource);

  HttpEntity< MultiValueMap<String, Object>> request = new HttpEntity<>(multiValueMap, headers);
  ResponseEntity<String> exchange = template.exchange(url, HttpMethod.POST, request, String.class);
  LOGGER.info("请求返回结果:{}", exchange.getBody());

2、下载

1. 小文件下载
小文件下载比较简单,只需要把请求响应类型设置为byte[] 即可,如下所示:

   String url = "http://localhost:10011/user/downLoad";
   // 请求下载
   ResponseEntity<byte[]> entity = template.getForEntity(url, byte[].class);
   // 写文件
   byte[] body = entity.getBody();
   Files.write(Paths.get("D:/download.jpg"), body);

这种下载方法实际上是将下载文件一次性加载到客户端本地内存,然后从内存将文件写入磁盘。这种方式对于小文件的下载还比较适合,如果文件比较大或者文件下载并发量比较大,容易造成内存的大量占用,从而降低应用的运行效率。

2. 大文件下载

这种下载方式的区别在于

  • 设置了请求头APPLICATION_OCTET_STREAM,表示以流的形式进行数据加载
  • RequestCallback 结合File.copy保证了接收到一部分文件内容,就向磁盘写入一部分内容。而不是全部加载到内存,最后再写入磁盘文件。
   String url = "http://localhost:10011/user/downLoad";

   String targetPath = "D:/download.jpg";

   // 定义请求头的接收类型
   RequestCallback requestCallback = request -> request.getHeaders()
           .setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
   //对响应进行流式处理而不是将其全部加载到内存中
   template.execute(url, HttpMethod.GET, requestCallback, clientHttpResponse -> {
       Files.copy(clientHttpResponse.getBody(), Paths.get(targetPath));
       return null;
   });

六、更换底层请求执行引擎

Spring中已经提供了大部分的执行引擎,比如 HttpClientOkHttp3 等,要想使用这些底层引擎只需要加入响应的依赖,然后再初始化类似,在构造方法中指定响应的执行引擎即可。

// 1、使用 HttpClient 引擎。
RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
// 2、使用OkHttp3 引擎
RestTemplate restTemplate = new RestTemplate(new OkHttp3ClientHttpRequestFactory());

七、拦截器

当所有的请求需要设置公共信息时,就可以使用拦截器,为每个请求设置。设置方法如下:

  RestTemplate template = new RestTemplate();
  // 此处设置了一个公共请求头 AUTHORIZATION
  ClientHttpRequestInterceptor interceptor = new ClientHttpRequestInterceptor() {
       @Override
       public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
           HttpHeaders headers = request.getHeaders();
           headers.set(HttpHeaders.AUTHORIZATION, "636213dc4ac6e9220a0f5107");
           return execution.execute(request, body);
       }
   };
   template.setInterceptors(Arrays.asList(interceptor));
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,513评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,312评论 1 305
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,124评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,529评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,937评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,913评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,084评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,816评论 0 205
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,593评论 1 249
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,788评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,267评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,601评论 3 261
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,265评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,158评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,953评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,066评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,852评论 2 277

推荐阅读更多精彩内容