HttpClient发送请求

HttpClient发送请求

前言:前段时间接到一个需求,要求系统中实现一个可以上传语音文件的功能,然后将文件和需要的参数发送到电信的接口上;

开始说起来感觉很简单,但是就真的被折磨了好几天,主要还是httpClient发送文件和参数到指定url,还有要从公司内网调中台再调电信接口这个过程花费了不少时间。既然涉及到了文件的上传,就想着把涉及到的东西都系统的学一遍,毕竟之前也没做过这些。

HttpClient请求数据到指定接口

对于通过HttpClien发送文件,就要先了解HttpClient的知识了;

HttpClient

理解:可以说HTTP Client类似与浏览器,它可以代替浏览器发送Http请求到指定的url;

所有的 Http 请求都有一个请求行(request line),包括方法名、请求的 URI 和 Http 版本号

HttpClient 支持 HTTP/1.1 这个版本定义的所有 Http 方法:GET,HEAD,POST,PUT,DELETE,TRACE 和 OPTIONS。对于每一种 http 方法,HttpClient 都定义了一个相应的类:
HttpGet, HttpHead, HttpPost, HttpPut, HttpDelete, HttpTrace 和 HttpOpquertions

URI 即统一资源标志符,用来标明 Http 请求中的资源。Http request URIs 包含协议名、主 机名、主机端口(可选)、资源路径、query(可选)和片段信息(可选)。

现在如果直接贴上HttpClient的工具类的话,感觉又看不懂。所以还是先温习一下http请求与响应的过程和格式;

HTTP请求

首先在浏览器对服务器发送请求之前,必须要做的一步就是通过三次握手建立TCP连接(’重点‘后面详细学习);

一个HTTP请求报文由请求行(request line),请求头(headers),空行(blank line)和请求数据(request body)4个部分组成。

  1. 请求行

    分为三个部分:请求方法,请求地址URL和HTTP协议版本;POST /login.htm HTTP/1.1

    1. 请求方法:常见GET、POST、DELETE、PUT,这里不介绍了(后面详细学习)

    2. URL:统一资源定位符

      它和URI的区别就是前者是定位(具体地址),后者是标识(身份证号)。

      URL组成:协议+主机+端口+路径

    3. 协议版本:常见的HTTP/1.0 、HTTP/1.1

  2. 请求头
    感觉类似与键值对,就是一些附加信息;
    host: 接受请求的服务器地址(ip地址端口号)
    ​user-agent: 发送请求应用程序名称(浏览器的信息)
    Connection: 与连接相关的属性
    Accept-charset:通知服务器可以发送的编码格式
    Accept-Encoding:通知服务器可以送的数据压缩格式
    Accept-Language:通知服务器可以发送的语言

    总结一下accept开头的感觉就是对服务器响应时的要求;

    如果是GET请求方法的话,应该到这里就结束了;
    请添加图片描述

  3. 空行

    如果后面还是信息,到这里就必须有一个空行,与下面的内容分开,表示请求头部已经结束了;

  4. 请求体(数据)

    至于请求数据主要是在POST的请求方法中才会看到的,GET方法的请求数据会直接加在URL的后面直接发送到服务器(例如百度搜索);

    发现post方法,除了多个请求数据,请求头好像也还不太一样;
    请添加图片描述

    post请求头好像多了两个内容长度和内容类型,这好像也就是描述下面多出来的请求数据的对吧;

    整个请求也就多了一个空行和请求数据

http响应

请求报文准备好后,接下来就是将请求发送到服务器端了,然后由服务器根据请求头中的accept要求进行响应;

HTTP响应报文由状态行(status line)、响应头部(headers)、空行(blank line)和响应数据(response body)4个部分组成。

  1. 状态行

    也不主要介绍了,反正根据状态行就能知道是哪里出的问题,常见4xx,500;

  2. 响应头 (直接看实际的响应截图吧,很晚很困了~~)

请添加图片描述

  1. 响应数据

    存放需要返回给客户端的数据信息。

    看状态行200应该就知道这个请求-响应是成功了的,并且返回的响应数据是Json格式的;

    大家平常浏览网页的话,一般响应数据的content-type应该是text/html;charset=UTF-8这样子的,然后响应数据是一串html代码,经过浏览器解析渲染呈现给用户。(遇到静态资源时,就向服务器端去请求下载)

    最后就是关闭TCP连接;

这搞得,就是想记录一下文件的上传和下载,结果还没开始就整出这么多东西来。。。

