系统定制开发RestTemplate

简介

  • RestTemplate是由Spring系统定制开发框架提供的一个可用于系统定制开发应用中调用rest系统定制开发服务的类它简化了与http系统定制开发服务的通信方式,统一了RESTFul的标准,封装了http连接,系统定制开发我们只需要传入url系统定制开发及其返回值类型即可。系统定制开发相较于之前常用的HttpClientRestTemplate系统定制开发是一种更为优雅的调用RESTFul系统定制开发服务的方式。
  • Spring系统定制开发应用程序中访问第三方REST服务与使用Spring RestTemplate类有关。RestTemplate系统定制开发类的设计原则与许多其他Spring的模板类(例如JdbcTemplate)相同,系统定制开发为执行复杂任务提供了系统定制开发一种具有默认行为的简化方法。
  • RestTemplate默认依赖JDK提供了http系统定制开发连接的能力(HttpURLConnection),系统定制开发如果有需要的话也可以通过setRequestFactory系统定制开发方法替换为例如Apache HttpCompoent、Netty或OKHttp等其他Http libaray
  • 考虑到了RestTemplate系统定制开发类是为了调用REST系统定制开发服务而设计的,系统定制开发因此它的主要方法与REST系统定制开发的基础紧密相连就不足为奇了,后者时HTTP系统定制开发协议的方法:HEAD、GET、POST、PUT、DELETE、OPTIONS例如,RestTemplate类具有headForHeaders()、getForObject()、putForObject(),put()和delete()等方法。

创建RestTemplate

​ 因为RestTemplateSpirng系统定制开发提供的所以只要是一个Springboot项目就不用考虑导包的问题,这些都是提供好的。

​ 但是Spring并没有将其加入SpringBean容器中,需要我们手动加入,因为我们首先创建一个Springboot配置类,再在配置类中将我们的RestTemlate注册到Bean容器中

方法一

​ 使用Springboot提供的RestTemplateBuilder构造类来构造一个RestTemplate,可以自定义一些连接参数,如:连接超时时间,读取超时时间,还有认证信息等

@Configurationpublic class WebConfiguration {    @Bean    public RestTemplate restTemplate(RestTemplateBuilder builder){        return builder                //设置连接超时时间                .setConnectTimeout(Duration.ofSeconds(5000))                //设置读取超时时间                .setReadTimeout(Duration.ofSeconds(5000))                //设置认证信息                .basicAuthentication("username","password")                //设置根路径                .rootUri("https://api.test.com/")                //构建                .build();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

添加自定义的

​ 自定义拦截器示例

@Slf4jpublic class CustomClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {    @Override    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {        //打印请求明细        logRequestDetails(request,body);        ClientHttpResponse response = execution.execute(request, body);        //打印响应明细        logResponseDetails(response);                return response;    }    private void logRequestDetails(HttpRequest request, byte[] body){        log.debug("Headers:{}",request.getHeaders());        log.debug("body:{}",new String(body, StandardCharsets.UTF_8));        log.debug("{}:{}",request.getMethod(),request.getMethodValue());    }    private void logResponseDetails(ClientHttpResponse response) throws IOException {        log.debug("Status code : {}",response.getStatusCode());        log.debug("Status text : {}",response.getStatusText());        log.debug("Headers : {}",response.getHeaders());        log.debug("Response body: {}", StreamUtils.copyToString(response.getBody(),StandardCharsets.UTF_8));    }}
  • 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
  • 26
  • 27

​ 使用RestTemplateBuilder构造类,添加自定义拦截器,构造带有自定义拦截器的RestTemplate实例

@Configurationpublic class WebConfiguration {    @Bean    public RestTemplate restTemplate(RestTemplateBuilder builder){        return builder                .additionalInterceptors(new CustomClientHttpRequestInterceptor())                //构建                .build();    }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

​ 测试请求确实经过了拦截器,注册成功(注意请求和响应的流只会被读取一次,这里我们读取了response后返回的response就读取不到刚刚读过的内容了)


方法二

​ 使用RestTemplate构造方法构造一个RestTemlate,虽然不能像RestTemplate构造类那样更详细、更多样的配置参数,但是RestTemplate构造方法在一般情况是够用的。

