13、HttpClient伺服器跨域請求

項羽齊發表於2018-03-26

回撥

1.1 回撥函式

1.1.1 回撥的原理圖

說明:在架構設計中,回撥的機制經常會被使用,課下自行學習.

 

 

 

1.2 JSON的資料結構

1.2.1 JSON官網介紹

 

 

1.2.2 Object格式

 

 

例子:{“key1”:”value1”,key2:”value2”}

User(id.name.age)

1.2.3 陣列格式

 

 

例子:[“value1”,”value2”,”value3”]

1.2.4 複雜格式

說明:將上述2中簡單JSON格式進行無限層級的巢狀.最終形成的

 

 

例子 [1,{id:1,name:”tom”,age:18}]

{id:1,name:"tom",array:[1,2,3,4,5,{array:[22,33,44,55]}]}

 

 

1.3 JSONP呼叫呼叫

1.3.1 流程圖

 

 

1.4 快取操作

1.4.1 編輯Controller

/**
     * 利用工具類直接返回JSONP的物件 callback({JSON})
     * @param callback
     * @return
     */
    @RequestMapping("/web/itemcat/all")
    @ResponseBody
    public Object findItemCat(String callback){
        
        ItemCatResult itemCatresult = itemCatService.findCacheItemCatAll();
        
        //負責JSONP物件返回 構造方法中新增返回的資料
        MappingJacksonValue jacksonValue = 
                new MappingJacksonValue(itemCatresult);
        
        //設定返回值方法
        jacksonValue.setJsonpFunction(callback);
        return jacksonValue;
    }
View Code

 

1.4.2 編輯Service

