跨项目信息通讯Http远程调用API

项目信息通信 远程调用API

​ 在A服务中 需要通过远程调用 B服务接口 获取数据。A项目与B项目部署在不同的服务器中。两个项目之间进行信息通信有很多中方式,比如有 Socket、Http、soap web service 、websocket、Rpc、mq、apache的HttpClient、java.net包下的HttpURLConnection、hutool.http.HttpRequest等等。

1、目标服务器的创建封装

  • 在进行通信的前提条件是需要知道接口提供者的服务器IP地址和访问路径。首先模拟创建了一个接受请求的服务项目。分别提供了post和get两种类型的请求接口。
package cn.example.springbootproject.HttpClientPage.controller;

import cn.example.springbootproject.util.R;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;

/**
 * @Description: 向其他项目提供(Http协议)访问接口,其他项目进行接口的调用:客户端controller,提供项目接口调用的物理层
 * @ClassName: serverController
 *
 * TODO -比如socket,http,soap web service,websocket,rpc等等
 *
 */

@Controller
@RequestMapping("/httpClient")
public class ServerController {
    //LoggerFactory.getLogger()
    Logger logger = LoggerFactory.getLogger(ServerController.class);

    @ResponseBody
    @RequestMapping(value = "/testPost" ,method = RequestMethod.POST)
    public JSONObject testPostRequest(@RequestBody String id){
        logger.info("访问post类型数据接口!  body参数id={}",id);
        HashMap<Object, Object> result = new HashMap<>();
        result.put("boolean",true);
        result.put("Integer",110);
        result.put("type","postMethod");
        return R.ok("请求访问成功!",result);
    }

    @ResponseBody
    @RequestMapping(value = "/testGet/{temp}",method = RequestMethod.GET)
    public JSONObject testGetRequestMethod(
            @PathVariable(value = "temp") String temp,
            @RequestParam String id){
        logger.info("访问get请求成功! 请求参数 temp={}  id={}",temp,id);
        HashMap<Object, Object> result = new HashMap<>();
        result.put("boolean",true);
        result.put("Integer",110);
        result.put("type","getMethod");
        return R.ok("访问Get请求成功!",result);
    }
}
  • util工具类下的统一返回值类
@Data
public class R extends JSONObject{
    //调用是否成功
    private Boolean sucess;
    //返回码
    private Integer code;
    //返回消息
    private String message;
    //返回数据
    private Map<String,Object> data = new HashMap<>();
    private R(){}  //构造方法私有化
    //操作成功响应
    public static R ok(String msg,Object resultObj){
        R result = new R();
        result.put("result","ok");
        result.put("msg",msg);
        result.put("resultObj",resultObj);
        return result;
    }
    //操作失败响应
    public static R error(String msg,Object resultObj){
        R result = new R();
        result.put("result","fail");
        result.put("msg",msg);
        result.put("resultObj",resultObj);
        return result;
    }

    /**
    *@Description 将JSon字符串转换为JSONObject
    */
    public static R getJsonObjectInfo(String str){
        /*
        * JSONObject extends JSON(继承parseObject(String str) 方法)
        * */
        R jsonObject =(R) JSONObject.parseObject(str);
        return jsonObject;
    }
}

2、HttpURLConnection实现通信

​ HttpURLConnection是Java的标准类库,在JDK的java.net包中已经提供了访问Http协议的该类。HttpURLConnection继承自URLConnection,可以向指定网站发送GET请求,Post请求。它在URLConnection的基础上提供了一些便捷的方法。HttpURLConnection是一个抽象类,无法直接实例化对象。通过调用openConnection() 方法获得对象实例。

2.1 使用URL发送GET请求

​ 首先需要通过URL.openConnection()来创建能够访问Http协议的HttpURLConnection类

​ 通过HttpURLConnection.connect()来链接到目标服务器的接口,请求参数的封装需要放在请求连接之前完成。

​ 然后会通过HttpURLConnection.getInputStream()来获取到响应数据,改响应数据是一个流对象,我们需要从流对象中读出响应数据,且注意该流对象是只能读取一次数据,读取第二次时会是空数据!

package cn.example.springbootproject.HttpUrlConnectonController_01;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * @Description: 通过调用java.net.HttpURLConnection 远程调用Get请求
 * @ClassName: HttpURLConnectionTestGET
 */
public class HttpURLConnectionTestGET {
    public static void main(String[] args) throws IOException {
        //1.创建URL对象
        URL url = new URL("http://localhost:8888/httpClient/testGet/restfulStyle?id=001");
        //2.通过URL对象获取能够访问Http协议的类HttpURLConnection
        HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
        //3.封装请求相关参数
        httpURLConnection.setRequestMethod("GET"); //请求方式
        httpURLConnection.setRequestProperty("version", "1.0");//设置请求头
        httpURLConnection.setConnectTimeout(6*1000); //设置超时时间
        httpURLConnection.setReadTimeout(6*1000);
        //4.建立连接
        httpURLConnection.connect();
        //5.得到响应数据(流对象)
        InputStream inputStream = httpURLConnection.getInputStream();
        byte[]  buff = new byte[1024]; //创建缓存大小
        int len = -1; //每次读取到的内容长度
        /*获取io流中的数据方式1*/
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        /*获取io流中的数据方式2*/
        StringBuffer result = new StringBuffer();
        while ((len = inputStream.read(buff)) != -1){  //当没有内容可读取后为-1则推出
            result.append(new String(buff,0,len));
            byteArrayOutputStream.write(buff,0,len);
        }
        System.out.println("result = " + result);
        String byteStream = byteArrayOutputStream.toString();
        System.out.println("byteStream = " + byteStream);
        //关闭流资源
        byteArrayOutputStream.close();
        inputStream.close();
        httpURLConnection.disconnect();

    }
}
/**
 * result = {"result":"ok","msg":"访问Get请求成功!","resultObj":{"Integer":110,"boolean":true,"type":"getMethod"}}
 * byteStream = {"result":"ok","msg":"访问Get请求成功!","resultObj":{"Integer":110,"boolean":true,"type":"getMethod"}}
 * */


2.2 使用URL发送POST请求

httpURLConnection.setRequestProperty(“Content-Type”,“application/x-www-form-urlencoded”);

