天天看点

Java调用外部REST请求的几种方式

1 restTemplate — spring 提供

特点:

1、RestOperations 提供了各种封装方法,非常方便直接将返回转成实体类。

2、默认使用JDK 的HttpURLConnection进行通信,但是可以通过RestTemplate.setRequestFactory 切换到不同的HTTP源:如Apache HttpComponents、Netty、OkHttp。

3、支持同步、异步请求;

4、支持更多的定制,比如拦截器等。

ps:支持 get 请求,参数是 body 的形式。

1.1 底层是java的HttpURLConnection(默认使用,可以定制)

所有的请求都需要执行 doExecute() 方法

@Nullable
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
Assert.notNull(url, "URI is required");
Assert.notNull(method, "HttpMethod is required");
ClientHttpResponse response = null;

Object var14;
try {
// 创建请求
ClientHttpRequest request = this.createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
        }

response = request.execute();
this.handleResponse(url, method, response);
var14 = responseExtractor != null ? responseExtractor.extractData(response) : null;
    } catch (IOException var12) {
String resource = url.toString();
String query = url.getRawQuery();
resource = query != null ? resource.substring(0, resource.indexOf(63)) : resource;
throw new ResourceAccessException("I/O error on " + method.name() + " request for \"" + resource + "\": " + var12.getMessage(), var12);
    } finally {
if (response != null) {
response.close();
        }

    }

return var14;
}
      

HttpAccessor 创建请求

public abstract class HttpAccessor {
    ... // 省略代码无数
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
// 使用 ClientHttpRequestFactory 创建请求
ClientHttpRequest request = this.getRequestFactory().createRequest(url, method);
if (this.logger.isDebugEnabled()) {
this.logger.debug("HTTP " + method.name() + " " + url);
        }

return request;
    }
}
      

ClientHttpRequestFactory接口的具体实现,如:SimpleClientHttpRequestFactory 创建请求

public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
// 使用 HttpURLConnection 创建请求
HttpURLConnection connection = this.openConnection(uri.toURL(), this.proxy);
this.prepareConnection(connection, httpMethod.name());
return (ClientHttpRequest)(this.bufferRequestBody ? new SimpleBufferingClientHttpRequest(connection, this.outputStreaming) : new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming));
}
      

1.2 post 请求,返回直接封装为实体

RestTemplate restTemplate = new RestTemplate();
HttpEntity<Foo> request = new HttpEntity<>(new Foo("bar"));
Foo foo = restTemplate.postForObject(fooResourceUrl, request, Foo.class);
assertThat(foo, notNullValue());
assertThat(foo.getName(), is("bar"));
      

1.3 get请求,但是参数是body形式

一般 get 请求,不支持 body 传参。​

HTTP GET with a body is a somewhat unconventional construct that falls in a gray area of the HTTP specification - the end result is that many older pieces of software either cannot handle such a request at all, or will explicitly reject it because they believe it to be malformed.

带有body参数的HTTP GET是一种非传统的构造,属于HTTP规范的灰色区域。最终的结果是,许多旧的软件要么根本不能处理这样的请求,要么会明确拒绝,因为他们认为它是格式错误的请求。

/**
* 注意:get请求,但是参数是body形式
*
* @param url
* @param paramBody
* @return
*/
private String getWithBody(String url, Map<String, Object> paramBody) {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
httpHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity requestEntity = new HttpEntity(JsonUtil.of(paramBody), httpHeaders);
RestTemplate template = getTemplate();
ResponseEntity response = template.exchange(url, HttpMethod.GET, requestEntity, String.class);
Object result = response.getBody();
logger.info("/invokeThirdPartyRequest/getWithBody/result/[{}]", result.toString());
return result.toString();
}
      
/**
* 获取 RestTemplate
*
* @return
*/
private RestTemplate getTemplate() {
RestTemplate restTemplate = new RestTemplate();
//修改restTemplate的RequestFactory使其支持Get携带body参数
restTemplate.setRequestFactory(new HttpComponentsClientRestfulHttpRequestFactory());
return restTemplate;
}

      
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;

import java.net.URI;

public class HttpComponentsClientRestfulHttpRequestFactory extends HttpComponentsClientHttpRequestFactory {

@Override
protected HttpUriRequest createHttpUriRequest(HttpMethod httpMethod, URI uri) {
if (httpMethod == HttpMethod.GET) {
return new HttpGetRequestWithEntity(uri);
        }
return super.createHttpUriRequest(httpMethod, uri);
    }

/**
* 定义HttpGetRequestWithEntity实现HttpEntityEnclosingRequestBase抽象类,以支持GET请求携带body数据
*/
private static final class HttpGetRequestWithEntity extends HttpEntityEnclosingRequestBase {
public HttpGetRequestWithEntity(final URI uri) {
super.setURI(uri);
        }
@Override
public String getMethod() {
return HttpMethod.GET.name();
        }
    }
}
      

2 HttpUtil — hutool 提供

HttpUtil 其实是 HttpRequest 的封装。

它支持各种封装好的get、post、put请求。

2.1 get 请求

public static String get(String urlString, Charset customCharset) {
return ((HttpRequest)HttpRequest.get(urlString).charset(customCharset)).execute().body();
}

public static String get(String urlString) {
return get(urlString, HttpGlobalConfig.timeout);
}

public static String get(String urlString, int timeout) {
return HttpRequest.get(urlString).timeout(timeout).execute().body();
}

// form 表单格式的入参
public static String get(String urlString, Map<String, Object> paramMap) {
return HttpRequest.get(urlString).form(paramMap).execute().body();
}

// form 表单格式的入参,并设置超时时间
public static String get(String urlString, Map<String, Object> paramMap, int timeout) {
return HttpRequest.get(urlString).form(paramMap).timeout(timeout).execute().body();
}
      

2.2 post 请求

这些请求最终调用的都是 HttpRequest 的 execute() 方法。

// form 表单格式的入参
public static String post(String urlString, Map<String, Object> paramMap) {
return post(urlString, paramMap, HttpGlobalConfig.timeout);
}

// form 表单格式的入参,并设置超时时间
public static String post(String urlString, Map<String, Object> paramMap, int timeout) {
return HttpRequest.post(urlString).form(paramMap).timeout(timeout).execute().body();
}

// body 格式入参
public static String post(String urlString, String body) {
return post(urlString, body, HttpGlobalConfig.timeout);
}

// body 格式入参,并设置超时时间
public static String post(String urlString, String body, int timeout) {
return HttpRequest.post(urlString).timeout(timeout).body(body).execute().body();
}
      

2.3 一个例子

Map<String, Object> param = new HashMap<>();
param.put("userId", userId);
String res = HttpUtil.post(url, JsonUtil.of(param));
      

3 HttpRequest — hutool 提供

HttpRequest 提供了非常方便构造请求的构造函数。当参数比较多、header比较多的时候,可以使用这种方式。(这里使用了构造模式)

3.1 底层是Java的HttpURLConnection

HttpRequest 底层又是使用了 java 提供的 ​

​HttpURLConnection​

上源码:

最终都需要执行这个execute方法,这个方法调用了hutool封装的HttpConnection,这个​

​HttpConnection​

​又使用了java提供的​

​HttpURLConnection​

​。

// hutool 执行方法
public HttpResponse execute(boolean isAsync) {
this.urlWithParamIfGet();
this.initConnection();
this.send();
HttpResponse httpResponse = this.sendRedirectIfPossible();
if (null == httpResponse) {
httpResponse = new HttpResponse(this.httpConnection, this.charset, isAsync, this.isIgnoreResponseBody());
    }

return httpResponse;
}
      
public class HttpConnection {
private final URL url;
private final Proxy proxy;
// 这个连接 HttpURLConnection ,是java提供的
private HttpURLConnection conn;
    ...// 省略无数代码
}
      

3.2 一个例子

private String invoke(String url, String isMock, Map<String, Object> map) {
String result = HttpRequest.post(url).body(JSONUtil.toJsonStr(map)).execute().body();
return result;
}
      

所有发生在我们身上的事件都是一个经过仔细包装的礼物。只要我们愿意面对它有时候有点丑恶的包装,带着耐心和勇气一点一点的拆开包装的话,我们会惊喜的看到里面珍藏的礼物。

----遇见未知的自己