13、HttpClient服务器跨域请求

回调

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中的更新数据直接删除.

原文地址:https://www.cnblogs.com/xiangyuqi/p/8652828.html