使用Spring的RestTemplate進行接口調用


引自:http://www.zimug.com/

1.常見的http服務的通信方式

經常使用的方式有HttpClient、OkHttp、RestTemplate。其中RestTemplate是一種更優雅的調用RESTful服務的方式。

RestTemplate使用了模版方法的設計模式,借助 RestTemplate,Spring應用可以很方便地訪問REST資源。

2.主要的方法

get請求常用方法:
getForObject  //返回響應內容
getForEntity  //返回響應體


post請求常用方法:
postForObject  //返回響應內容
postForEntity  //返回響應體

二合一的方法:
exchange //該方法的參數可以設置不同請求方法,可以選擇get或者post

3.客戶端和服務端

3.1 服務端

新建一個spring-boot項目。引入依賴。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.73</version>
    </dependency>
</dependencies>
server:
  port: 8090
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.Enumeration;

@RestController
@CrossOrigin
public class IndexController {

    @GetMapping("/getData")
    public String getData() {
        return "hello, get method";
    }

    @PostMapping("/postData")
    public String postData(HttpServletRequest request, HttpServletResponse response) throws IOException {
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        System.out.println(request.getRemoteUser());
        System.out.println(request.getAuthType());
        System.out.println(request.getContentType());
        System.out.println(request.getContextPath());
        System.out.println(request.getRemoteAddr());
        System.out.println(request.getRemoteHost());
        System.out.println(request.getRemotePort());
         //1.讀取header中的數據,通過key來獲取
        System.out.println(request.getHeader("username"));
        System.out.println("=====================");
        BufferedReader reader = request.getReader();
        //2.讀取body中的數據
        String line = null;
        while ((line = reader.readLine()) != null){
            System.out.println(line);
        }
        return "hello, post method";
    }
}

3.2 客戶端

新建一個客戶端的項目。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.yqd</groupId>
    <artifactId>hello-rest-template</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <name>hello-rest-template</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.73</version>
        </dependency>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>3.14.4</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
server:
  port: 8091

添加RestTemplate的配置類

@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
        RestTemplate restTemplate = new RestTemplate(factory);
        // 支持中文編碼
        restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(Charset.forName("UTF-8")));
        return restTemplate;
    }

    /**RestTemplate 支持至少三種HTTP客戶端庫:JDK自帶的HttpURLConnection,Apache HttpComponents,OkHttp。
     * 從各種HTTP客戶端性能以及易用程度評測來看,OkHttp 優於 Apache HttpComponents、Apache HttpComponents優於HttpURLConnection。
     * 所以我個人更建議大家將底層HTTP實現切換為okHTTP。
     */
    @Bean
    public ClientHttpRequestFactory myClientHttpRequestFactory() {
        OkHttp3ClientHttpRequestFactory factory= new OkHttp3ClientHttpRequestFactory(); //此處使用自定義的OkHttp客戶端,需要引入okhttp依賴,已在pom中引入
        factory.setReadTimeout(2000);//從服務器獲取數據超時時間 單位為ms
        factory.setConnectTimeout(2000);//連接超時時間 單位為ms
        return factory;
    }
}

4.客戶端發送get請求

@Autowired
private RestTemplate restTemplate;

@Test
void getMethod() {
    String url = "http://localhost:8090/getData";
    //String result = restTemplate.getForObject(url, String.class); //直接返回響應內容
    ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);//返回響應體
    System.out.println(responseEntity.getStatusCode()); //200 OK
    System.out.println(responseEntity.getStatusCodeValue()); 200
    System.out.println(responseEntity.getBody());//hello, get method
    System.out.println(responseEntity.getClass()); //class org.springframework.http.ResponseEntity
    System.out.println(responseEntity.getHeaders());//[Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", Content-Type:"text/plain;charset=UTF-8", Content-Length:"17", Date:"Mon, 11 Jan 2021 12:04:14 GMT", Keep-Alive:"timeout=60", Connection:"keep-alive"]
}

5.客戶端發送post請求

@Test
public void testPost() throws UnsupportedEncodingException {

    String url = "http://localhost:8090/postData";
    //1、header
    HttpHeaders header = new HttpHeaders();
    header.add("username", "tom");
    header.add("charset", "utf-8");
    header.add("Content-Type", "application/json");

    //2、body 有兩種數據封裝方式,第一種是使用MultiValueMap,第二種是直接用JSONObject發送json對象
    //        MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<>();
    //        requestBody.add("data", "123456");
    JSONObject requestBody = new JSONObject();//放入body中的json參數
    requestBody.put("data", "123456");

    //3、把header和body封裝到請求中
    HttpEntity<JSONObject> httpEntity = new HttpEntity<>(requestBody, header);

    //4、提交請求,得到響應
    //String response = restTemplate.postForObject(url, httpEntity, String.class); //直接返回響應內容
    ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, httpEntity, String.class);//返回響應體
    System.out.println(responseEntity.toString()); //<200,hello, post method,[Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", Content-Type:"text/plain;charset=UTF-8", Content-Length:"18", Date:"Mon, 11 Jan 2021 12:05:42 GMT", Keep-Alive:"timeout=60", Connection:"keep-alive"]>
}

6.使用exchange方法

只需要把getForEntity、postForEntity等方法換成exchange就可以了