​ 在使用HttpURLConnection发送POST请求时,传递参数的方式是建立连接之后,取出一个输出流,然后通过输出流将Body数据写到我们的请求之中。参数的格式是以键值对的方式,键值对之间用‘=’相连,多个键值对之间采用&来连接。

package cn.example.springbootproject.HttpUrlConnectonController_01;

//import javax.net.ssl.HttpsURLConnection;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * @Description: HttpURLConnection-测试发送POST请求
 * @ClassName: HttpURLConnectionTestPOST
 */
public class HttpURLConnectionTestPOST {
    public static void main(String[] args) throws Exception{
        //1.创建URL
        URL url = new URL("http://localhost:8888/httpClient/testPost/");
        //2.创建HttpURLConnection对象
        HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
        //3.设置请求参数
        httpURLConnection.setRequestMethod("POST"); //请求方式
        httpURLConnection.setConnectTimeout(6*1000); //请求时间
        httpURLConnection.setRequestProperty("version", "1.0");//设置请求头
        httpURLConnection.setDoInput(true); //是否读入
        httpURLConnection.setDoOutput(true); //是否读出
        httpURLConnection.setUseCaches(false); //是否使用缓存
        httpURLConnection.setInstanceFollowRedirects(true); // 设置此 HttpURLConnection 实例是否应该自动执行 HTTP 重定向
        // 设置使用标准编码格式编码参数的名-值对
        httpURLConnection.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
        httpURLConnection.connect(); //建立http连接
        //4.通过输出流将参数写入请求之中
        String parameters = "id=123456&name=纪录片";
        OutputStream outputStream = httpURLConnection.getOutputStream(); //获取连接的输出流
        outputStream.write(parameters.getBytes()); //将参数写入流中
        outputStream.flush();
        outputStream.close();

        System.out.println("httpURLConnection.getResponseCode() = " + httpURLConnection.getResponseCode());  //获取响应状态码
        System.out.println("httpURLConnection.getResponseMessage() = " + httpURLConnection.getResponseMessage());  //获取响应Message?
        //5.从输入流中获取响应数据
        InputStream inputStream = httpURLConnection.getInputStream();
        String result = "";
        BufferedReader bufferedReader = new BufferedReader(
                new InputStreamReader(inputStream));
        String line;
        while ((line = bufferedReader.readLine())!=null){
            result += line;
        }
        System.out.println("响应结果:result = " + result);
        //关闭流资源
        inputStream.close();
        bufferedReader.close();
        httpURLConnection.disconnect();  //断开http连接
    }
    /**
     * httpURLConnection.getResponseCode() = 200
     * httpURLConnection.getResponseMessage() = null
     * 响应结果:result = {"result":"ok","msg":"请求访问成功!","resultObj":{"Integer":110,"boolean":true,"type":"postMethod"}}
     * */
}

connection.setRequestProperty(“Content-Type”, “application/json;charset=utf-8”);

​ 设置编码格式Content-Type类型,实现传送JSON格式的参数到目标资源接口中

package cn.example.springbootproject.HttpUrlConnectonController_01;

//import javax.net.ssl.HttpsURLConnection;
import com.alibaba.fastjson.JSONObject;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * @Description: HttpURLConnection-测试发送POST请求
 * @ClassName: HttpURLConnectionTestPOST
 */
public class HttpURLConnectionTestPOST {
    public static void main(String[] args) throws Exception {
        //1.创建URL
        URL url = new URL("http://localhost:8888/httpClient/testPost/");
        //2.创建HttpURLConnection对象
        HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
        //3.设置请求参数
        httpURLConnection.setRequestMethod("POST"); //请求方式
        httpURLConnection.setDoInput(true); //是否读入
        httpURLConnection.setRequestProperty("version", "1.0");//设置请求头
        httpURLConnection.setDoOutput(true); //是否读出
        httpURLConnection.setUseCaches(false); //是否使用缓存
        // 设置使用标准编码格式编码参数的名-值对
        httpURLConnection.setRequestProperty("Content-Type","application/json;charset=utf-8"); //设置参数类型为JSON格式
        httpURLConnection.connect(); //建立http连接
        //4.通过输出流将参数写入请求之中
        JSONObject jsonParams = new JSONObject();
        jsonParams.put("id","1452");
        jsonParams.put("name","战争片");
        System.out.println("jsonParams.toJSONString() = " + jsonParams.toJSONString());
        BufferedWriter bufferedWriter =   //创建缓存字符输出流
                new BufferedWriter(new OutputStreamWriter(httpURLConnection.getOutputStream()));
        //写入body参数
        bufferedWriter.write(jsonParams.toJSONString());
        bufferedWriter.flush();
        bufferedWriter.close();

        //5.从输入流中获取响应数据
        if (httpURLConnection.HTTP_OK == httpURLConnection.getResponseCode()){
            //请求访问成功正常 200
            InputStream inputStream = httpURLConnection.getInputStream();
            String result = "";
            BufferedReader bufferedReader = new BufferedReader(
                    new InputStreamReader(inputStream));
            String line;
            while ((line = bufferedReader.readLine())!=null){
                result += line;
            }
            System.out.println("响应结果:result = " + result);
            //关闭流资源
            inputStream.close();
            bufferedReader.close();
            httpURLConnection.disconnect();  //断开http连接
        }
    }
}

3、RestTemplate实现通信

​ spring框架提供的RestTemplate类可用于在应用中调用rest服务它简化了与http服务的通信方式,统一了Restful的标准,封装了http链接,我们只需要传入url及返回值类型即可。相较于之前常用的HttpClient,RestTemplate是一种更优雅的调用Restful服务的方式。

​ RestTemplate默认依赖JDK提供http连接的能力(HttpURLConnection),如果有需要的话也可以通过setRequestFactory方法替换为例如Apache HttpComponents、Netty或OkHttp等其它HTTP library。

​ spring并没有真正的去实现底层的http请求(3次握手),而是集成了别的http请求,spring只是在原有的各种http请求进行了规范标准,让开发者更加简单易用,底层默认用的是jdk的http请求。

