【问题标题】:Spring Boot http call to remote web serviceSpring Boot http 调用远程 Web 服务
【发布时间】:2019-06-02 08:23:04
【问题描述】:

我必须使用 Spring Boot 2.0.5

从我的 Web 应用程序构建中调用远程 REST 端点

我可以使用 HttpURLConnection,虽然 Spring 有 RestTemplate,但我检查了它是什么,发现它很快就会被弃用:

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html

此页面还提到了通过 HTTP 调用的新类。可以同步和异步方式使用它:

WebClient 提供了 RestTemplate 的现代替代方案 对同步和异步以及流式传输的有效支持 场景

问题是我在 WebClient 的 javadoc 中没有看到任何关于同步工作方式的说明:

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.html

WebClient 的另一个问题 - 要使其正常工作,我需要在类路径 https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-webclient.html 中有 WebFlux

但这会破坏我的 Spring Security 配置,因为它以同步方式构建。据我了解,一旦拥有 WebFlux,Spring Security 将使用异步配置。

如何使用 Spring 对远程端点进行 http 调用,还是应该避免使用 HttpURLConnection 类(或 Apache 的库)?

更新

WebFlux 似乎不会以同步方式对 Spring Security 造成任何问题。

另外请注意我的应用程序不是反应式的 - 它是多线程的(抱歉,如果我之前不清楚)。我有交易,所以被动的方法似乎不适合我的情况。

【问题讨论】:

  • 您仍然可以使用RestTemplate,也可以使用WebClient 确实需要Web Flux,但这并不意味着您的整个应用程序都需要响应式。您可以将应用程序的类型设置为 SERVLET,它仍将使用常规机制。
  • 我可以使用 RestTemplate,但是 Spring 团队专注于 WebClient 有什么意义。 RestTemplate 将被弃用。我尝试使用 WebClient。似乎我对 WebFlux 有误解——它不会导致传统的(非反应性)应用程序问题,但 WebClient.Builder 不是线程安全的,我不知道如何正确使用它。这是我关于它的新问题stackoverflow.com/questions/54136085/…
  • 您应该使用一次Builder 来创建WebClient 的实例并重复使用它。但是 Spring Boot 已经为你配置了一个WebClient,那你为什么需要一个新配置的呢?!

标签: java spring http spring-boot


【解决方案1】:

您可以使用 Spring org.springframework.web.client.AsyncRestTemplate 进行异步 Rest 调用。以下是我用于同步和异步调用的实用程序之一。下面是用于异步的 Rest 实用程序和 CallBack。

/**
 *
 */
package com.debopam.services.policyenquiryservice.rest.util;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.web.client.AsyncRestTemplate;

/**
 * @author dpoddar
 *
 */
public class RestUtil {

    private String url;
    private HttpMethod httpMethod;
    private MultiValueMap<String, String> headers;
    private Map<String, Object> params;
    private Class<?> responseType;
    private List<Object> uriVariables;

    private HttpEntity<Object> httpEntity;

    //AsyncRestTemplate asyncRestTemplate = (AsyncRestTemplate) ContextProvider.getBean("customAsyncRestTemplate");