/**
     * 1.查詢時應該先查詢快取
     * 2.如果快取中沒有快取資料則執行業務操作查詢資料
     * 3.將查詢結果返回,將查詢的結果存入快取中
     * 4.如果快取中含有該資料
     * 5.將快取資料轉化物件返回.滿足程式設計的規範
     * @return
     */
    //實現三級商品分類的快取操作
    @Override
    public ItemCatResult findCacheItemCatAll(){
        
        String key = "ITEM_CAT_ALL";
        
        String jsonData = jedisCluster.get(key);
        
        try {
            //判斷資料是否為空
            if(StringUtils.isEmpty(jsonData)){
                ItemCatResult itemCatResult = 
                        findItemCatAll();
                //將物件轉化為JSON串
                String restJSON = 
                        objectMapper.writeValueAsString(itemCatResult);
                //將資料存入redis中
                jedisCluster.set(key, restJSON);
                return itemCatResult;
                
            }else {
                ItemCatResult itemCatResult =
                        objectMapper.readValue(jsonData, ItemCatResult.class);
                return itemCatResult;    
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
View Code

 

商品詳細頁面展現

2.1 HttpClent

2.1.1 介紹

 

總結:在業務層程式碼中,通過httpClient的方式可以模擬瀏覽器發出的Http請求.

2.1.2 HttpClientJSONP的差別

 

 

 

區別:

  1.傳送請求的位置不同.

    JSONP的請求是由瀏覽器發出的.

    httpClient請求是由業務層模擬http協議發出的

  2.瀏覽器監控不同

    JSONP的呼叫瀏覽器可以完全的監控.

    HttpClient的方式瀏覽器不能監控其呼叫.對於業務的操作一般都會使用httpClient

  3.返回值處理不同

     1.JSONP的處理是通過頁面的JS的方式解析返回結果

     2.HttpClient是通過業務程式碼的方式解析返回值結果.

2.2 入門案例

2.2.1 Jar包引入

<!-- httpclient -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>${httpclient.version}</version>
        </dependency>

 

 

2.2.2 Get請求

//模擬get請求
    @Test
    public void testGet() throws ClientProtocolException, IOException{
        
        //1.建立httpCLient物件
        CloseableHttpClient httpClient 
         = HttpClients.createDefault();
        
        //2.定義uri
        String uri = "https://item.jd.com/1607218.html";
        
        //3.定義請求方式
        HttpGet httpGet = new HttpGet(uri);

        //4.發出請求
        CloseableHttpResponse response
                     = httpClient.execute(httpGet);
        
        //判斷請求是否正確
        if(response.getStatusLine().getStatusCode() == 200){
            //獲取請求內容
            String result = 
                    EntityUtils.toString(response.getEntity())  ;
            System.out.println("列印實體資訊"+result);
        }
    }
View Code

 

2.2.3 Post提交

@Test
    public void testPost() throws ClientProtocolException, IOException{
        
        //獲取httpclient物件
        CloseableHttpClient client = 
                HttpClients.createDefault();
        
        //定義url
        String url = "http://www.tmooc.cn/web/index_new.html?tedu";
        
        //定義Post請求方式
        HttpPost httpPost = new HttpPost(url);
        
        //Entity中需要設定post中提交的引數
        //UrlEncodedFormEntity entity =  new UrlEncodedFormEntity(parameters)
        //httpPost.setEntity(entity);
        
        CloseableHttpResponse httpResponse 
         =    client.execute(httpPost);
        
        //判斷資料是否正確
        if(httpResponse.getStatusLine().getStatusCode() == 200){
            
            String msg = EntityUtils.toString(httpResponse.getEntity());
            System.out.println(msg);
        }
    }
View Code

 

2.3 Spring整合HttpClient

2.3.1 匯入Spring配置檔案

<!-- 定義httpclient連線池 -->
    <bean id="httpClientConnectionManager" class="org.apache.http.impl.conn.PoolingHttpClientConnectionManager" destroy-method="close">
        <!-- 設定連線總數 -->
        <property name="maxTotal" value="${http.pool.maxTotal}"></property>
        <!-- 設定每個地址的併發數 -->
        <property name="defaultMaxPerRoute" value="${http.pool.defaultMaxPerRoute}"></property>
    </bean>
    
    <!-- 定義 HttpClient工廠,這裡使用HttpClientBuilder構建-->
    <bean id="httpClientBuilder" class="org.apache.http.impl.client.HttpClientBuilder" factory-method="create">
        <property name="connectionManager" ref="httpClientConnectionManager"></property>
    </bean>
    
    <!-- 得到httpClient的例項 -->
    <bean id="httpClient" factory-bean="httpClientBuilder" factory-method="build"/>
    
    <!-- 定期清理無效的連線 -->
    <bean class="com.jt.common.util.IdleConnectionEvictor" destroy-method="shutdown">
        <constructor-arg index="0" ref="httpClientConnectionManager" />
        <!-- 間隔一分鐘清理一次 -->
        <constructor-arg index="1" value="60000" />
    </bean>
    
    <!-- 定義requestConfig的工廠 -->
    <bean id="requestConfigBuilder" class="org.apache.http.client.config.RequestConfig.Builder">
        <!-- 從連線池中獲取到連線的最長時間 -->
        <property name="connectionRequestTimeout" value="${http.request.connectionRequestTimeout}"/>
        <!-- 建立連線的最長時間 -->
        <property name="connectTimeout" value="${http.request.connectTimeout}"/>
        <!-- 資料傳輸的最長時間 -->
        <property name="socketTimeout" value="${http.request.socketTimeout}"/>
        <!-- 提交請求前測試連線是否可用 -->
        <property name="staleConnectionCheckEnabled" value="${http.request.staleConnectionCheckEnabled}"/>
    </bean>    
    
    <!-- 得到requestConfig例項 -->
    <bean id="requestConfig" factory-bean="requestConfigBuilder" factory-method="build" />
View Code

 

2.3.2 匯入properties檔案

 

 

Spring引入配置檔案

 

 

2.3.3 編輯Get請求

Get請求

Get請求
/**
     * 說明:
     *     編輯工具類時需要處理2中型別的請求 get post
     * 引數介紹:
     *  addUser?id:1&name=tom&age=18
     *     定義url  確定訪問的路徑
     *  定義引數集合 Map<String,String>.指定引數的型別都是String
     *  定義字符集 encode=utf-8 
     *  
     *  方法介紹
     *  根據不同的使用者需求,過載多個方法
     */
    
    
    /**
     * 編輯思路:
     * Url:findItem?id=1&name=tom
     * 1.判斷是否包含引數,如果包含引數應該將引數進行動態的拼接
     * 2.判斷是否指定字符集編碼  如果沒有指定則設定預設值UTF-8
     * 3.通過httpClient物件發起http請求
     * 4.判斷返回值是否有效
     * 5.將結果返回
     * @param url
     * @param params
     * @param charset
     * @return
     * @throws URISyntaxException 
     */
    public String doGet(String uri,Map<String, String> params,String charset) throws URISyntaxException{
        //1.判斷是否含有引數 Url:findItem?id=1&name=tom
        URIBuilder builder = new URIBuilder(uri);
        if(params !=null){
            //整理get提交引數
            for (Map.Entry<String, String> param :params.entrySet()) {
                builder.addParameter(param.getKey(), param.getValue());
            }
            //Uri:findItem?id=1&name=tom&age=18
            System.out.println("編輯uri結果:!!!!"+builder.toString());
            uri = builder.toString();    
        }
        
        //判斷字符集編碼
        if(StringUtils.isEmpty(charset)){
            
            charset = "UTF-8";
        }
        
        //定義Get請求物件
        HttpGet httpGet = new HttpGet(uri);
        httpGet.setConfig(requestConfig);
        
        //傳送請求
        try {
            CloseableHttpResponse httpResponse 
                = httpClient.execute(httpGet);
            //判斷請求是否正確
            if(httpResponse.getStatusLine().getStatusCode() == 200){
                //result是遠端返回的JSON資料
                String result = EntityUtils.toString(httpResponse.getEntity(),charset);
                return result;    
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }
View Code

 

2.3.4 編輯Post請求

Post請求

Post請求
/*
     * 1.doPost請求方式和doget類似
     * 2.doPost中的引數傳遞藉助form表單.
     * 
     * 編碼步驟:
     *     1.定義請求的物件 httpPost()
     *  2.判斷是否含有引數,如果含有引數需要表單的賦值
     *  3.將form表單的引數賦值給post請求
     * 
     */
    public String doPost(String uri,Map<String, String> params,String charset) throws UnsupportedEncodingException{
        
        //定義post提交方式
        HttpPost httpPost = new HttpPost(uri);
        if(StringUtils.isEmpty(charset)){
             
            charset = "UTF-8";
        }
        //判斷引數是否為空
        if(params !=null){
            //定義引數提交的集合
            List<NameValuePair> parameters = 
                    new ArrayList<NameValuePair>();
            
            //為引數賦值
            for (Map.Entry<String, String> param : params.entrySet()) {
                
                BasicNameValuePair nameValuePair =
                        new BasicNameValuePair(param.getKey(), param.getValue());
                parameters.add(nameValuePair);
            }
            
            UrlEncodedFormEntity entity = 
                    new UrlEncodedFormEntity(parameters, charset);
            //為post請求賦值
            httpPost.setEntity(entity);
        }
        try {
            CloseableHttpResponse response = 
                    httpClient.execute(httpPost);
            
            //判斷返回值是否正確
            if(response.getStatusLine().getStatusCode() == 200){
                
                String result = EntityUtils.toString(response.getEntity(),charset);
                return result;
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        } 
        return null;
    }
View Code

 

2.4 商品詳細實現

2.4.1  頁面分析

說明:當查詢某一個商品時,會傳送一個請求,並且採用resuFul風格進行資料的提交

http://www.jt.com/items/562379.html

2.4.2 HttpClient呼叫流程圖

 

 

 

說明:

1.客戶端首先接收請求 http://www.jt.com/items/1474391982.html,交給Controller處理

2.呼叫客戶端Service

3.通過httpClient進行跨域訪問

4.服務端Controller接收請求並且處理

5.業務層程式碼獲取Item資料

2.4.3 分析頁面

說明:根據訪問地址編輯Controller

 

2.4.4 編輯客戶端Controller

說明:controller攔截客戶端請求

/**

 * 將來的頁面是否需要人為的指定

 * @param itemId

 * @return

 */

@RequestMapping("/{itemId}")

public String findItemById(@PathVariable Long itemId,Model model){

 

Item item = itemService.findItemById(itemId);

 

model.addAttribute("item", item);

 

return "item";

}

 

2.4.5 呼叫客戶端Service

/**

 * 經過京淘前臺的業務層,去訪問後臺的業務程式碼?

 * 解決策略:跨域

 * 問題:在業務層中不能採用JSONP的形式進行跨域呼叫

 * 解決:採用HttpClient方式進行呼叫

 *

 */

@Override

public Item findItemById(Long itemId) {

String uri = "http://manage.jt.com/web/item/findItemById/"+itemId;

try {

String jsonData = httpClientService.doGet(uri);

 

if(!StringUtils.isEmpty(jsonData)){

//需要將JSON串轉化為Item物件

Item item =

objectMapper.readValue(jsonData, Item.class);

return item;

}

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

 

2.4.6 編輯後端Controller

說明:根據客戶端發出的請求,Controller接收請求

//http://manage.jt.com/web/item/findItemById/"+itemId

@RequestMapping("/findItemById/{itemId}")

@ResponseBody

public Item findItemById(@PathVariable Long itemId){

 

//根據Id獲取後臺Item資料

Item item = itemService.findItemById(itemId);

 

return item;

}

2.4.7 編輯服務端Service

說明:根據ItemId獲取Item資料並且返回

@Override

public Item findItemById(Long itemId) {

 

Item item = itemMapper.selectByPrimaryKey(itemId);

 

return item;

}

2.5 商品資訊實現快取

2.5.1 前臺實現快取處理

 

2.6 維護redis中資料的一致性

2.6.1 後臺資料更新維護

說明:當後臺資料進行更新或者刪除操作時,需要進行redis記憶體的資料維護,策略將redis中的更新資料直接刪除.

 

相關文章