方法组说明
getForObject通过 GET 检索一个表示结果。
getForEntity通过使用GET检索一个 ResponseEntity(即 status、header 和 body)。
headForHeaders通过使用 HEAD 检索一个资源的所有 header。
PostForLocation通过使用 POST 创建一个新的资源,并从响应中返回 Location header。
PostForObject通过使用POST创建一个新资源,并从响应中返回表示。
postForEntity通过使用POST创建一个新资源,并从响应中返回表示。
put通过使用PUT创建或更新一个资源。
patchForObject通过使用 PATCH 更新一个资源,并从响应中返回表示。请注意,JDK的 HttpURLConnection 不支持 PATCH,但 Apache 的 HttpComponents 和其他的支持。
delete通过使用 DELETE 删除指定URI上的资源。
optionsForAllow通过使用 ALLOW 为资源检索允许的HTTP方法。
exchange前面的方法的更通用(和更少的意见)版本,在需要时提供额外的灵活性。它接受一个 RequestEntity(包括HTTP方法、URL、header 和 body 作为输入)并返回一个 ResponseEntity。
execute执行请求的最通用方式,通过回调接口对请求准备和响应提取进行完全控制。

3.1 服务调用者环境准备

  • 导入所需依赖
<!--RestTemplate实现远程调用-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
</dependency>
  • 创建 RestTemplate 的配置类
package cn.example.springbootproject.HttpClientPage.clientPage;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

/**
 * @Description: RestTemplate配置类
 */

@Configuration
public class RestTemplateConfig {

    //启动的时候要注意,因为我们在controller中注入了RestTemplate,所以在启动的时候需要实例化该类一个实例对象
    @Autowired
    private RestTemplateBuilder restTemplateBuilder;
    //使用RestTemplateBuilder来实例化一个RestTemplate对象,spring中默认已经注入了RestTemplateBuilder实例
    @Bean
    public RestTemplate restTemplate(){
        return restTemplateBuilder.build();
    }
    @Bean
    public RestTemplate clientRestTemplate(){
        HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
        httpComponentsClientHttpRequestFactory.setConnectionRequestTimeout(3000);
        httpComponentsClientHttpRequestFactory.setConnectTimeout(3000);
        httpComponentsClientHttpRequestFactory.setReadTimeout(3000);
        return new RestTemplate();
    }
}

3.2 Send-POST/GETRequest

  • HTTP+Post请求通过配置类中注册的RestTemplateBean就可以实现通信,调用目标服务端的接口了
package cn.example.springbootproject.HttpClientPage.RestTemplateClientController;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;

@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class ClientTestHttp {
    @Autowired
    private RestTemplate restTemplate;

    @Test  //测试利用RestTemplate发送Post请求(调用http接口!)
    public void testPostMethod(){
        String url = "http://localhost:8888/httpClient/testPost";  //接口提供者访问路径
        JSONObject parameter = new JSONObject();
        parameter.putIfAbsent("id",12);
       //url:访问地址    parameter:post请求放入body中的参数   JSONObject.class:设置响应数据类型
        JSONObject jsonObject = restTemplate.postForObject(url, parameter, JSONObject.class);
        log.info("信息通信成功!  result ={}",JSONObject.toJSONString(jsonObject));
    }
    
    
    @Test //测试RestTemplate发送GetRequest
    public void testGetRequest(){
        String url = "http://localhost:8888/httpClient/testGet/restfulStyle?id=001";
        JSONObject getRequestResult = restTemplate.getForObject(url, JSONObject.class);
        System.out.println("getRequestResult.toJSONString() = " + 				getRequestResult.toJSONString());
    }

}

3.3 Send-HttpsRequest

  • 如果需要通过HTTPS的请求方式进行通信,则还需要在原来的基础上封装一个Https请求工厂类。
package cn.example.springbootproject.HttpClientPage.RestTemplateClientController;

/*import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.ssl.SSLContexts;
import org.springframework.http.client.SimpleClientHttpRequestFactory;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import java.net.HttpURLConnection;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;*/

import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.conn.ssl.TrustStrategy;
import org.springframework.http.client.SimpleClientHttpRequestFactory;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import java.net.HttpURLConnection;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;


/**
 * @Description: HttpsRequestFactory : Https请求工厂类
 */