    /**
     * @param url
     * @param httpMethod
     * @param headers
     * @param params
     * @param responseType
     * @param uriVariables
     */
    public RestUtil(String url, HttpMethod httpMethod, MultiValueMap<String, String> headers,
            Map<String, Object> params, Class<?> responseType, List<Object> uriVariables) {
        super();
        this.url = url;
        this.httpMethod = httpMethod;
        this.headers = headers;
        this.params = params;
        this.responseType = responseType;
        this.uriVariables = uriVariables;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public Foo callServicesync(RestTemplate restTemplate) {

        //DO a sync Call
        HttpEntity<Foo> request = new HttpEntity<>(new Foo("bar"));
        Foo foo = restTemplate.postForObject(fooResourceUrl, request, Foo.class);

    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public void callServiceAsync(AsyncRestTemplate asyncRestTemplate,ResponseCallBack responseCallBack) {

        if(asyncRestTemplate.getMessageConverters().isEmpty()){
            asyncRestTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
        }

        ListenableFuture restCall = null;
        if(null != uriVariables){
            restCall = asyncRestTemplate.exchange(this.url, this.httpMethod, this.httpEntity, responseType,uriVariables);
        }else{
            restCall = asyncRestTemplate.exchange(this.url, this.httpMethod, this.httpEntity, responseType);
        }

        restCall.addCallback(responseCallBack);

    }

    public static class RestUtilBuilder {
        private String url;
        private HttpMethod httpMethod;
        private MultiValueMap<String, String> headers;
        private Map<String, Object> params;
        private Class<?> responseType;
        private List<Object> uriVariables;

        public RestUtilBuilder url(String url) {
            this.url = url;
            return this;
        }

        public RestUtilBuilder httpMethod(HttpMethod httpMethod) {
            this.httpMethod = httpMethod;
            return this;
        }

        public RestUtilBuilder headers(MultiValueMap<String, String> headers) {
            this.headers = headers;
            return this;
        }

        public RestUtilBuilder addHeader(String key,String value) {
            if(null == this.headers){
                this.headers = new LinkedMultiValueMap<>();
            }
            this.headers.add(key, value);
            return this;
        }

        public RestUtilBuilder params(Map<String, Object> params) {
            this.params = params;
            return this;
        }

        public RestUtilBuilder addparam(String key,Object value) {
            if(null == this.params){
                this.params = new HashMap<>();
            }
            this.params.put(key, value);
            return this;
        }

        public RestUtilBuilder responseType(Class<?> responseType) {
            this.responseType = responseType;
            return this;
        }

        public RestUtilBuilder uriVariables(List<Object> uriVariables) {
            this.uriVariables = uriVariables;
            return this;
        }

        public RestUtil build() {
            RestUtil util = new RestUtil(url, httpMethod, headers, params, responseType, uriVariables);
            util.httpEntity = new HttpEntity<Object>(util.params, util.headers);
            return util;
        }
    }

    public static RestUtilBuilder restUtil() {
        return new RestUtilBuilder();
    }

}



package com.debopam.services.policyenquiryservice.rest.util;

import java.util.Map;
import java.util.concurrent.CountDownLatch;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.http.ResponseEntity;
import org.springframework.util.concurrent.ListenableFutureCallback;

/**
Response Call back for Async Call
*/
public abstract class ResponseCallBack<T> implements ListenableFutureCallback<ResponseEntity<T>>{

    private static final Logger logger = LoggerFactory.getLogger(ResponseCallBack.class.getName());

    Map<String,Object> inputs;


    public ResponseCallBack(Map<String,Object> inputs){
        this.inputs = inputs;
    }

    @Override
    public void onSuccess(ResponseEntity<T> stringResponseEntity) {
        onCallSuccess(this.inputs,stringResponseEntity);
    }

    @Override
    public void onFailure(Throwable ex) {
        logger.error(ex.getMessage(),ex);
        onCallFailure(this.inputs, ex);
    }

    //Do your stuff
    public abstract void onCallSuccess(Map<String,Object> inputs,ResponseEntity<T> stringResponseEntity);
    public abstract void onCallFailure(Map<String,Object> inputs,Throwable ex);
}

//Example
private void createRestUtilForAsync()
    {

    RestUtil restUtil = RestUtil.restUtil().url(url).addHeader("Accept", "application/json").addHeader("Content-Type", "application/json").addparam("xxx", 10).addparam("yyyy", "").addparam("zzz", "dsadsa").httpMethod(HttpMethod.POST).responseType(Policy.class).build();
    //create inputs
    ResponseCallBack<Policy> responseCallBack = new ResponseContractValuesCallBack(inputs);

    //asyncRestTemplate is autowired in the class
    restUtil.callServiceAsync(this.asyncRestTemplate, responseCallBack);
}

private void createRestUtilForSync()
    {

    RestUtil restUtil = RestUtil.restUtil().url(url).addHeader("Accept", "application/json").addHeader("Content-Type", "application/json").addparam("xxx", 10).addparam("yyyy", "").addparam("zzz", "dsadsa").httpMethod(HttpMethod.POST).responseType(Policy.class).build();

    //asyncRestTemplate is autowired in the class
    Foo foo = restUtil.callServiceAsync(this.restTemplate);
}

【讨论】:

  • 嗨。我实际上会在多线程应用程序中使用它——不是反应式的。您提议的方法在这种情况下是否有效?
  • 这适用于 Mutithreded 环境。添加示例 createRestUtilForSync().Configure RestTemplate 将其注入 Spring 组件并重用此
  • 这个 Rest utiltiy 有 Sync 和 Async 两者。要并行化多个 Rest 调用,您可以使用 Async 版本并在最后聚合。对于 Single call 使用 Sync,也可以进行 Paremterized。
【解决方案2】:

您可以使用 Spring Cloud 提供的技术。例如,请求其他网络服务的最佳方式是使用 Feign Client。对于 Hystrix 的异常处理。

【讨论】:

  • 嗨,这对我来说是新的。我会检查它是什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-10-25
  • 1970-01-01
  • 2021-01-01
  • 1970-01-01
  • 2014-11-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多