对于HTTP请求和响应携带的相关参数了解了,下面就可以了解HttpClient了;

HttpClient客户端

首先说一下,对于HttpClient发送请求,网上很多很多封装好的方法,工具类;针对于不同的请求方式,不同的请求参数,封装方法实现大不一样;这里就只介绍一下普通大体的一个封装实现,有特别需求的可以根据自己的要求去做更改或者csdn上搜索;

发送请求步骤:

  1. 创建HttpClient对象;

  2. 创建请求方法的实例;

    常见请求方法对象HttpGet和HttpPost(url+请求方式),个人理解这里应该就相当于对请求行的创建了吧;

  3. 添加请求参数;

    调用请求方法对象的setParams(HttpParams params)添加请求参数,HttpPsot对象也可以使用setEntity(HttpEntity entity)方法添加请求参数;

  4. 调用httpClient.execute(HttpXXX request)正式发送请求,返回一个HtppResponse对象;

  5. 调用HttpResponse的getAllHeaders()getHeaders(String name)等方法可获取服务器的响应头,getEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。

  6. 释放连接;

具体实现:

  1. 基于springboot项目,添加httpclient依赖;

    <dependency>
          <groupId>org.apache.httpcomponents</groupId>
          <artifactId>httpclient</artifactId>
    </dependency>
    
  2. GET请求

    不管怎么说,步骤里肯定有创建HttpClient对象,HttpGet对象,Params请求参数(有参的情况下);尤其是Params的添加,因为大家需求不同,需要传的参数以及类型不同,对于参数的封装方式就有不同;

    public static String sendGetForm(String url, Map<String, String> params) {
          
          
           try (CloseableHttpClient httpClient = HttpClients.createDefault()){
          
          
               String param = "";
               if (params != null) {
          
          
                   Set<Entry<String, String>> set = params.entrySet();
                   for(Entry<String, String> entry : set) {
          
          
                      if(param.isEmpty()) {
          
          
                         param = entry.getKey() + "=" + entry.getValue();
                      }else {
          
          
                         param = param + "&" + entry.getKey() + "=" + entry.getValue();
                      }
                   }
               }
               url = param.isEmpty() ? url : url + "?" + param;
               HttpGet httpGet = new HttpGet(url);
               try(CloseableHttpResponse response = httpClient.execute(httpGet)){
          
          
                    return EntityUtils.toString(response.getEntity(), "utf-8");
               }
           } catch (Exception e) {
          
          
               log.error("发送get请求失败", e);
          throw new RuntimeException("发送get请求失败", e);
           }
    }
    

    简单介绍一下吧:

    • 创建httpClient,至于为什么声明的CloseableHttpClient 类,这个类可以在我们请求完自动释放连接;

      CloseableHttpClient httpClient = HttpClients.createDefault()

    • 下面定义param参数配置到url后

      这样请求参数可有可无,并且参数放到url后需要的格式;

    • 创建httpGet请求对象

      HttpGet httpGet = new HttpGet(url);

    • 发送请求,接收响应(配置响应体)

      CloseableHttpResponse response = httpClient.execute(httpGet)

  3. POST请求

    POST请求又不一样啦,因为它有请求体可以传各种类型的数据(content-type)

    看了一下公司封装好的post请求,真的好多种,根据你要传的参数不同,需要配置的请求头、请求体就有所不同;所以在我们使用发送方法前要提前明白自己需要传什么样的数据,这样也就知道需要如何封装请求参数;、

    对于返回的请求结果response后面的,一般就是将获取到的响应体进行一个编码,防止中文乱码等;

    //Post方式表单提交
    public static String sendPostForm(String url, Map<String, String> params) {
          
          
           try (CloseableHttpClient httpClient = HttpClients.createDefault()){
          
          
               HttpPost httpPost = new HttpPost(url);
               if (params != null) {
          
          
                   List<NameValuePair> nameValuePairs = new ArrayList<>();
                   for (String key : params.keySet()) {
          
          
                       nameValuePairs.add(new BasicNameValuePair(key, params.get(key)));
                   }
                   UrlEncodedFormEntity entity = new UrlEncodedFormEntity(nameValuePairs);
                   httpPost.setEntity(entity);
               }
               try(CloseableHttpResponse response = httpClient.execute(httpPost)){
          
          
                    return EntityUtils.toString(response.getEntity(), "utf-8");
               }
           } catch (Exception e) {
          
          
               log.error("发送post请求失败", e);
          throw new RuntimeException("发送post请求失败", e);
           }
    }
    
    //发送json格式数据
    public static String sendPostJson(String url, String json) {
          
          
           try (CloseableHttpClient httpClient = HttpClients.createDefault()){
          
          
               HttpPost httpPost = new HttpPost(url);
               StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
               httpPost.setEntity(entity);
               try(CloseableHttpResponse response = httpClient.execute(httpPost)){
          
          
                    return EntityUtils.toString(response.getEntity(), "utf-8");
               }
           } catch (Exception e) {
          
          
               log.error("发送post请求失败", e);
          throw new RuntimeException("发送post请求失败", e);
           }
    }
    

    艰难介绍一下:

    • 介于NameValuePair类型,叫做简单名称值对节点类型,就是请求数据中要求的参数类型吧;
    • 介于UrlEncodedFormEntity(URL实体转换工具)而且它直接在请求数据上指定了content-type的值;也可以用httpPost.addHeader(“contetntType”,“xxx”)来指定请求数据类型;
    • 发送json格式数据和表单数据,对应的请求content-type 也就不同。

    还有一个就是通过post请求发送文件

    private String sendFile( String url,MultipartFile file,HashMap<String,String> map) throws IOException {
          
          
            CloseableHttpClient httpclient = HttpClients.createDefault();
            HttpPost httpPost = new HttpPost(url);
            String rspMsg =null;
      
            MultipartEntityBuilder params = MultipartEntityBuilder.create();
      		//定义post请求的请求数据类型
            ContentType strContent = ContentType.create(ContentType.MULTIPART_FORM_DATA.getMimeType(),Consts.UTF_8);
      		//将文件放到请求体中
            params.addBinaryBody("file", file.getInputStream(), ContentType.DEFAULT_BINARY, file.getOriginalFilename());
      		//添加需要发送的普通参数
            params.addTextBody("waterno",map.get("waterno"),strContent);
            params.addTextBody("cutwatertone",map.get("cutwatertone"),strContent);
            HttpEntity httpEntity =  params.build();
            httpPost.setEntity(httpEntity);
            try {
          
          
                StringBuilder sb = new StringBuilder();
                String line;
                HttpResponse httpResponse = httpclient.execute(httpPost);
                InputStream inputStream = httpResponse.getEntity().getContent();
                BufferedReader br = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
                while ((line = br.readLine()) != null) {
          
          
                    sb.append(line);
                }
                rspMsg = URLDecoder.decode(sb.toString(),"UTF-8");
            } catch (Exception e) {
          
          
                e.printStackTrace();
            }
            return rspMsg;
    }
    

    对于这个发送文件的封装方法,网上真的很多,但有些就是封装了file的参数,没有其他普通参数的封装,还有要主要的就是发送文件必须要把文件转换成流才行。当时做发送文件和参数到电信接口时,就在网上找了好多好多方法,都不行…;其实这也是再网上找的,不同的是在创建请求体时,使用的是一个创建者设计模式,有点绕。但是大致步骤还是一样的。