public class HttpsClientRequestFactory extends SimpleClientHttpRequestFactory {
    @Override
    protected void prepareConnection(HttpURLConnection connection,String httpMethod){
        try {
            if (!(connection instanceof HttpsURLConnection)){ //http协议
                // throw new RuntimeException("An instance of HttpsURLConnection is expected");
                super.prepareConnection(connection,httpMethod);
            }
            if (connection instanceof HttpsURLConnection){ //https协议,修改版本协议
                KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
                //信任任何链接
                TrustStrategy anyTrustStrategy = new TrustSelfSignedStrategy/*TrustStrategy*/(){
                    @Override
                    public boolean  isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                        return true;
                    }
                };
                SSLContext ctx = SSLContexts.custom().useTLS().loadTrustMaterial(trustStore, anyTrustStrategy).build();
                ((HttpsURLConnection)connection).setSSLSocketFactory(ctx.getSocketFactory());
                HttpsURLConnection httpsConnection = (HttpsURLConnection)connection;
                super.prepareConnection(httpsConnection, httpMethod);
            }
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
  • 采用Https的方式调用目标服务器的资源接口:

*注:前提条件服务端接口支持Https:

  1. 申请一个 SSL 证书,并将其安装到服务器上。
  2. 修改服务器配置,使其支持 HTTPS。这可能需要在服务器上安装特定的软件和模块。
  3. 在接口代码中,使用 HTTPS 协议进行请求。
  4. 检查接口是否能正常工作并返回正确的结果。
package cn.example.springbootproject.HttpClientPage.RestTemplateClientController;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;


@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class ClientTestHttp {
    @Autowired
    private RestTemplate restTemplate;

    @Test  //测试Https请求!
    public void TestHttpsRequest(){
        RestTemplate restTemplate = new RestTemplate(new HttpsClientRequestFactory());
        String url = "https://localhost:8888/httpClient/testPost";
        ResponseEntity<JSONObject> result = restTemplate.getForEntity(url, JSONObject.class);
        System.out.println("JSON.toJSONString(result) = " + JSON.toJSONString(result));
    }
}

3.4 RestTemplate请求参数的封装

Entity未RestTemplate的实体类,内部包含请求头/响应头、请求体/响应体、状态码 等

ResponseEntity / RequstEntity

/**
* url: 请求地址;
* method: 请求类型(如:POST,PUT,DELETE,GET);
* requestEntity: 请求实体,封装请求头,请求内容
* responseType: 响应类型,根据服务接口的返回类型决定
* uriVariables: url中参数变量值
*/

package cn.example.springbootproject.HttpClientPage.RestTemplateClientController;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonAlias;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.HttpHead;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.*;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.security.PublicKey;

/**
 * @Description: 客户端访问调用服务端接口
 * @ClassName: ClientController
 * problem: springboot项目没有resource和开始
 */

@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class ClientTestHttp {
    @Autowired
    private RestTemplate restTemplate;

    @Test  //普通接口的调用
    public void testRestTemplateRequest(){
        //资源访问地址
        String url  = "http://localhost:8888/httpClient/testPost";
        //参数的封装
        JSONObject parameter = new JSONObject();
        parameter.put("id",1024);
        /*  该方式不行!!
        //请求头的封装
        HttpHead httpHead = new HttpHead();
        httpHead.addHeader("RestTemplateRequestType","Post");
        //参数封装到HttpEntity实体类中  第一个参数:body,第二个参数:headers
        HttpEntity<String> stringHttpEntity = new HttpEntity<>(parameter.toJSONString(), (MultiValueMap<String, String>) httpHead); */
        RequestEntity<JSONObject> requestEntity = RequestEntity
                .post(url)
                .header(HttpHeaders.COOKIE,"xxxxxx") //添加token信息
                .header("xx","xx")  //添加请求头
                .accept(MediaType.APPLICATION_JSON)
                .contentType(MediaType.APPLICATION_JSON)
                .body(parameter);

        ResponseEntity<JSONObject> responseEntity =
                restTemplate.exchange(url, HttpMethod.POST,requestEntity, JSONObject.class);

        //解析响应数据:
        HttpStatus statusCode = responseEntity.getStatusCode(); //响应状态码
        System.out.println("statusCode = " + statusCode);
        HttpHeaders headers = responseEntity.getHeaders(); //响应头数据
        System.out.println("headers = " + headers);
        int statusCodeValue = responseEntity.getStatusCodeValue();
        System.out.println("statusCodeValue = " + statusCodeValue);
        JSONObject body = responseEntity.getBody();  //响应体
        System.out.println("body.toJSONString() = " + body.toJSONString());
    }
}

/** 控制台输出
statusCode = 200 OK
headers = [Content-Type:"application/json", Transfer-Encoding:"chunked", Date:"Tue, 12 Sep 2023 02:54:07 GMT", Keep-Alive:"timeout=60", Connection:"keep-alive"]
statusCodeValue = 200
body.toJSONString() = {"result":"ok","msg":"请求访问成功!","resultObj":{"Integer":110,"boolean":true,"type":"postMethod"}}
*/

*标准方式设置参数:

private void getAccessToken() {
        try {
            MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
            params.add("grant_type", "client_credentials");
            params.add("client_id", clientId);
            params.add("client_secret", clientSecret);
            RestTemplate client = new RestTemplate();
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
            ResponseEntity<JSONObject> response = client.exchange(uaaUrl, HttpMethod.POST, new HttpEntity<>(params, headers), JSONObject.class);
            log.info("获取 Access Token 请求地址 {}请求参数{}请求响应{}", uaaUrl, params, response.getBody());
            accessToken = Objects.requireNonNull(response.getBody()).getString("access_token");
            accessTokenIsExist = true;
        } catch (Exception e) {
         log.error("getAccessToken 发生错误!错误信息:{}", e.getMessage());
            accessTokenIsExist = false;
        }
    }

3.5 RestTemplate实现文件上传

  • 首先服务端创建了一个文件上传的接口
@RequestMapping(value = "/FileupLoad",method = RequestMethod.POST)
    @ResponseBody
    public JSONObject fileUploadTest(@RequestParam("file")MultipartFile[] files, HttpServletRequest request) throws IOException {
        //携带其他参数可以通过getParameter来获取
        String parameter2 = request.getParameter("parameter2");
        if (files == null){
            return R.error(" 文件上传错误,服务器未拿到上传的文件!",null);
        }
        for (MultipartFile file : files){
             if (!file.isEmpty() && file.getSize()>0) {
                 String filename = file.getOriginalFilename();
                 file.transferTo(new File("E:\\program_Test\\"+filename));
             }
        }
        return R.ok("文件上传成功!",null);
    }
  • RestTemplate文件上传接口调用的参数封装
 @Test //测试文件上传!
    public void testFileUpLoad(){
        String url  = "http://localhost:8888/httpClient/fileUpLoad";
        //设置请求头
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
        //设置请求体 注意是 LinkedMultiValueMap
        FileSystemResource resource = new FileSystemResource("C:\\Users\\AD\\Desktop\\图片测试.jpg");
        LinkedMultiValueMap<String, Object> form = new LinkedMultiValueMap<>();
        form.add("file",resource);
        HttpEntity<LinkedMultiValueMap<String,Object>> file = new HttpEntity<>(form,httpHeaders);
        JSONObject responseResult = restTemplate.postForObject(url, file, JSONObject.class);
        System.out.println("responseResult.toJSONString() = " + responseResult.toJSONString());
    }

4、WebClient实现通信

​ WebClient是从Spring WebFlux 5.0版本开始提供的一个非阻塞的基于响应式编程的进行Http请求的客户端工具。它的响应式编程的基于Reactor的。WebClient中提供了标准Http请求方式对应的get、post、put、delete等方法,可以用来发起相应的请求。

​ WebClient 与HttpClient 的区别:

  • WebClient在SpringBoot是有启动器的,HttpClient没有启动器,需要自己去配置
  • WebClent简单,HttpClient功能更强大,看需选择,HttpClient用的人相对会比较多

​ 使用场景:当我需要一个第三方接口返回数据时,我们不通过网关或者oapi等其它方式去调用,而是直接本地调其它服务的方式获取返回数据,此时用到这两个类了。其实就连SpringCloud中服务和服务之间的调用全部是使用HttpClient

  • xml文件中导入WebClient相关依赖
<!-- WebClient实现远程调用 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
  • 利用 WebClient 发送Get请求
package cn.example.springbootproject.WebClientController_03;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

/**
 * @Description: 使用WebClient请求远程调用服务
 * @ClassName: WebClientRequestTest
 */
public class WebClientRequestTest {
    public static void main(String[] args) {
        WebClient webClient = WebClient.create();
        Mono<JSONObject> jsonObjectMono = webClient
                .get()   //get请求方式访问
                .uri("http://localhost:8888/httpClient/testGet/restfulStyle?id=001")  //请求资源地址
                .header("testHead", "123456")  //请求头的封装
                .cookie("key1", "test1")  //Cookie封装
                .retrieve().bodyToMono(JSONObject.class); //响应结果类型设置
        //获取响应数据
        JSONObject block = jsonObjectMono.block();
        System.out.println("block.toJSONString() = " + block.toJSONString());
    }
}

  • 利用WebClient发送POST请求
package cn.example.springbootproject.WebClientController_03;

import com.alibaba.fastjson.JSONObject;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.util.MultiValueMapAdapter;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
/**
 * @Description: 使用WebClient请求远程调用服务
 * @ClassName: WebClientRequestTest
 */
public class WebClientRequestTest {
    public static void main(String[] args) {
        //1.创建WebClient对象
        WebClient webClient = WebClient.create();
        //2.封装POST请问体body
        /***
         * TODO--参数封装存在问题!!!
         * */
        HashMap<String, List<String>> bodyMap1 = new HashMap<>();
        bodyMap1.put("id", Arrays.asList("123"));
        bodyMap1.put("name", Arrays.asList("古装剧!"));
        MultiValueMap<String, String> body = new MultiValueMapAdapter<>(bodyMap1);
        //参数封装方式2
        JSONObject jsonBody = new JSONObject();
        jsonBody.put("id","1314");
        jsonBody.put("name","springboot");
        //body封装方式3
        String srtBody = jsonBody.toJSONString();
        //3.封装请求相关数据
        WebClient.ResponseSpec retrieve = webClient
                .method(HttpMethod.POST) //设置请求方式  也可以直接采用.post()
                //.uri(url, params) // 这里的params是拼接在url中的,可以用@PathVariable接收的参数
                .uri("http://localhost:8888/httpClient/testPost")
                .accept(MediaType.APPLICATION_JSON) // contentType
                .contentType(MediaType.APPLICATION_JSON) // contentType
                .header("testHeadInfo", "test1")
                .cookie("cookieTest", "test02")
                // 这里可以放入BodyInserters.from……多种方法,按需使用
                //.body(Mono.just(jsonBody),JSONObject.class)
                //.body(BodyInserters.fromFormData(body)) // 构造的参数可以用@RequestParam、@RequestBody接收
                .bodyValue(srtBody)
                .retrieve();// 发送请求
        //4.获取响应数据
        JSONObject result = retrieve.bodyToMono(JSONObject.class).block();
        System.out.println("result.toJSONString() = " + result.toJSONString());
    }
    /*
    * result.toJSONString() = {"result":"ok","msg":"请求访问成功!","resultObj":{"Integer":110,"boolean":true,"type":"postMethod"}}
    * */
}

5、HttpClient实现通信

​ HttpClient是Apache的客户端的http通信实现库,这个类库的作用是接收和发送http报文,使用这个类库,它相比传统的 HttpURLConnection,增加了易用性和灵活性,我们对于http的操作会变得简单一些;SpringCloud中服务和服务之间的调用全部是使用HttpClient,HttpClient是非常强大的。

  • pom.xml中导入相关依赖
   <!-- HttpClient实现远程调用 -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpcore</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>
  • 调用已知服务ip和请求接口,利用HttpClient发送POST请求DEMO
package cn.example.springbootproject.HttpClientController_04;

import com.alibaba.fastjson.JSONObject;
import net.minidev.json.JSONUtil;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

/**
 * @Description: HttpClient实现资源的远程调用测试
 * @ClassName: HttpClientRequest
 */
public class HttpClientRequest {

    public static void main(String[] args) throws IOException {
        //1.创建HttpClient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //2.请求数据的处理
        HttpPost httpPost = new HttpPost("http://localhost:8888/httpClient/testPost"); //Post请求创建
        httpPost.setHeader("Content-type","application/json;charset=UTF-8"); //请求格式
        /**
         * body参数的封装
         * TODO--访问post类型数据接口!  body参数id={"name":"??HttpClient","id":"123456"},name=null: 参数封装还是存在问题待处理!!!!
         * */
        JSONObject jsonBody = new JSONObject();
        jsonBody.put("id","123456");
        jsonBody.put("name","测试HttpClient");
        StringEntity stringEntity = new StringEntity((String) jsonBody.toJSONString()); //String类型的JSON数据
        stringEntity.setContentType("text/json");
        httpPost.setEntity(stringEntity);
        /*
        * 3.设置超时-- 连接超时:setConnectTimeout;传输超时:setSocketTimeout
        * */
        RequestConfig build = RequestConfig.custom().setConnectTimeout(3 * 1000).setSocketTimeout(3 * 1000).build();
        httpPost.setConfig(build);
        //4.执行PostRequst
        CloseableHttpResponse executeResult = httpClient.execute(httpPost);
        //5.处理响应结果
        //获取响应状态码
        System.out.println("executeResult.getStatusLine().getStatusCode() = " + executeResult.getStatusLine().getStatusCode());
        //获取响应头信息
        //获取响应头信息
        Iterator<Header> iterator = Arrays.stream(executeResult.getAllHeaders()).iterator();
        while (iterator.hasNext()){
            Header next = iterator.next();
            System.out.println(next.getName()+"=="+next.getValue());
        }
        //获取响应数据
        String result = EntityUtils.toString(executeResult.getEntity(), "UTF-8");
        System.out.println("result = " + result);
    }
}
  • 利用HttpClient调用GETREquest示例
package cn.example.springbootproject.HttpClientController_04;

import com.alibaba.fastjson.JSONObject;
import net.minidev.json.JSONUtil;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Iterator;

/**
 * @Description: HttpClient实现资源的远程调用测试
 * @ClassName: HttpClientRequest
 */
public class HttpClientRequest {
    public static void main(String[] args) throws IOException {
        //1.创建HttpClient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //2.封装GET请求
        HttpGet httpGet = new HttpGet("http://localhost:8888/httpClient/testGet/restfulStyle?id=001");
        httpGet.setHeader("test","001");
        httpGet.setHeader("Content-Type","application/json;charset=UTF-8");
        httpGet.setConfig(RequestConfig.custom().setConnectTimeout(6*10000).setSocketTimeout(6*10000).build());
        //3.执行GETRequest
        CloseableHttpResponse execute = httpClient.execute(httpGet);
        String result = EntityUtils.toString(execute.getEntity(), "UTF-8");
        System.out.println("result = " + result);
    }
}

6、Hutool-HttpRequest 实现通信

​ Hutool-http针对JDK的HttpUrlConnection做一层封装,简化了HTTPS请求、文件上传、Cookie记忆等操作,使Http请求变得无比简单。其核心类为HttpRequest和HttpResponse。与此同时,Hutool-http针对一些场景封装了HttpUtil工具类,让一些操作变得更简单。

优点:

1.根据URL自动判断是请求HTTP还是HTTPS,不需要单独写多余的代码。
2.表单数据中有File对象时自动转为multipart/form-data表单,不必单做做操作。
3.默认情况下Cookie自动记录,比如可以实现模拟登录,即第一次访问登录URL后后续请求就是登录状态。
4.自动识别304跳转并二次请求
5.自动识别页面编码,即根据header信息或者页面中的相关标签信息自动识别编码,最大可能避免乱码。
6.自动识别并解压Gzip格式返回内容

  • pom.xml中导入相关依赖
   <!-- hutool-HttpRequest 实现Http远程调用-->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.8</version>
</dependency>

  • Hutool-HttpRequest–Post请求例子
package cn.example.springbootproject.HutoolHttpReuestClientController_05;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSONObject;
/**
 * @Description: Hutool工具类封装的HttpRequest测试
 * @ClassName: HuToolHttpRequest
 */
public class HuToolHttpRequest {
    //testPostRequest
    public static void main(String[] args) {
        //封装body参数
        JSONObject jsonBody = new JSONObject();
        jsonBody.put("id","122345");
        jsonBody.put("name","后端工程师");
        //链式构建请求
        String hutoolHttpResult = HttpRequest
                .post("http://localhost:8888/httpClient/testPost")
                .header(Header.USER_AGENT, "Hutool http")  //头信息,多个头信息多次调用此方法即
                //.form()  //表单内容
                .timeout(6 * 1000)  //超时时间
                .body(jsonBody.toJSONString())  //body参数
                .execute().body();
        System.out.println("hutoolHttpResult = " + hutoolHttpResult);
        /*
        * hutoolHttpResult = {"result":"ok","msg":"请求访问成功!","resultObj":{"Integer":110,"boolean":true,"type":"postMethod"}}
         * */
    }
}
  • Hutool-HttpReqquest-GET请求例子
package cn.example.springbootproject.HutoolHttpReuestClientController_05;

import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import java.net.HttpCookie;
import java.util.Iterator;
import java.util.List;
/**
 * @Description: Hutool工具类封装的HttpRequest测试
 * @ClassName: HuToolHttpRequest
 */
public class HuToolHttpRequest {
    //GETRequestTest
    public static void main(String[] args) {
        //1.GETRequest设置
        HttpResponse result = HttpRequest
                .get("http://localhost:8888/httpClient/testGet/restfulStyle?id=001")
                .header("Content-Type", "application/json;charset=UTF-8")
                .cookie("stet")
                .timeout(6 * 1000)
                .execute();

        //2.响应数据处理
        //cookie数据
        List<HttpCookie> cookies = result.getCookies();
        Iterator<HttpCookie> iterator = cookies.iterator();
        while (iterator.hasNext()){
            HttpCookie next = iterator.next();
            System.out.print("CookieInfo:  "+next.getName()+"=");
            System.out.println(next.getValue());
        }
        //响应状态码
        System.out.println("result.getStatus() = " + result.getStatus());
        //响应体
        System.out.println("result.body() = " + result.body());
    }
}

7、其他笔记

7.1 请求头中的数据

Accept : 浏览器能够接收服务器响应的数据类型。

Accept-Encoding:浏览器能够接收服务器响应的数据的压缩格式。

Accept-Language: 浏览器能够接收的语言类型。

Host: 当前请求访问的目标地址。

Cookie:当前请求需要携带的cookie信息。

8、补充更新总结

补充内容参考文章:https://mp.weixin.qq.com/s/2G87_t_mbEN7ssKxCXIFZQ

8.1 使用FeignClient调用

​ Feign是一款轻量级的HTTP服务客户端远程调用框架,相比较于传统上的HTTP封装接口,它以Java接⼝注解的⽅式调⽤HTTP请求,使服务间的调用变的简单。

  • 1.引入依赖
<!-- Feign注解:注意版本要与springBoot匹配 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <!-- <version>4.0.1</version> -->
</dependency>
  • 2.在启动类上加上@EnableFeignClients注解
  • 3.Feign客户端代码
@FeignClient(name = "masterdata",url = "${masterdata-service-url}")
public interface ISysUserClient {

    @GetMapping(value = "/masterdata/getSysUserById")
    public Map getSysUserById(@RequestParam("userId") String userId);
}

​ @FeignClient注解的常用属性说明

name:指定Feign的名称,如果使用了注册中心,name属性会作为微服务的名称,用于服务发现

url:Feign调用的跳转路径,可以在配置文件中设置,多用于代码调试

8.2 使用RestTemplate调用

​ RestTemplate中几个常用的方法:getForObject()、getForEntity()、postForObject()、postForEntity()。其中,getForObject() 和 getForEntity() 方法可以用来发送 GET 请求。

  • 1.引入依赖

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
    

    虽然SpringBoot会自动的装配很多常见的bean,但是RestTemplate,我们需要显示的配置它。

  • 2.RestTemplateConfig配置类

    @Configuration
    public class RestTemplateConfig {
    
        @Bean
        public RestTemplate restTemplate(ClientHttpRequestFactory factory){
            return new RestTemplate(factory);
        }
    
        @Bean
        public ClientHttpRequestFactory simpleClientHttpRequestFactory(){
            SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
            factory.setReadTimeout(5000);//单位为ms
            factory.setConnectTimeout(5000);//单位为ms
            return factory;
        }
    }
    

    SimpleClientHttpRequestFactory类对应的HTTP库是JDK自带的HttpUrlConnection,当然我们可以根据自身的需求使用其他的HTTP库,例如HttpComponentsAsyncClientHttpRequestFactory。

  • 3.客户端调用接口代码

    我们还可以通过HttpHeader增加一些请求头的属性,例如请求头格式,或者一些需要用户认证的信息。

    @RestController
    public class TestRestTemplate {
        @Resource
        private RestTemplate restTemplate;
    
      
      @GetMapping(value = "/saveUser")
      public void saveUser(String userId) {
          String url = "http://127.0.0.1:8094/masterdata/sysUser/saveUser";
          Map map = new HashMap<>();
          map.put("name", "郭郭");
          HttpHeaders httpHeaders = new HttpHeaders();
          httpHeaders.setContentType(MediaType.APPLICATION_JSON);
          HttpEntity httpEntity = new HttpEntity(map, httpHeaders);
          String results = restTemplate.postForObject(url, httpEntity, String.class);
      }
      }
    

    postForObject方法中的第三个属性String.class是响应数据的类型,这个要根据自身代码的接口,正确书写,否则会报错。

8.3 使用WebClient调用

​ Spring3.0引入了RestTemplate,但是在后来的官方源码中介绍,RestTemplate有可能在未来的版本中被弃用,所谓替代RestTemplate,在Spring5中引入了WebClient作为异步的非阻塞、响应式的HTTP客户端。

  • 1.引入依赖

    <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    
  • 2.WebClient示例代码


public class TestWebClient {
    @Test
    public void doGet() {
        String userId = "郭郭";
        String url = "http://127.0.0.1:8094/masterdata/sysUser/getSysUserById?userId={userId}";
        Mono<String> mono = WebClient
                //创建WebClient实例
                .create()
                //方法调用,WebClient中提供了多种方法
                .get()
                //请求url
                .uri(url, userId)
                //获取响应结果
                .retrieve()
                //将结果转换为指定类型
                .bodyToMono(String.class);
        //返回最终结果:block是阻塞的/subscribe()非阻塞式获取响应结果
        System.out.println("响应结果:" + mono.block());
    }
    @Test
    public void doPost() {
        Map map = new HashMap<>();
        map.put("name", "郭郭");
        String requestBody = JSON.toJSONString(map);
        String url = "http://127.0.0.1:8094/masterdata/sysUser/saveUser";
        Mono<String> mono = WebClient
                //创建WebClient实例
                .create()
                //方法调用,WebClient中提供了多种方法
                .post()
                //请求url
                .uri(url)
                //指定请求的Content-Type为JSON
                .contentType(MediaType.APPLICATION_JSON)
                //使用bodyValue方法传递请求体
                .bodyValue(requestBody)
                //获取响应结果
                .retrieve()
                //将结果转换为指定类型
                .bodyToMono(String.class);
        //返回最终结果:block是阻塞的/subscribe()非阻塞式获取响应结果
        System.out.println("响应结果:" + mono.block());
    }
}

​ 在上述doPost请求中,我们的请求接口入参是一个Map,但是需要转换为JSON格式传递,这是因为WebClient默认是使用JSON序列化的。

8.4 使用Apache HttpClient调用

​ Apache HttpClient 是 Apache 提供的一个同步的 HTTP 客户端库,它提供了丰富的 API,可以很方便地发送 HTTP 请求和获取 HTTP 响应。

  • 1.引入依赖

     <!-- HttpClient实现远程调用 -->
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpcore</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
    </dependency>
    

  • 2.示例代码

public class TestHttpClient {
    @Test
    public void doGet() throws IOException {
        //步骤一:创建httpClient实例
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //步骤二:创建HTTP请求
        HttpGet httpGet = new HttpGet("http://127.0.0.1:8094/masterdata/sysUser/getSysUserById?userId=郭郭");
        //步骤三:发送请求并获取响应数据
        CloseableHttpResponse response = httpClient.execute(httpGet);
        //步骤四:处理响应数据
        HttpEntity entity = response.getEntity();
        String result = EntityUtils.toString(entity);
        //步骤五:关闭httpClient和response
        response.close();
        httpClient.close();
    }
    @Test
    public void doPost() throws IOException {
        //步骤一:创建httpClient实例
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //步骤二:创建HTTP请求
        HttpPost httpPost = new HttpPost("http://127.0.0.1:8094/masterdata/sysUser/saveUser");
        //步骤三:设置请求体数据,使用JSON格式
        Map map = new HashMap<>();
        map.put("name", "郭郭");
        String requestBody = JSON.toJSONString(map);
        StringEntity stringEntity = new StringEntity(requestBody, "UTF-8");
        stringEntity.setContentType("application/json");
        httpPost.setEntity(stringEntity);
        
        //步骤四:发送请求并获取响应数据
        CloseableHttpResponse response = httpClient.execute(httpPost);
        //步骤五:处理响应数据
        HttpEntity entity = response.getEntity();
        String result = EntityUtils.toString(entity);
        //步骤五:关闭httpClient和response
        response.close();
        httpClient.close();
    }
}

​ 上述doPost方法中,我们使用了map转String,使用了JSON,这里引入JSON包即可。

<dependency>
      <groupId>com.alibaba.fastjson2</groupId>
      <artifactId>fastjson2</artifactId>
      <version>2.0.25</version>    
</dependency>

8.5 使用HttpURLConnection调用

​ HttpURLConnection 是 Java 自带的一个 HTTP 客户端工具,可以发送 HTTP 请求和接收 HTTP 响应。

  • 1.示例代码

    
    public class TestHttpURLConnection {
    
        @Test
        public void doGet() throws IOException {
            String userId = "郭郭";  // 参数值
            userId = URLEncoder.encode(userId, "UTF-8"); // 对参数值进行URL编码
            //步骤一:创建URL对象
            URL url = new URL("http://127.0.0.1:8094/masterdata/sysUser/getSysUserById?userId=" + userId);
            //步骤二:打开连接
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            //步骤三:设置请求方式
            conn.setRequestMethod("GET");
            //步骤四:读取响应内容
            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            reader.close();
            System.out.println(sb.toString());
        } 
        @Test
        public void doPost() throws IOException {
            //创建URL对象
            URL url = new URL("http://127.0.0.1:8094/masterdata/sysUser/saveUser");
            //打开连接
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            //设置请求方式
            conn.setRequestMethod("POST");
            // 设置请求头
            conn.setRequestProperty("Content-Type", "application/json");
            //启用输出流
            conn.setDoOutput(true);
            //设置请求体数据
            Map map = new HashMap<>();
            map.put("name", "郭郭");
            String requestBody = JSON.toJSONString(map);
            //发送请求体数据
            try (DataOutputStream outputStream = new DataOutputStream(conn.getOutputStream())) {
                outputStream.write(requestBody.getBytes(StandardCharsets.UTF_8));
            }
    
            //读取响应内容
            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            reader.close();
            System.out.println(sb.toString());
        } 
      }
    

    ​ doPost请求比doGet请求多了一些逻辑,比如我们设置了conn.setDoOutPut,这用于启动输出流。

    当您发送HTTP POST请求时,通常需要将数据发送到服务器,这些数据包含在请求体中。通过调用 setDoOutput(true),您告诉连接对象您将使用输出流来发送数据,这样它会准备好接受输出流,并将数据发送到服务器。这是发送POST请求体数据所必需的步骤。

    另一方面,对于HTTP GET请求,通常不需要发送请求体数据,因此不需要设置 setDoOutput(true)。

    ​ 而post请求的入参,我们是放入DataOutputStream进行传递,这里我们使用try语句块来包裹DataOutputStream,是因为DataOutputStream实现了AutoCloseable接口,因此它会在try块结束的时候自动关闭。

    ​ 我们介绍了HttpURLConnection,还有一种调用方式是URLConnection,它们是什么关系呢?

    ​ 通过查看源码,我们不难发现,HttpURLConnection继承自URLConnection,是它的一个子类,而HttpURLConnection专门用于处理HTTP协议的连接,如果需要处理其他协议,我们可以考虑使用通用的URLConnection。

8.6 使用 OKHttp 调用

​ OkHttp是一款高效的HTTP客户端框架,经过优化,具有低内存占有和出色的性能。

  • 1.引入依赖
<!--okhttp依赖-->
  <dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.0.0</version>
  </dependency>
  • 2.示例代码

public class TestOkHttp {

    @Test
    public void doGet() throws IOException {
        OkHttpClient client = new OkHttpClient();
        String url = "http://127.0.0.1:8094/masterdata/sysUser/getSysUserById?userId=郭郭";
        Request request = new Request.Builder().url(url).build();
        try (Response response = client.newCall(request).execute()) {
            ResponseBody body = response.body();
            System.out.println(body.string());
        }
    }

    @Test
    public void doPost() throws IOException{
        OkHttpClient client = new OkHttpClient();
        String url = "http://127.0.0.1:8094/masterdata/sysUser/saveUser";
        MediaType mediaType = MediaType.get("application/json; charset=utf-8");
        //requestBody请求入参
        Map map = new HashMap<>();
        map.put("name", "郭郭");
        RequestBody requestBody = RequestBody.create(mediaType, JSON.toJSONString(map));
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();
        try (Response response = client.newCall(request).execute()) {
            ResponseBody body = response.body();
            System.out.println(body.string());
        }
    }
}

​ doGet方法中,我们没有指定请求方式,因为Request默认就是get请求,所以构造的时候,可以省略。

8.7 使用 AsyncHttpClient调用

​ AsyncHttpClient是一个支持异步HTTP请求的开源库,用于非阻塞I/O操作,适用于需要高并发,非阻塞的应用。

  • 1.引入依赖
<dependency>
      <groupId>org.asynchttpclient</groupId>
      <artifactId>async-http-client</artifactId>
      <version>2.12.3</version>
</dependency>
  • 2.示例代码

public class TestAsyncHttpClient {
    @Test
    public void doGet() throws IOException {
        try (AsyncHttpClient client = new DefaultAsyncHttpClient();) {
            BoundRequestBuilder requestBuilder = client.prepareGet("http://127.0.0.1:8094/masterdata/sysUser/getSysUserById?userId=郭郭");
            CompletableFuture<String> future = requestBuilder.execute()
                    .toCompletableFuture()
                    .thenApply(Response::getResponseBody);
            //使用join等待响应完成
            String responseBody = future.join();
            System.out.println(responseBody);
        }
    }
    @Test
    public void doPost() throws IOException {
        try (AsyncHttpClient client = new DefaultAsyncHttpClient();) {
            BoundRequestBuilder requestBuilder = client.preparePost("http://127.0.0.1:8094/masterdata/sysUser/saveUser");
            //requestBody请求入参
            Map map = new HashMap<>();
            map.put("name", "郭郭");
            String requestBody = JSON.toJSONString(map);
            requestBuilder.addHeader("Content-Type", "application/json");
            requestBuilder.setBody(requestBody);
            CompletableFuture<String> future = requestBuilder.execute()
                    .toCompletableFuture()
                    .thenApply(Response::getResponseBody);
            //使用join等待响应完成
            String responseBody = future.join();
            System.out.println(responseBody);
        }
    }
  }

​ 我们看到doGet、doPost方法都使用了try代码块,对AsyncHttpClient进行包裹,同理因为继承了AutoCloseable,为了自动调用close关闭功能。

​ 我们在上述文章中,提到过的WebClient,也是支持异步,适用于高并发,非阻塞的场景,那么它们二者有什么区别呢?

​ 通过上网查阅资料,得知,AsyncHttpClient是一个独立的开源库,它不依赖于任何的框架或者技术栈,而WebClient是Spring Framework的一部分,如果你正在构建Spring应用程序,那么WebClient可能是更好的选择。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值