  • 无参构造 全部参数默认
  • 指定ClientHttpRequestFactory 的构造方法可以指定自己实现的ClientHttpRequestFactory(客户端http请求工厂)其他的与无参构造相同。
    • ClientHttpRequestFactory
  • 指定List<HttpMessageConverter<?>>的构造方法可以指定自己是实现的HttpMessageConverterHttp消息转换器)传入其他与无参构造相同。
@Configurationpublic class WebConfiguration {    @Bean    public RestTemplate restTemplate(){        return new RestTemplate();    }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

​ 两者方法都可使用,前者提供了多样的自定义参数的选择,可以将RestTemplate配置的更为完善,后者则简化了配置虽然配置多样性不如前者,但是日常使用调用些API还是足以使用

RestTemplate API使用

​ 在使用RestTemplate前先让我们看看RestTemplate有哪些API

​ 相信大家看到这么多方法,一定很头大,但是我们仔细看上述的方法,我们可以提取出主要的几种方法是(这里只讨论Http请求的):

  • GET
  • POST
  • PUT
  • DELETE
  • HEAD
  • OPTIONS
  • EXCHANGE
  • EXECUTE

这里我给大家安利一个一个网站,它提供免费的RESTFul api的样例测试。


GET

​ 通过上图我们可以发现RestTemlate发送GET请求的方法有两种