对方接收请求参数

通过HttpClient发送了请求,但服务端如何接收请求参数呢,其实很简单的。大家只要知道客户端发送的是什么请求,参数是什么类型就可以了;

对于get请求,对应的controller方法接收参数使用@RequestParam,

对于post请求,对应的controller方法接收参数使用@RequestBody;

总结:

其实理解了HTTP请求,对于HttpClient发送请求,最难搞的就是对于请求体的封装,尤其POST请求中content-type,它决定请求数据是一个什么类型,例如表单类型,json类型,文件类型等等;然后你在httpPost.addEntity(xxxx)这个方法的参数中就要传递不同的请求体参数类型,网上实现又很多,不一定每个都适合自己;所以还是主要理解HttpClient请求创建的大致步骤吧;而且现在springboot中有个现成的RestTemplate类,也是来在客户端发送请求的,只需要直接调用方法就可以,但是不知道为什么当时直接用它发送文件就是不行。RestTemplate有时间可以去了解一下:RestTemplate用法

最后,本文的HTTP请求和HttpClient由其他文章理解写成,相关链接:

https://blog.csdn.net/w372426096/article/details/82713315

https://blog.csdn.net/ailunlee/article/details/90600174

下一篇文件的下载和上传(本来上次说的是这篇是文件的上传和下载的,结果变成了HttpClient发送请求)

猜你喜欢

转载自blog.csdn.net/weixin_43573186/article/details/121318461
今日推荐