转自:

Springboot — 用更优雅的方式发HTTP请求(RestTemplate详解) - Java知音号 - 博客园RestTemplate是Spring提供的用于访问Rest服务的客户端,RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。 我之前的HTTP开发是用aphttps://www.cnblogs.com/javazhiyin/p/9851775.html


RestTemplate是Spring提供的用于访问Rest服务的客户端,RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。

我之前的HTTP开发是用apache的HttpClient开发,代码复杂,还得操心资源回收等。代码很复杂,冗余代码多,稍微截个图,这是我封装好的一个post请求工具:

本教程将带领大家实现Spring生态内RestTemplate的Get请求和Post请求还有exchange指定请求类型的实践和RestTemplate核心方法源码的分析,看完你就会用优雅的方式来发HTTP请求。

1.简述RestTemplate

是Spring用于同步client端的核心类,简化了与http服务的通信,并满足RestFul原则,程序代码可以给它提供URL,并提取结果。默认情况下,RestTemplate默认依赖jdk的HTTP连接工具。当然你也可以 通过setRequestFactory属性切换到不同的HTTP源,比如Apache HttpComponents、Netty和OkHttp。

RestTemplate能大幅简化了提交表单数据的难度,并且附带了自动转换JSON数据的功能,但只有理解了HttpEntity的组成结构(header与body),且理解了与uriVariables之间的差异,才能真正掌握其用法。这一点在Post请求更加突出,下面会介绍到。

该类的入口主要是根据HTTP的六个方法制定:

此外,exchange和excute可以通用上述方法。

在内部,RestTemplate默认使用HttpMessageConverter实例将HTTP消息转换成POJO或者从POJO转换成HTTP消息。默认情况下会注册主mime类型的转换器,但也可以通过setMessageConverters注册其他的转换器。

其实这点在使用的时候是察觉不到的,很多方法有一个responseType 参数,它让你传入一个响应体所映射成的对象,然后底层用HttpMessageConverter将其做映射

1

2

HttpMessageConverterExtractor<T> responseExtractor =

                new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);

 

HttpMessageConverter.java源码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

public interface HttpMessageConverter<T> {

        //指示此转换器是否可以读取给定的类。

    boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);

        //指示此转换器是否可以写给定的类。

    boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);

        //返回List<MediaType>

    List<MediaType> getSupportedMediaTypes();

        //读取一个inputMessage

    T read(Class<? extends T> clazz, HttpInputMessage inputMessage)

            throws IOException, HttpMessageNotReadableException;

        //往output message写一个Object

    void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)

            throws IOException, HttpMessageNotWritableException;

}

  

在内部,RestTemplate默认使用SimpleClientHttpRequestFactory和DefaultResponseErrorHandler来分别处理HTTP的创建和错误,但也可以通过setRequestFactory和setErrorHandler来覆盖。

2.get请求实践

2.1.getForObject()方法

1

2

3

public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables){}

public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables)

public <T> T getForObject(URI url, Class<T> responseType)

 

getForObject()其实比getForEntity()多包含了将HTTP转成POJO的功能,但是getForObject没有处理response的能力。因为它拿到手的就是成型的pojo。省略了很多response的信息。

2.1.1 POJO:

1

2

3

4

5

6

7

8

9

10

11

12

13

public class Notice {

    private int status;

    private Object msg;

    private List<DataBean> data;

}

public  class DataBean {

  private int noticeId;

  private String noticeTitle;

  private Object noticeImg;

  private long noticeCreateTime;

  private long noticeUpdateTime;

  private String noticeContent;

}

  

示例:2.1.2 不带参的get请求

    

1

2

3

4

5

6

7

8

9

10

/**

     * 不带参的get请求

     */

    @Test

    public void restTemplateGetTest(){

        RestTemplate restTemplate = new RestTemplate();

        Notice notice = restTemplate.getForObject("http://xxx.top/notice/list/1/5"

                , Notice.class);

        System.out.println(notice);

    }

  

控制台打印:

1

2

3

4

5

6

7

INFO 19076 --- [           main] c.w.s.c.w.c.HelloControllerTest         

