4

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

 3 years ago
source link: https://segmentfault.com/a/1190000040536923
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

1 restTemplate — spring 提供

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

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

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

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

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

参考:https://www.huaweicloud.com/a...

国外知名博客Baeldung的博客 The Guide to RestTemplate: https://www.baeldung.com/rest...

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 传参。

参考:https://stackoverflow.com/que...

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

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK