• 使用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"]>
    
    
  • 相关阅读:
    标准模板库中的链表(list)
    C++接口
    qsort
    C++异常
    标准模板库中的向量(vector)
    后缀表达式/逆波兰表达式
    静态数据成员
    c++存储区域
    #define 和 const
    Git 的下载与安装
  • 原文地址:https://www.cnblogs.com/smalldong/p/14264110.html
Copyright © 2020-2023  润新知