Note.Ink
亦可赛艇的头像
发布于

SpringCloud学习之-使用RestTemplate发送http请求

上一节我们学习了如何使用jdk的URI工具类发送http请求,这一节学习一下spring框架中对于jdk的网络请求工具类的封装RestTemplate.

@RestController public class Controller { @GetMapping("hello") public String hello(){ return "hello"; } } 复制代码

首先还是接着上一节的内容。先创建一个服务端,提供上述接口。

template.getForObject("http://127.0.0.1:8763/hello",String.class); 复制代码

接着使用RestTemplate来调用该服务。可以看出,代码量上,节省了非常多。那么RestTemplate到底进行了哪些封装呢?我们从源码来探究一下。

在这儿我就以基础的getForObject方法来进行分析。

String url, Class<T> responseType, Object... uriVariables 复制代码

getForObject方法有三个参数

参数名 作用 url 请求的地址 responseType 返回值的类型 uriVariables url里面的填充字段

进入方法

public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger); return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); } 复制代码

在该方法中有三行代码,第一行封装了一个AcceptHeaderRequestCallback对象,第二行封装了一个HttpMessageConverterExtractor对象,这两对象的作用我们可以先放一放,然后第三行就是执行请求。

所以我们进入execute方法

public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor, Map<String, ?> uriVariables) throws RestClientException { URI expanded = getUriTemplateHandler().expand(url, uriVariables); return doExecute(expanded, method, requestCallback, responseExtractor); } 复制代码

在excute方法中首先做了准备工作,将我们的字符串url转化成了URI对象。这个对象正是我们上一节用以发送http请求的对象。在扩展的时候有一个expand方法,该方法的作用大致如下

Map<String,String> uriVariables = new HashMap<>(); uriVariables.put("name" ,"hello"); template.getForObject("http://127.0.0.1:8080/{name}",String.class,uriVariables); 复制代码

在url中我们使用{}输入了一个占位符name,而在uriVariables这个map中我们设置了name的值为hello,经过这个方法后,http://127.0.0.1:8080/{name} 就会变成 http://127.0.0.1:8080/hello

下面进入doExecute方法。

ClientHttpResponse response = null; try { //封装一个request对象 ClientHttpRequest request = createRequest(url, method); if (requestCallback != null) { //对request对象进行回调处理 requestCallback.doWithRequest(request); } //执行request请求 response = request.execute(); //处理返回结果 handleResponse(url, method, response); return (responseExtractor != null ? responseExtractor.extractData(response) : null); } 复制代码

除开异常处理相关代码,doExecute主要代码如上。可以看出主要逻辑还是很清晰的。

首先创建ClientHttpRequest对象

protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException { ClientHttpRequest request = getRequestFactory().createRequest(url, method); if (logger.isDebugEnabled()) { logger.debug("HTTP " + method.name() + " " + url); } return request; } public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException { HttpURLConnection connection = openConnection(uri.toURL(), this.proxy); prepareConnection(connection, httpMethod.name()); if (this.bufferRequestBody) { return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming); } else { return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming); } } 复制代码

openConnection和prepareConnection这两个方法可以自己看下,看看和上一节我们自己调用URI对象有啥区别。

接下来对request对象进行回调处理

public void doWithRequest(ClientHttpRequest request) throws IOException { if (this.responseType != null) { List<MediaType> allSupportedMediaTypes = getMessageConverters().stream() .filter(converter -> canReadResponse(this.responseType, converter)) .flatMap(this::getSupportedMediaTypes) .distinct() .sorted(MediaType.SPECIFICITY_COMPARATOR) .collect(Collectors.toList()); if (logger.isDebugEnabled()) { logger.debug("Accept=" + allSupportedMediaTypes); } request.getHeaders().setAccept(allSupportedMediaTypes); } } 复制代码

get方法的回调处理只做了一件事,就是设置请求头的"Accept"属性。

执行request请求

public final ClientHttpResponse execute() throws IOException { //校验,确认请求未执行过 assertNotExecuted(); ClientHttpResponse result = executeInternal(this.headers); //执行完成,修改请求状态 this.executed = true; return result; } protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException { byte[] bytes = this.bufferedOutput.toByteArray(); if (headers.getContentLength() < 0) { //设置请求头属性 headers.setContentLength(bytes.length); } ClientHttpResponse result = executeInternal(headers, bytes); this.bufferedOutput = new ByteArrayOutputStream(0); return result; } protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException { //将我们设置的请求头加入connection中 addHeaders(this.connection, headers); if (getMethod() == HttpMethod.DELETE && bufferedOutput.length == 0) { this.connection.setDoOutput(false); } if (this.connection.getDoOutput() && this.outputStreaming) { this.connection.setFixedLengthStreamingMode(bufferedOutput.length); } //建立连接,发送请求 this.connection.connect(); if (this.connection.getDoOutput()) { FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream()); } else { this.connection.getResponseCode(); } return new SimpleClientHttpResponse(this.connection); } 复制代码

处理返回结果

最后对结果的处理,其实猜也能猜到了,就是将返回的信息流封装成我们可以直接操作的对象,一般最常用的应该就是json转对象了吧。

作者:吾乃上将军邢道荣链接:https://juejin.im/post/5cff89636fb9a07ef819f425来源:掘金著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

本文收录于

评论