RestTemplateBuilder類


 

Spring Boot使用RestTemplate消費REST服務的幾個問題記錄

我們可以通過Spring Boot快速開發REST接口,同時也可能需要在實現接口的過程中,通過Spring Boot調用內外部REST接口完成業務邏輯。

在Spring Boot中,調用REST Api常見的一般主要有兩種方式,通過自帶的RestTemplate或者自己開發http客戶端工具實現服務調用。

RestTemplate基本功能非常強大,不過某些特殊場景,我們可能還是更習慣用自己封裝的工具類,比如上傳文件至分布式文件系統、處理帶證書的https請求等。

本文以RestTemplate來舉例,記錄幾個使用RestTemplate調用接口過程中發現的問題和解決方案。

一、RestTemplate簡介

1、什么是RestTemplate

我們自己封裝的HttpClient,通常都會有一些模板代碼,比如建立連接,構造請求頭和請求體,然后根據響應,解析響應信息,最后關閉連接。

RestTemplate是Spring中對HttpClient的再次封裝,簡化了發起HTTP請求以及處理響應的過程,抽象層級更高,減少消費者的模板代碼,使冗余代碼更少。

其實仔細想想Spring Boot下的很多XXXTemplate類,它們也提供各種模板方法,只不過抽象的層次更高,隱藏了更多細節而已。

順便提一下,Spring Cloud有一個聲明式服務調用Feign,是基於Netflix Feign實現的,整合了Spring Cloud Ribbon與 Spring Cloud Hystrix,並且實現了聲明式的Web服務客戶端定義方式。

本質上Feign是在RestTemplate的基礎上對其再次封裝,由它來幫助我們定義和實現依賴服務接口的定義。

2、RestTemplate常見方法

常見的REST服務有很多種請求方式,如GET,POST,PUT,DELETE,HEAD,OPTIONS等。RestTemplate實現了最常見的方式,用的最多的就是Get和Post了,調用API可參考源碼,這里列舉幾個方法定義(GET、POST、DELETE):

public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) 

public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)

public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType,Object... uriVariables)

public <T> ResponseEntity<T> postForEntity(String url, @Nullable Object request,Class<T> responseType, Object... uriVariables)

public void delete(String url, Object... uriVariables)

public void delete(URI url)

methods

同時要注意兩個較為“靈活”的方法exchange和execute。

RestTemplate暴露的exchange與其它接口的不同:

(1)允許調用者指定HTTP請求的方法(GET,POST,DELETE等)

(2)可以在請求中增加body以及頭信息,其內容通過參數‘HttpEntity<?>requestEntity’描述

(3)exchange支持‘含參數的類型’(即泛型類)作為返回類型,該特性通過‘ParameterizedTypeReference<T>responseType’描述。

RestTemplate所有的GET,POST等等方法,最終調用的都是execute方法。excute方法的內部實現是將String格式的URI轉成了java.net.URI,之后調用了doExecute方法,doExecute方法的實現如下:

/**
     * Execute the given method on the provided URI.
     * <p>The {@link ClientHttpRequest} is processed using the {@link RequestCallback};
     * the response with the {@link ResponseExtractor}.
     * @param url the fully-expanded URL to connect to
     * @param method the HTTP method to execute (GET, POST, etc.)
     * @param requestCallback object that prepares the request (can be {@code null})
     * @param responseExtractor object that extracts the return value from the response (can be {@code null})
     * @return an arbitrary object, as returned by the {@link ResponseExtractor}
     */
    @Nullable
    protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
            @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {

        Assert.notNull(url, "'url' must not be null");
        Assert.notNull(method, "'method' must not be null");
        ClientHttpResponse response = null;
        try {
            ClientHttpRequest request = createRequest(url, method);
            if (requestCallback != null) {
                requestCallback.doWithRequest(request);
            }
            response = request.execute();
            handleResponse(url, method, response);
            if (responseExtractor != null) {
                return responseExtractor.extractData(response);
            }
            else {
                return null;
            }
        }
        catch (IOException ex) {
            String resource = url.toString();
            String query = url.getRawQuery();
            resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
            throw new ResourceAccessException("I/O error on " + method.name() +
                    " request for \"" + resource + "\": " + ex.getMessage(), ex);
        }
        finally {
            if (response != null) {
                response.close();
            }
        }
    }

doExecute

doExecute方法封裝了模板方法,比如創建連接、處理請求和應答,關閉連接等。

多數人看到這里,估計都會覺得封裝一個RestClient不過如此吧?

3、簡單調用

以一個POST調用為例:

package com.power.demo.restclient;

import com.power.demo.common.AppConst;
import com.power.demo.restclient.clientrequest.ClientGetGoodsByGoodsIdRequest;
import com.power.demo.restclient.clientresponse.ClientGetGoodsByGoodsIdResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

/**
 * 商品REST接口客戶端 (demo測試用)
 **/
@Component
public class GoodsServiceClient {

    //服務消費者調用的接口URL 形如:http://localhost:9090
    @Value("${spring.power.serviceurl}")
    private String _serviceUrl;

    @Autowired
    private RestTemplate restTemplate;

    public ClientGetGoodsByGoodsIdResponse getGoodsByGoodsId(ClientGetGoodsByGoodsIdRequest request) {
        String svcUrl = getGoodsSvcUrl() + "/getinfobyid";

        ClientGetGoodsByGoodsIdResponse response = null;

        try {
            response = restTemplate.postForObject(svcUrl, request, ClientGetGoodsByGoodsIdResponse.class);
        } catch (Exception e) {
            e.printStackTrace();
            response = new ClientGetGoodsByGoodsIdResponse();
            response.setCode(AppConst.FAIL);
            response.setMessage(e.toString());
        }

        return response;
    }

    private String getGoodsSvcUrl() {

        String url = "";

        if (_serviceUrl == null) {
            _serviceUrl = "";
        }
        if (_serviceUrl.length() == 0) {
            return url;
        }

        if (_serviceUrl.substring(_serviceUrl.length() - 1, _serviceUrl.length()) == "/") {
            url = String.format("%sapi/v1/goods", _serviceUrl);
        } else {
            url = String.format("%s/api/v1/goods", _serviceUrl);
        }

        return url;
    }

}

GoodsServiceClient

demo里直接RestTemplate.postForObject方法調用,反序列化實體轉換這些RestTemplate內部封裝搞定。

 

文獻來源於: https://www.cnblogs.com/jeffwongishandsome/archive/2018/05/17/8995562.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM