1. 程式人生 > >HttpClient伺服器模擬瀏覽器傳送請求

HttpClient伺服器模擬瀏覽器傳送請求

前言:
學習第三方登陸的時候,發現開頭的知識就用到了HttpClient,也就是伺服器模擬瀏覽器發起的請求,而我不會,於是就先花時間學習了下。
內容包括:GET、POST請求,以及各種零散的知識點。

maven 依賴

<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 http://maven.apache.org/maven-v4_0_0.xsd"
> <modelVersion>4.0.0</modelVersion> <groupId>login</groupId> <artifactId>wechat</artifactId> <packaging>war</packaging> <version>1.0</version> <name>wechat Maven Webapp</name> <url>http://maven.apache.org</url>
<!-- maven預設是1.5版本,不寫這段話會有警告,資源已過時 --> <profiles> <profile> <id>jdk-1.8</id> <activation> <activeByDefault>true</activeByDefault> <jdk>1.8</jdk> </activation> <properties> <maven.compiler.source>1.8
</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion> </properties> </profile> </profiles> <dependencies> <!--tomcat中有servlet,因此scope應該宣告為provided --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <scope>provided</scope> <version>3.0-alpha-1</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/net.sf.json-lib/json-lib --> <dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</artifactId> <version>2.4</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.2</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore</artifactId> <version>4.4.4</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpmime --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpmime</artifactId> <version>4.5.2</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload --> <!-- 封裝了對http傳輸中檔案相關操作 --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-io/commons-io --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency> <!-- https://mvnrepository.com/artifact/org.json/json --> <dependency> <groupId>org.json</groupId> <artifactId>json</artifactId> <version>20160810</version> </dependency> </dependencies> <build> <finalName>wechat</finalName> </build> </project>