: Started HelloControllerTest in 5.532 seconds (JVM running for 7.233)

Notice{status=200, msg=null, data=[DataBean{noticeId=21, noticeTitle='aaa', noticeImg=null,

noticeCreateTime=1525292723000, noticeUpdateTime=1525292723000, noticeContent='<p>aaa</p>'},

DataBean{noticeId=20, noticeTitle='ahaha', noticeImg=null, noticeCreateTime=1525291492000,

noticeUpdateTime=1525291492000, noticeContent='<p>ah.......'

  

示例:2.1.3 带参数的get请求1

Notice notice = restTemplate.getForObject("http://fantj.top/notice/list/{1}/{2}", Notice.class,1,5);

明眼人一眼能看出是用了占位符{1}。

示例:2.1.4 带参数的get请求2

  

1

2

3

4

5

Map<String,String> map = new HashMap();

        map.put("start","1");

        map.put("page","5");

        Notice notice = restTemplate.getForObject("http://fantj.top/notice/list/"

                , Notice.class,map);

  

明眼人一看就是利用map装载参数,不过它默认解析的是PathVariable的url形式。

2.2 getForEntity()方法

1

2

3

public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables){}

public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables){}

public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType){}

 

与getForObject()方法不同的是返回的是ResponseEntity对象,如果需要转换成pojo,还需要json工具类的引入,这个按个人喜好用。不会解析json的可以百度FastJson或者Jackson等工具类。然后我们就研究一下ResponseEntity下面有啥方法。

ResponseEntity、HttpStatus、BodyBuilder结构

ResponseEntity.java

1

2

3

4

5

6

7

8

9

public HttpStatus getStatusCode(){}

public int getStatusCodeValue(){}

public boolean equals(@Nullable Object other) {}

public String toString() {}

public static BodyBuilder status(HttpStatus status) {}

public static BodyBuilder ok() {}

public static <T> ResponseEntity<T> ok(T body) {}

public static BodyBuilder created(URI location) {}

...

  

HttpStatus.java

1

2

3

4

5

6

7

8

public enum HttpStatus {

public boolean is1xxInformational() {}

public boolean is2xxSuccessful() {}

public boolean is3xxRedirection() {}

public boolean is4xxClientError() {}

public boolean is5xxServerError() {}

public boolean isError() {}

}

  

BodyBuilder.java

1

2

3

4

5

6

7

8

public interface BodyBuilder extends HeadersBuilder<BodyBuilder> {

    //设置正文的长度,以字节为单位,由Content-Length标头

      BodyBuilder contentLength(long contentLength);

    //设置body的MediaType 类型

      BodyBuilder contentType(MediaType contentType);

    //设置响应实体的主体并返回它。