  • public <T> T getForObject(...)
  • public <T> ResponseEntity<T> getForEntity(...)

getForEntity()

​ 后缀带有Entity的方法都代表返回一个ResponseEntity<T>ResponseEntity<T>是Spring对HTTP请求响应的封装,包括了几个重要的元素,如响应码,contentType、contentLength、响应消息体等

​ 通过它继承父类(HttpEntity<T>)的getHeader()方法我们可以获取contentType、contentLength、响应消息体等。比如下面这个例子。

public void queryWeather() {        ResponseEntity<Object> forEntity = restTemplate.getForEntity("https://restapi.amap.com/v3/weather/weatherInfo?city=510100&key=e7a5fa943f706602033b6b329c49fbc6", Object.class);        System.out.println("状态码:"+forEntity.getStatusCode());        System.out.println("状态码内容:"+forEntity.getStatusCodeValue());        HttpHeaders headers = forEntity.getHeaders();        System.out.println("响应头:"+headers);        Object body = forEntity.getBody();        System.out.println("响应内容:"+body);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

​ 该例子中getForEntity()方法的第一个参数为我要调用服务的URL,第二个参数则为响应内容的类的类型(Java嘛 万物皆对象)还可以添加第三个参数,第三个参数为一个可变参数 代表着调用服务时的传参。

第三个参数可以使用key-value的map来传入参数

get请求也可通过向在url上添加查询参数来发送带有请求的参数


getForObject()

​ 相比于前者getForEntity()该方法则是,更偏向于直接获取响应内容的,因为他直接返回响应实体的body(响应内容),。比如下面这个例子

public void queryWeather() {                Object body = restTemplate.getForObject("https://restapi.amap.com/v3/weather/weatherInfo?city=510100&key=e7a5fa943f706602033b6b329c49fbc6", Object.class);        System.out.println(body);    }
  • 1
  • 2
  • 3
  • 4
  • 5
方法参数签名与`getForEntity()`基本一致。
  • 1

​ 当你只需要返回的响应内容时,使用getForObject()是一个很好的选择,但当你需要获得更详细的响应信息,如响应头中的信息,你就只能选择getForEntity()了。


POST

POST请求有如下三种方法

  • public URI postForLocation(...)
  • public <T> T postForObject(...)
  • public <T> ResponseEntity<T> postForEntity(...)

​ 后两种用法与GET基本一致不做详细介绍,这里着重介绍postForLocation()


postForEntity()

​ 该方法有三个参数,第一个为调用服务的地址(URL)

​ 第二个参数表示上传的参数(json格式提交)

​ 第三个表示返回响应内容的具体类型

​ 第四个参数也用于指定参数(在URL中添加)

@Override    public void queryWeather() {        User user = new User();        user.setName("鲁大师");        ResponseEntity<Object> objectResponseEntity = restTemplate.postForEntity("https://restapi.amap.com/v3/weather/weatherInfo?city=510100&key=e7a5fa943f706602033b6b329c49fbc6", user, Object.class);        System.out.println("消息响应内容:"+objectResponseEntity.getBody());    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

postForObject()

​ 使用方法与getForObject类似只是多了一个传入对象参数(传入方式与postForEntity()相同)

public void queryWeather() {        User user = new User();        user.setName("鲁大师");        ResponseEntity<Object> objectResponseEntity = restTemplate.postForEntity("https://httpbin.org/post", user, Object.class);        MediaType contentType = objectResponseEntity.getHeaders().getContentType();        System.out.println(contentType);        System.out.println("消息响应内容:"+objectResponseEntity.getBody());    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

postForLocation()

postForLocation传参用法与前两者一致,只不过返回从实体变成了一个URL,因此它不需要指定返回响应内容的类型。

public void queryWeather() {        User user = new User();        user.setName("鲁大师");        URI uri = restTemplate.postForLocation("https://httpbin.org/post", user);        System.out.println(uri);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这个只需要服务提供者返回一个 URI 即可,该URI返回值体现的是:用于提交完成数据之后的页面跳转,或数据提交完成之后的下一步数据操作URI

使用POST以表单方式提交

​ 这里我们着重说一下,如何自己封装一个请求体。

​ 我们需要用到如下几个类

  • HttpHeaders
  • MultiValueMap<K,V>
  • HttpEntity<T>
HttpHeaders

​ 故名思意,就是用来封装Http请求的请求头的,这里我们要设置他的ContentType为**MediaType.APPLICATION_FORM_URLENCODED**以使得我们提交的参数是以Form(表单)的形式提交。

//设置请求头, x-www-form-urlencoded格式的数据        HttpHeaders httpHeaders = new HttpHeaders();        //这里指定参数以UTF-8编码格式传输        MediaType mediaType = new MediaType(MediaType.APPLICATION_FORM_URLENCODED, UTF_8);        httpHeaders.setContentType(mediaType);        //提交参数设置        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();        map.add("name","鲁大师");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

MultiValueMap<K,V>

​ 该类是用来封装请求参数的,是以key-value的形式封装但是以单个key对应多个value的格式传输(也就是是以单个key:[value...]的格式传输的)。

//提交参数设置        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();        map.add("name","鲁大师");
  • 1
  • 2
  • 3

如果像传输单个key对应单个value使用普通的Map传参即可


HttpEntity<T>

​ 该类是用来封装请求的,主要作用就是将请求头和请求体封装在一起成为一个请求实体 T用来指定用来封装参数的容器的类型。

//组装请求体        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, httpHeaders);
  • 1
  • 2

测试

通过上述介绍后,我们就可以自己封装一个以form形式提交参数的POST请求了。

@Test    void contextLoads() {        //请求地址        String url = "https://httpbin.org/post";        //设置请求头, x-www-form-urlencoded格式的数据        HttpHeaders httpHeaders = new HttpHeaders();        httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);        //提交参数设置        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();        map.add("name","鲁大师");        //组装请求体        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, httpHeaders);        //发送post请求并打印结果 以String类型接收响应结果JSON字符串        String s = restTemplate.postForObject(url, request, String.class);        System.out.println(s);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

​ 通过拦截器拦截了请求并对请求头进行拆包,可以发现ContentType已经被修改成了x-www-form-urlencoded格式了。

PUT

PUT请求的方法只有一类

  • void put()

PUT()

​ 使用方法与postForEntity()参数基本一致,只是put方法没有返回值(也就不必去设置响应内容的类型了)。

@Test    void contextLoads() {        //请求地址        String url = "http://httpbin.org/put";        User user = new User();        user.setName("鲁大师");        restTemplate.put(url,user);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

DELETE

​ 与PUT一样,DELETE方法只有一类

  • void delete()

delete()

delete()可以指定url中的中的参数,但是RestTemplatedelete()方法是不支持上传requestBody的。

void contextLoads() {    //请求地址    String url = "http://httpbin.org/delete";    restTemplate.delete(url);}
  • 1
  • 2
  • 3
  • 4
  • 5

HEADER也只有一类方法

  • public HttpHeaders headForHeaders()

​ 主要用来发送请求获取响应头部信息,但是像DELETEPUT这类没有响应的方法,是不能使用该方法的(因为没有响应也就没有响应头了)。

@Test    void contextLoads() {        //请求地址        String url = "http://httpbin.org/get";        HttpHeaders httpHeaders = restTemplate.headForHeaders(url);        System.out.println(httpHeaders);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7


OPTIONS

  • public Set<HttpMethod> optionsForAllow()

​ 该方法的主要用来判断该服务地址,能够使用那种方法去执行

	@Test    void contextLoads() {        //请求地址        String url = "http://httpbin.org/get";        Set<HttpMethod> httpMethods = restTemplate.optionsForAllow(url);        System.out.println(httpMethods);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7


EXCHANGE

  • <T> ResponseEntity<T> exchange()

​ 该接口与其他接口不同

  • 该方法允许用户指定请求的方法(get,post,put等)
  • 可以在请求中增加body以及头信息,其内容通过参数HttpEntity<?> requestEntity描述
  • exchange支持’含参数的类型(即泛型)'作为返回类型,该特性通过ParameterizedTypeReferenceresponseType 描述

​ 该方法支持五个参数

  • 第一个是服务地址
  • 第二个是请求方法
  • 第三个是写入的请求实体
  • 第四个是响应内容的类型
  • 第五个是扩展模板的变量或包含URI模板变量的映射
@Testvoid contextLoads() {    //请求地址    String url = "http://httpbin.org/post";    User user = new User();    user.setName("彭于晏");    HttpHeaders httpHeaders = new HttpHeaders();    httpHeaders.setContentType(MediaType.APPLICATION_JSON);    HttpEntity<User> userHttpEntity = new HttpEntity<>(user, httpHeaders);    ResponseEntity<Object> exchange = restTemplate.exchange(url, HttpMethod.POST, userHttpEntity, Object.class);    System.out.println(exchange);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

​ 上述代码模拟了一个简单的POST请求 可以理解为可以动态的指定请求方法和请求实体的一个方法。

响应实体


EXECUTE

  • <T> T execute()

​ 该方法就是执行请求的方法,我们可以发现上述的所有方法的最后执行都是调用的该方法执行,所以他在RestTemplate中十分重要

​ 该方法有五个参数

  • 服务地址
  • 请求的方法
  • 准备请求的对象(requestCallback
  • 从响应中提取返回值的对象
  • 扩展模板的变量或包含URI模板变量的映射

execute()

@Override	@Nullable	public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,			@Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {		URI expanded = getUriTemplateHandler().expand(url, uriVariables);		return doExecute(expanded, method, requestCallback, responseExtractor);	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

​ 通过上述源码我们可以发现execute()方法只是将我们传入的String类型的URL转换为了URL类型,最后执行请求是由doExecute()方法


doExecute()

​ 这里需要了解两个类:RequestCallbackResPonseExtractor

RequestCallback: 用于操作请求头和body,在请求发出前执行。不需要关心关闭请求或处理错误:这都将由RestTemplate处理。

​ 该接口有两个实现类:

ResPonseExtractor: 解析HTTP响应的数据,而且不需要担心异常和资源的关闭。

​ 该接口在RestTemplate中同样有两个实现类:

HeadersExtractor提取响应HttpHeaders的响应提取器。直接提取响应体中的响应头
ResponseEntityResponseExtractor<T>HttpEntity的响应提取器。可以获取响应实体里面包括响应头,响应体等。具体请查看HttpEntity
@Testvoid contextLoads() {    //请求地址    String url = "http://httpbin.org/post";    User user = new User();    user.setName("彭于晏");    HttpHeaders httpHeaders = new HttpHeaders();    httpHeaders.setContentType(MediaType.APPLICATION_JSON);    HttpEntity<User> userHttpEntity = new HttpEntity<>(user, httpHeaders);    ResponseEntity<Object> execute = restTemplate.execute(url, HttpMethod.POST, restTemplate.httpEntityCallback(userHttpEntity), restTemplate.responseEntityExtractor(Object.class));    System.out.println(execute);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12


解惑

  • 前面我们介绍方法的时候发现有个一个可变参数,那个参数被描述成了扩展模板的变量或是包含URI模板变量的映射

我们来简单看一下这个参数,我们知道请求传参可以通过url拼接参数的方式传参,拼接参数也分为两种:

  • 路径中嵌入占位的格式(http://httpbin.org/{1}/post)也叫模板映射
  • 末尾添加Key-value格式(http://httpbin.org/post?name="彭于晏")即扩展模板的变量
  • 当我们最后一参数传入map时会以key-value的格式拼接在URL后(通俗的说就是这样设置的变量会跟着URL路径后面)

http://httpbin.org/post?name="彭于晏"

@Testvoid contextLoads() {    //请求地址    String url = "http://httpbin.org/get";    HashMap<String, String> map = new HashMap<>();    map.put("name","彭于晏");    Object forObject = restTemplate.getForObject(url, Object.class, map);    System.out.println(forObject);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 当我们传入简单的对象如String,Integer时且路径中有嵌入的占位符时就会代替调用URL中占位符
@Testvoid contextLoads() {    //请求地址    String url = "http://httpbin.org/{2}/get";    HashMap<String, String> map = new HashMap<>();    Object forObject = restTemplate.getForObject(url, Object.class, 99);    System.out.println(forObject);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8


参考


-End-

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发