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

转自:

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请求工具:

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

本教程将带领大家实现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的六个方法制定:

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

此外,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}

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

代码中,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类型。

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

但是你会发现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);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/329884.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot整合Shiro权限框架

前言 在系统管理中&#xff0c;权限是非常重要的一个环节。目前权限框架中使用比较多的有Shiro、Spring Security。&#x1f383; 本篇简单写一下SpringBoot整合Shiro权限框架小栗子&#x1f330;。 个人博客地址&#xff1a;SpringBoot整合Shiro权限框架 介绍Shiro Apache S…

Java开发必须掌握的8种网站攻防技术

转载自 Java开发必须掌握的8种网站攻防技术 XSS攻击 XSS攻击的全称是跨站脚本攻击(Cross Site Scripting),是WEB应用程序中最常见到的攻击手段之一。跨站脚本攻击指的是攻击者在网页中嵌入恶意脚本程序, 当用户打开该网页时,脚本程序便开始在客户端的浏览器上执行,以盗取客户端…

转:json与map互转

转自&#xff1a; java中字符串&#xff0c;json&#xff0c;Map互相转换&#xff08;各种转换都有&#xff09;_蜗牛驿站-CSDN博客_java json转map前提&#xff1a;使用jar包为fastjsonimport com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject; 程序如下: …

专属微信二维码python制作_如何利用Python制作简单的公众号二维码关注图

创意配图&#xff1a;微信&#xff0c;微信公众号&#xff0c;微信大V 而且最近发现了一个新的图像处理方面的库—Wand&#xff0c;它是 ImageMagick 库的 Python 接口。于是&#xff0c;我就打算用这个库来实现简单的制作一个二维码关注图&#xff0c;主要是完成以下几个工作&…

如何迁移#SNMP到.NET Core平台的一些体会

.NET Core 依然在飞速进化中&#xff0c;所以如果不是非常喜欢折腾的性格&#xff0c;建议各位还是暂时忍耐。 准备阶段 首先&#xff0c;Visual Studio 2015是必要的开发工具。虽然它已经包含了.NET Core的原始测试版&#xff0c;这里还是推荐下载 RC1 安装包&#xff0c; htt…

JAVA实现一个图片上传预览功能

这个小项目主要使用java实现了一个简单的图片上传预览功能&#xff0c;废话不多说&#xff0c;先上实现成果 ^ _ ^&#x1f4af; 预览 登录页 主页 上传页 图片预览 项目架构 后端: SpringBoot shiro mybatis-plus druid hutool 前端: layui viewer 项目结构 ├─src├…

小程序执行运行过程原理_PLC的基础小知识!不用把PLC想的太难

PLC实质上是工业计算机&#xff0c;是计算机技术与传统继电接触器控制器技术相结合的产物&#xff0c;只不过比一般的计算机具有更强的与工业过程相连接的接口和更直接的适用于工业控制要求的编程语言。一、PLC的结构从硬件结构上看&#xff0c;PLC主要由中央处理单元(CPU)、存…

内部局域网可自行分配的ip地址

【1】有些ip地址很特殊 以 10. 、 172.16. 、172.31. 、192.168. 开头的所有ipv4地址都未分配。 更确切的说&#xff0c;这些地址不会被 ISP&#xff08;Internet服务提供商&#xff09;分配给广域网上的计算机&#xff0c;即无法加入全球Internet网络-广域网&#xff1b; 但…

以吃货的角度理解 IaaS,PaaS,SaaS 是什么

转载自 以吃货的角度理解 IaaS&#xff0c;PaaS&#xff0c;SaaS 是什么随着云计算时代的到来&#xff0c;越来越多的软件&#xff0c;开始采用云服务。越来越多的概念也随之而来。云服务只是一个统称&#xff0c;可以分成三大类。IaaS&#xff1a;基础设施服务&#xff0c;Inf…

.Net使用RabbitMQ详解