      <T> ResponseEntity<T> body(@Nullable T body);

 

可以看出来,ResponseEntity包含了HttpStatus和BodyBuilder的这些信息,这更方便我们处理response原生的东西。

示例:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

@Test

public void rtGetEntity(){

        RestTemplate restTemplate = new RestTemplate();

        ResponseEntity<Notice> entity = restTemplate.getForEntity("http://fantj.top/notice/list/1/5"

                , Notice.class);

        HttpStatus statusCode = entity.getStatusCode();

        System.out.println("statusCode.is2xxSuccessful()"+statusCode.is2xxSuccessful());

        Notice body = entity.getBody();

        System.out.println("entity.getBody()"+body);

        ResponseEntity.BodyBuilder status = ResponseEntity.status(statusCode);

        status.contentLength(100);

        status.body("我在这里添加一句话");

        ResponseEntity<Class<Notice>> body1 = status.body(Notice.class);

        Class<Notice> body2 = body1.getBody();

        System.out.println("body1.toString()"+body1.toString());

    }

 

statusCode.is2xxSuccessful()true

entity.getBody()Notice{status=200, msg=null, data=[DataBean{noticeId=21, noticeTitle='aaa', ...

body1.toString()<200 OK,class com.waylau.spring.cloud.weather.pojo.Notice,{Content-Length=[100]}>

  

当然,还有getHeaders()等方法没有举例。

3. post请求实践

同样的,post请求也有postForObject和postForEntity。

1

2

3

4

5

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 {}

  

示例

我用一个验证邮箱的接口来测试。

1

2

3

4

5

6

7

8

9

10

11

12

13

@Test

public void rtPostObject(){

    RestTemplate restTemplate = new RestTemplate();

    String url = "http://47.xxx.xxx.96/register/checkEmail";

    HttpHeaders headers = new HttpHeaders();

    headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

    MultiValueMap<String, String> map= new LinkedMultiValueMap<>();

    map.add("email", "844072586@qq.com");

    HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);

    ResponseEntity<String> response = restTemplate.postForEntity( url, request , String.class );

    System.out.println(response.getBody());

}

  

执行结果:

{"status":500,"msg":"该邮箱已被注册","data":null}

代码中,MultiValueMap是Map的一个子类,它的一个key可以存储多个value,简单的看下这个接口:

1

public interface MultiValueMap<K, V> extends Map<K, List<V>> {...}

 

为什么用MultiValueMap?因为HttpEntity接受的request类型是它。

1

2

public HttpEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers){}

//我这里只展示它的一个construct,从它可以看到我们传入的map是请求体,headers是请求头。

  

为什么用HttpEntity是因为restTemplate.postForEntity方法虽然表面上接收的request是@Nullable Object request类型,但是你追踪下去会发现,这个request是用HttpEntity来解析。核心代码如下:

1

2

3

4

5

6

7

if (requestBody instanceof HttpEntity) {

    this.requestEntity = (HttpEntity<?>) requestBody;

}else if (requestBody != null) {

    this.requestEntity = new HttpEntity<>(requestBody);

}else {

    this.requestEntity = HttpEntity.EMPTY;

}

  

我曾尝试用map来传递参数,编译不会报错,但是执行不了,是无效的url request请求(400 ERROR)。其实这样的请求方式已经满足post请求了,cookie也是属于header的一部分。可以按需求设置请求头和请求体。其它方法与之类似。

4.使用exchange指定调用方式

exchange()方法跟上面的getForObject()、getForEntity()、postForObject()、postForEntity()等方法不同之处在于它可以指定请求的HTTP类型。

但是你会发现exchange的方法中似乎都有@Nullable HttpEntity requestEntity这个参数,这就意味着我们至少要用HttpEntity来传递这个请求体,之前说过源码所以建议就使用HttpEntity提高性能。

示例

    

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

@Test

    public void rtExchangeTest() throws JSONException {

        RestTemplate restTemplate = new RestTemplate();

        String url = "http://xxx.top/notice/list";

        HttpHeaders headers = new HttpHeaders();

        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        JSONObject jsonObj = new JSONObject();

        jsonObj.put("start",1);

        jsonObj.put("page",5);

        HttpEntity<String> entity = new HttpEntity<>(jsonObj.toString(), headers);

        ResponseEntity<JSONObject> exchange = restTemplate.exchange(url,

                                          HttpMethod.GET, entity, JSONObject.class);

        System.out.println(exchange.getBody());

    }

 

这次可以看到,我使用了JSONObject对象传入和返回。
当然,HttpMethod方法还有很多,用法类似。

5.excute()指定调用方式

excute()的用法与exchange()大同小异了,它同样可以指定不同的HttpMethod,不同的是它返回的对象是响应体所映射成的对象,而不是ResponseEntity。

需要强调的是,execute()方法是以上所有方法的底层调用。随便看一个:

    

1

2

3

4

5

6

7

8

9

10

@Override

    @Nullable

    public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables)

            throws RestClientException {

        RequestCallback requestCallback = httpEntityCallback(request, responseType);

        HttpMessageConverterExtractor<T> responseExtractor =

                new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);

        return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);

