乐趣区

关于java:Java调用外部REST请求的几种方式

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;
}
退出移动版