序言 这几天呢&#xff0c;公司风波再起&#xff0c;去年一年公司CTO换啦4任&#xff0c;CEO换啦三个&#xff0c;这不刚来个新老大&#xff0c;感觉还不错&#xff0c;却没干过3个月又要走&#xff0c;索性趁老大们走来走去的时候&#xff0c;就给自己空出来&#xff0c;稍稍总…

SpringBoot整合Redis要注意的那些

前言 昨天自己在重新学习SpringBoot整合Redis时&#xff0c;遇到了一个问题java.lang.NoClassDefFoundError: org/apache/commons/pool2/impl/GenericObjectPoolConfig&#xff0c;错误很明显找不到需要的类。下面主要记录一下错误是怎么出线的&#xff0c;并且如何解决。 错…

Java之Socket与HTTP区别

转自&#xff1a; Java之Socket与HTTP区别 - 曹刚 - 博客园我们都知道TCP/IP协议共分四层&#xff1a;①链路层&#xff0c;有时也称作数据链路层或网络接口层&#xff0c;通常包括操作系统中的设备驱动程序和计算机中对应的网络接口卡。它们一起处理与电缆&#xff08;或其他…

人人都能掌握的Java服务端性能优化方案

转载自 人人都能掌握的Java服务端性能优化方案 作为一个Java后端开发&#xff0c;我们写出的大部分代码都决定着用户的使用体验。如果我们的代码性能不好&#xff0c;那么用户在访问我们的网站时就要浪费一些时间等待服务器的响应。这就可能导致用户投诉甚至用户的流失。 关于性…

python模板模式_python-模板方法模式

说明&#xff1a; 模板方法模式时行为模式中比较简单的设计模式之一。模板方法关注这样的一类行为&#xff1a;该类行为在执行过程中拥有大致相同的动作次序&#xff0c;只是动作在实现的具体细节上有所差异。例如&#xff1a;泡茶和泡咖啡&#xff0c;泡茶&#xff1a;把水煮沸…

nacos启动失败:org.springframework.boot.web.server.WebServerExceptio

准备环境 系统环境: windows nacos: 2.0.0-BETA 错误信息 org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat 配置文件…

post使用form-data和x-www-form-urlencoded的本质区别

转自&#xff1a; post使用form-data和x-www-form-urlencoded的本质区别_null-CSDN博客一是数据包格式的区别&#xff0c;二是数据包中非ANSCII字符怎么编码&#xff0c;是百分号转码发送还是直接发送一、application/x-www-form-urlencoded1、它是post的默认格式&#xff0c;…

使用Nancy打造TaskManager2.0管理系统

上一篇开源任务管理平台TaskManager介绍发布后&#xff0c;有网友联系我看看能不能做个后台管理界面&#xff0c;方便管理系统中所有的任务。由于时间和技术问题1.0版本的时候&#xff0c;新增了一个3分钟读取配置文件动态修改任务的功能&#xff0c;不过总体来说还是不直观&am…

Java对象的序列化与反序列化

转载自 Java对象的序列化与反序列化 序列化与反序列化 序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。一般将一个对象存储至一个储存媒介&#xff0c;例如档案或是记亿体缓冲等。在网络传输过程中&#xff0c;可以是字节或是XML等格式。而字节的…

网站能拿到其他网站的cookie_网站能给公司带来哪些好处?

事实上&#xff0c;许多企业仍在努力建立一个网站&#xff0c;因为如果它建立起来&#xff0c;它需要一笔钱和其他费用。但此时客户业务不太好&#xff0c;企业客户需要考虑很多问题&#xff0c;但创建网站的企业已经尝到了甜头。其实有些朋友可能会有一些偏差。例如&#xff0…

笨办法学习@ConditionalOnProperty 烧脑配置记录

前言 今天继续学习springboot时&#xff0c;一不小心就被ConditionalOnProperty注解的配置真假搞得我真的变得真真假假了。。&#xff08;此为真&#xff0c;彼为假&#xff0c;到底你是真还是你是假&#xff0c;晕了晕了。。。&#xff09; 本片主要记录一下注解的真假情况 …