转: Springboot — 用更优雅的方式发HTTP请求(RestTemplate详解)相关推荐

  1. Springboot — 用更优雅的方式发HTTP请求(RestTemplate详解)

    RestTemplate是Spring提供的用于访问Rest服务的客户端,RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率. 我之前的HTTP开发是用ap ...

  2. 用JDBC直连方式访问SQL Server 2005详解

    用JDBC直连方式访问SQL Server 2005详解 1.安装JDK,配置其环境变量:(笔者所用版本为1.6版) (1)从官方网http://java.sun.com/jdk下载安装文件. (2) ...

  3. SpringBoot默认包扫描机制及@ComponentScan指定扫描路径详解

    SpringBoot默认包扫描机制及@ComponentScan指定扫描路径详解 SpringBoot默认包扫描机制 标注了@Component和@Component的衍生注解如@Controller ...

  4. 以更优雅的方式实现弹性架构

    为什么弹性架构是重要的 谈到IT系统架构,我们经常会用建筑架构来做类比,事实上,Architecture这个词也正是来自于传统的建筑行业.系统架构图就像建筑设计图一样,用来指导软件构建.同时,两者之间 ...

  5. yum方式安装android_linux yum 命令 详解

    yum(全称为 Yellow dog Updater, Modified)是一个在Fedora和RedHat以及SUSE中的Shell前端软件包管理器.基於RPM包管理,能够从指定的服务器自动下载RP ...

  6. SpringBoot - 获取Get请求参数详解(附样例:非空、默认值、数组、对象)

    利用 Spring Boot 来制作 Web 应用,就必定会涉及到前端与后台之间互相传递参数.下面演示 Controller 如何接收以 GET 方式传递过来的参数. 一.参数直接在路径中 (1)假设 ...

  7. SpringBoot 获取 Get 请求参数详解

    叙述 利用 Spring Boot 来制作 Web 应用,就必定会涉及到前端与后台之间互相传递参数. 下面演示 Controller 如何接收以 GET 方式传递过来的参数. 解决方案 参数直接在路径 ...

  8. python元组的定义方式_序列之元组详解

    元组是序列一种,与列表类似,但是不能修改,下面我们详解介绍下元组: 1 列表近亲:元组 1.1 元组定义与访问方式: 元组与列表类似,元组语法:(ele1, ele2, ele3):具体如下: #定义 ...

  9. CDH Hadoop 基于CM方式半在线安装步骤详解

    CDH Hadoop基于Cloudera Manager半在线方式安装详解 1 CM简介 Cloudera Manager是一个端到端的管理CDH集群的工具.它通过管理CDH集群(组件安装.服务管理. ...

最新文章

  1. 对网络中安全审计产品的理解
  2. page rank算法
  3. Java基础--通过反射获取成员方法并使用
  4. Alibaba Cloud Toolkit——简介
  5. 10个用于处理日期和时间的 Python 库
  6. java安装显示更改_java 安装与配置
  7. linux临时文件创建失败,-bash: 无法为立即文档创建临时文件: 设备上没有空间
  8. python jsonpath效率低_Python学习:jsonpath的性能问题
  9. 相片堆叠瀑布流网格布局动画效果
  10. PyTorch 1.0 中文官方教程:使用 PyTorch C++ 前端
  11. MTK功能机2503 GPIO配置
  12. python如何连接sql_python连接SQL数据库
  13. ElasticSearch内存优化
  14. leetcode675. Cut Off Trees for Golf Event(Hard)
  15. iOS 开发中的日期格式
  16. Processing 案例 | 去“富士山”看樱花从树上纷纷而落
  17. 1.8.8 配置防盗链
  18. 分析DB2数据库的优势
  19. java开发工程师转大数据,一招彻底弄懂!
  20. 【回溯】B042_LQ_滑动解锁(dfs + 跨点判断)

热门文章

  1. Minimum grid
  2. Stars(树状数组)
  3. 模板:k短路(可并堆)
  4. AT2667-[AGC017D]Game on Tree【SG函数】
  5. P3185-[HNOI2007]分裂游戏【SG函数】
  6. P1332,nssl1316-血色先锋军【bfs】
  7. P2607-[ZJOI2008]骑士【基环树,树形dp】
  8. jzoj2136-(GDKOI2004)汉诺塔【找规律,模拟】
  9. 莫比乌斯反演 做题记录
  10. 2021“MINIEYE杯”中国大学生算法设计超级联赛(2)I love exam(背包)