@Test
public void testExchage() throws UnsupportedEncodingException {

    String url = "http://localhost:8090/postData";
    //1、header
    HttpHeaders header = new HttpHeaders();
    header.add("username", "tom");
    header.add("charset", "utf-8");
    header.add("Content-Type", "application/json");

    //2、body 有兩種數據封裝方式,第一種是使用MultiValueMap,第二種是直接用JSONObject發送json對象
    //        MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<>();
    //        requestBody.add("data", "123456");
    JSONObject requestBody = new JSONObject();//放入body中的json參數
    requestBody.put("data", "123456");

    //3、把header和body封裝到請求中
    HttpEntity<JSONObject> entity = new HttpEntity<JSONObject>(requestBody, header);

    //4、提交請求
    ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
    System.out.println(exchange.toString());
}

7.如何自定義請求攔截器

7.1 增加CustomInterceptor類

public class CustomInterceptor implements ClientHttpRequestInterceptor {
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        HttpHeaders headers = request.getHeaders();
        // 加入自定義請求頭
        headers.add("xy","1024" );
        System.out.println("通過請求攔截器查看請求頭:"+headers);
        // 請求繼續被執行
        return execution.execute(request, body);
    }
}

7.2 在RestTemplate配置類中增加配置

RestTemplateConfig中增加如下攔截器配置。

@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
    RestTemplate restTemplate = new RestTemplate(factory);
    //配置自定義的請求攔截處理器
    restTemplate.setInterceptors(Arrays.asList(new CustomInterceptor()));
    // 支持中文編碼
    restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(Charset.forName("UTF-8")));
    return restTemplate;
}

測試的時候會發現每次打印請求頭信息,說明請求經過了攔截器。如果在請求時有其他需求,比如動態增加頭部信息,校驗頭部信息等可以在攔截器中修改。

8.如何自定義請求異常處理

8.1 增加CustomErrorHandler類

public class CustomErrorHandler implements ResponseErrorHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(CustomErrorHandler.class);

    /**
     * 返回false表示不管response的status是多少都返回沒有錯
     * 這里可以自己定義那些status code你認為是可以拋Error
     */
    @Override
    public boolean hasError(ClientHttpResponse response) throws IOException {
        return response.getStatusCode().value() != 200 && response.getStatusCode().value() !=302;
    }

    /**
     * 這里面可以實現你自己遇到了Error進行合理的處理
     */
    @Override
    public void handleError(ClientHttpResponse response) throws IOException {
        System.out.println("請求中有錯誤");
    }

    /**
     * 重載方法
     */
    @Override
    public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
        LOGGER.error("=======================ERROR============================");
        LOGGER.error("DateTime:{}", DateUtil.generateTimeFormatter(response.getHeaders().getDate(),"yyyy-MM-dd HH:mm:ss"));
        LOGGER.error("HOST:{},URI:{}", url.getHost(),url.getPath());
        LOGGER.error("Method:{}", method.name());
        LOGGER.error("Exception:{}", response.getStatusCode());
        LOGGER.error("========================================================");
    }
}

8.2 在RestTemplate配置類中增加配置

@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
    RestTemplate restTemplate = new RestTemplate(factory);
    //配置自定義的請求攔截處理器
    restTemplate.setInterceptors(Arrays.asList(new CustomInterceptor()));
    //配置自定義的請求異常處理器
    restTemplate.setErrorHandler(new CustomErrorHandler());
    // 支持中文編碼
    restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(Charset.forName("UTF-8")));
    return restTemplate;
}

測試的時候,啟動服務器,請求的url設置一個錯誤參數,可以看到錯誤信息,而不會報RestTemplate默認的異常。

默認異常:(后台報紅)

org.springframework.web.client.HttpClientErrorException$NotFound: 404 

配置之后的異常:(后台不會報紅)

攔截器查看請求頭:[Accept:"text/plain, application/json, application/*+json, */*", username:"tom", charset:"utf-8", Content-Type:"application/json", Content-Length:"17", xy:"1024"]
2021-01-14 19:05:24.024 ERROR 22768 --- [           main] com.yqd.config.CustomErrorHandler        : =======================ERROR============================
2021-01-14 19:05:24.025 ERROR 22768 --- [           main] com.yqd.config.CustomErrorHandler        : DateTime:2021-01-14 19:05:23
2021-01-14 19:05:24.025 ERROR 22768 --- [           main] com.yqd.config.CustomErrorHandler        : HOST:localhost,URI:/postData1
2021-01-14 19:05:24.025 ERROR 22768 --- [           main] com.yqd.config.CustomErrorHandler        : Method:POST
2021-01-14 19:05:24.025 ERROR 22768 --- [           main] com.yqd.config.CustomErrorHandler        : Exception:404 NOT_FOUND
2021-01-14 19:05:24.025 ERROR 22768 --- [           main] com.yqd.config.CustomErrorHandler        : ========================================================
HTTP 響應狀態:404 NOT_FOUND
<404,{"timestamp":"2021-01-14T11:05:24.024+00:00","status":404,"error":"Not Found","message":"","path":"/postData1"},[Connection:"keep-alive", Content-Type:"application/json", Date:"Thu, 14 Jan 2021 11:05:23 GMT", Keep-Alive:"timeout=60", Transfer-Encoding:"chunked", Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers"]>


免責聲明!

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



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