預備知識

  • HttpClients

    Factory methods for CloseableHttpClient instances.

  • HttpEntity

    public interface HttpEntity
    An entity that can be sent or received with an HTTP message. Entities can be found in some requests and in responses, where they are optional.
    There are three distinct types of entities in HttpCore, depending on where their content originates:

 public class Test{
 private RequestConfig requestConfig = RequestConfig.custom()
            .setSocketTimeout(15000)
            .setConnectTimeout(15000)
            .setConnectionRequestTimeout(15000)
            .build();

public void testHttpClientGet(String httpUrl) {
        HttpGet httpGet = new HttpGet(httpUrl);
        CloseableHttpClient httpClient = null;
        CloseableHttpResponse httpResponse = null;
        HttpEntity httpEntity = null;
        String responseContent = null;
        try {
            //建立預設的httpClient例項
            httpClient = HttpClients.createDefault();
            httpGet.setConfig(requestConfig);
            httpResponse = httpClient.execute(httpGet);
            httpEntity = httpResponse.getEntity();
            responseContent = EntityUtils.toString(httpEntity, "utf-8");
            /*
            another encode method could refers to this blog
            "http://blog.sina.com.cn/s/blog_6d002146010130wv.html"
             */
            System.out.println(responseContent);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (httpResponse != null) {
                    httpResponse.close();
                }
                if (httpClient != null) {
                    httpClient.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

/**
     * POST請求1
     * @param httpUrl eg:http://localhost:8080/testPost?username=cwy&password=123
     */
    public void sendHttpPost(String httpUrl) {
        HttpPost httpPost = new HttpPost(httpUrl);
        testHttpClietPost(httpPost);
    }

    /**
     * POST請求2
     * @param httpUrl 請求地址
     * @param params  key1=value1&key2=value2
     */
    public void sendHttpPost(String httpUrl, String params) {
        HttpPost httpPost = new HttpPost(httpUrl);
        try {

            StringEntity stringEntity = new StringEntity(params, "UTF-8");
            /*
            *http://blog.csdn.net/blueheart20/article/details/45174399
             */
            stringEntity.setContentType("application/x-www-form-urlencoded");
            /*
            StringEntity implements httpEntity extends AbstractHttpEntity
            AbstractHttpEntity implements httpEntity
            httpEntity has get-Content/ContentEncoding/ContentLength/ContentType 
            method
            but only AbstractHttpEntity has set-xx method
            more information refers to  API     
            "http://hc.apache.org/httpcomponents-core
            ga/httpcore/apidocs/org/apache/http/HttpEntity.html"
             */
            httpPost.setEntity(stringEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
        testHttpClietPost(httpPost);
    }

     /**
     * 傳送POST請求(帶檔案)
     * @param httpUrl 請求地址
     * @param maps  請求引數
     * @param fileLists 檔案列表
     */
    public void sendHttpPost3(String httpUrl, Map<String,String> maps, List<File> fileLists){
        HttpPost httpPost=new HttpPost(httpUrl);
        MultipartEntityBuilder meBuilder=MultipartEntityBuilder.create();
        for(String key:maps.keySet()){
            meBuilder.addPart(key,new StringBody(maps.get(key),ContentType.TEXT_PLAIN));
        }
        for(File file:fileLists){
            FileBody fileBody=new FileBody(file);
            meBuilder.addPart("files",fileBody);
        }
        HttpEntity httpEntity=meBuilder.build();
        httpPost.setEntity(httpEntity);
        testHttpClietPost(httpPost);
    }
/*
//測試
public void testPost3(){
        Map<String,String> maps=new HashMap<>();
        maps.put("username","cwy");
        maps.put("password","123456");
        List<File> files=new ArrayList<>();
        files.add(new File("E:\\前端系列\\作業--完整網頁\\images\\11.png"));
        sendHttpPost("http://localhost:8080/testPostFiles",maps,files);
    }
*/
public void testHttpClietPost(HttpPost httpPost) {
        CloseableHttpClient closeableHttpClient = null;
        CloseableHttpResponse closeableHttpResponse = null;
        HttpEntity httpEntity = null;
        String responseContent = null;
        try {
            closeableHttpClient = HttpClients.createDefault();
            httpPost.setConfig(requestConfig);
            closeableHttpResponse = closeableHttpClient.execute(httpPost);
            httpEntity = closeableHttpResponse.getEntity();
            responseContent = EntityUtils.toString(httpEntity, "UTF-8");
            System.out.println("responseContent=" + responseContent);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (closeableHttpResponse != null) {
                    closeableHttpResponse.close();
                }
                if (closeableHttpClient != null) {
                    closeableHttpClient.close();
                }
            } catch (Exception e) {
            }

        }
    }
 }

GET請求以及非檔案POST請求servlet

 //非通用,僅為測試
 protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
        System.out.println("QueryString"+request.getQueryString());
        System.out.println("CharacterEncoding"+request.getCharacterEncoding());
        System.out.println("ContentType"+request.getContentType());
        Map<String,String> map=new HashMap();
        String username=request.getParameter("username");
        String password=request.getParameter("password");
        map.put("username",username);
        map.put("password",password);
        map.put("requestContentType",request.getContentType());
        System.out.println("map="+map);
        response.setContentType("application/json; charset=utf-8");
        response.setCharacterEncoding("UTF-8");
        String jsonObject=JSONObject.valueToString(map);
        System.out.println("jsonObject="+jsonObject);
        OutputStream out = response.getOutputStream();
        out.write(jsonObject.getBytes("utf-8"));
        out.flush();
    }

檔案請求抓包
寫了簡易的jsp頁面對檔案上傳進行抓包測試

<form action="${pageContext.request.contextPath}/testPostFiles" enctype="multipart/form-data" method="post">
        上傳使用者: <input type="text" name="username"><br/>
        上傳檔案1: <input type="file" name="file1"><br/>
        上傳檔案2:<input type="file" name="file2"><br/>
        <input type="submit" value="submit">
</form>

寫了簡易的jsp頁面對檔案上傳進行抓包測試

  • 注意,對於form/data這種形式,無法以key-value的形式獲取值,只能以流的方式,
    如果選擇手動實現,不使用第三方jar包(如我這兒使用的uploadfile),就要以分隔字元、判斷、切割等複雜操作來實現流的轉換。
    對於Content-Type,這兒有篇文章 點選連結

攜帶檔案POST請求servlet

/*
這只是最簡單的實現,還可以優化
1、檔案上傳能夠設定緩衝區 
2、檔名應該有UUID或其他唯一名,避免檔案覆蓋
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String savePath=getServletContext().getRealPath("/WEB-INF/upload");
        File file=new File(savePath);
        if(!file.exists()&&!file.isDirectory()){
            System.out.println(savePath+"目錄不存在,需要建立");
            file.mkdir();
        }
        String message="";
        /*
        使用Apache檔案上傳元件處理檔案上傳步驟
        1、建立一個DiskFileItemFactory工廠
        2、建立一個檔案上傳解析器
        3、判斷提交上來的資料是否是上傳表單的資料
        4、使用ServletFileUpload解析器解析上傳資料,解析結果返回一個List<FileItem>
         */
        try{
            DiskFileItemFactory factory=new DiskFileItemFactory();
            ServletFileUpload upload=new ServletFileUpload(factory);
            //解決上傳檔案中的中文亂碼
            upload.setHeaderEncoding("UTF-8");
            if(!ServletFileUpload.isMultipartContent(request)){
                //按照傳統方式獲取資料
                return;
            }
            List<FileItem> list=upload.parseRequest(request);
            for(FileItem item:list){
                //如果fileitem中封裝的是普通輸入項的資料
                if(item.isFormField()){
                    String name=item.getFieldName();
                    String value=item.getString("utf-8");
                    System.out.println("name="+name+" ;value="+value);
                }else{
                    //若封裝的是上傳檔案
                    String filename=item.getName();
                    System.out.println("filename="+filename);
                    if(filename==null||filename.trim().equals("")){
                        continue;
                    }
                    //注意,不同的瀏覽器提交的檔名是不一樣的,有些帶路徑,有些不帶
                    //處理上傳檔案的路徑,統一隻保留檔名部分
                    filename=filename.substring(filename.lastIndexOf("\\")+1);
                    InputStream in=item.getInputStream();
                    FileOutputStream out=new FileOutputStream(savePath+"\\"+filename);
                    //建立一個緩衝區
                    byte buffer[]=new byte[1024];
                    //判斷輸入流中的資料是否已讀完的標誌
                    int len=0;
                    while((len=in.read(buffer))>0){
                        out.write(buffer,0,len);
                    }
                    in.close();
                    out.close();
                    //刪除處理檔案上傳時生成的臨時檔案
                    item.delete();
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }