zoukankan      html  css  js  c++  java
  • 分布式系统中的跨域请求问题

    1 跨域请求

    在构建分布式系统时,将门户系统(负责前端页面展示的控制器)和获取数据的系统(从数据库获取资料)分开。在开发过程中,会遇到跨域请求问题。

    image

    什么是跨域请求

    跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。

    1. 资源跳转: A链接、重定向、表单提交
    2. 资源嵌入: <link><script><img><frame> 等 dom 标签,还有样式中background:url()、@font-face()等文件外链
    3. 脚本请求: js 发起的 ajax 请求、dom 和 js 对象的跨域操作等

    其实我们通常所说的跨域是狭义的,是由浏览器同源策略限制的一类请求场景。主要特征有两种:

    1. 域名不相同,即两个不同的应用

      image

    2. 域名相同,但是端口不同,即同一个应用中的不同子系统

      image

    解决方式

    想要从数据系统的接口中获得数据,我常用的有两种方式:

    • 若使用前端 ajax 获取数据,常用的解决方式是使用 jsonp 实现跨域请求
    • 若从后台程序中获取数据,使用 HttpClient

    2 jsonp

    通常为了减轻 web 服务器的负载,我们把js、css,img 等静态资源分离到另一台独立域名的服务器上,在 html 页面中再通过相应的标签从不同域名下加载静态资源,这种行为被浏览器允许。基于此原理,我们可以把需要的数据封装成一段 js 代码。

    实现方式

    jsonp 只能使用 get 请求,前台请求时,指定回调函数名,后台系统将数据封装成 js 代码,将数据放到参数里面:

    callBack(数据)
    

    前台 javascript 里准备好对应的回调函数操作数据,或在 ajax 中用 success 的 function 去接受参数,并操作数据。示例如下:

    /*
    8081 端口的服务器有一静态资源  test.json,现在需要从 8080 的服务器去获得并显示到页面上。这里将数据修改,封装成 js:
    callback({"data":"helloworld", "status":"200"})
    */
    $.ajax({
                    url: "http://localhost:8081/test.json",
                    type: "GET",
                    dataType: "jsonp",   //jsonp请求
                    jsonp: "callbackFunction",   //请求参数名
                    jsonpCallback: "callback",  //回调函数名称,也可以直接用 success 接受参数
                    /*success: function (data) {
                        $('#text').val(JSON.stringify(data));
                    }*/
    })
    function callback(data) {
                $('#text').val(JSON.stringify(data));
    }
    

    3 HttpClient

    HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。实现了所有 HTTP 的方法(GET,POST,PUT,HEAD 等)。在 java 代码中,可以通过它发送 Http 请求,通常用来实现远程接口调用。

    maven 中需要的依赖:

    <dependency>
    		<groupId>org.apache.httpcomponents</groupId>
    		<artifactId>httpclient</artifactId>
    </dependency>
    

    示例

    //无参数 get 请求
    @Test
    public void doGet() {
        //创建 HttpClient 对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //创建 Get 请求
        HttpGet httpGet = new HttpGet("http://www.baidu.com/");
        CloseableHttpResponse response = null;
        try {
            //执行请求
            response = httpClient.execute(httpGet);
            //输出请求状态
            System.out.println(response.getStatusLine());
            //判断请求状态并输出响应内容
            if (response.getStatusLine().getStatusCode() == 200){
                //使用工具将响应实体字符串化
                String content = EntityUtils.toString(response.getEntity());
                System.out.println("content:
    " + content);
            }
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != response) {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (null != httpClient) {
                        try {
                            httpClient.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    
    //get请求带参数
    @Test
    public void doGetWithParam() {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
    
        try {
            //定义请求的参数,使用 URIBuilder 工具创建
            URI uri = new URIBuilder("http://www.baidu.com/s").setParameter("wd", "你好世界").build();
            System.out.println("URI: " + uri);
            //创建请求对象
            HttpGet httpGet = new HttpGet(uri);
            //execute
            response = httpClient.execute(httpGet);
            //判断响应状态
            if (response.getStatusLine().getStatusCode() == 200) {
                System.out.println(EntityUtils.toString(response.getEntity()));
            }
    
        } catch (IOException e) {
            e.printStackTrace();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        } finally {
            if (null != response) {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (null != httpClient) {
                        try {
                            httpClient.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    
    //POST 请求
    @Test
    public void post() {
        //创建 HttpClient 对象
        //CloseableHttpClient httpClient = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).build();
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //创建 post 请求
        HttpPost httpPost = new HttpPost("http://localhost:8080/item/536563");
    
        CloseableHttpResponse response = null;
    
        try {
            //执行请求
            response = httpClient.execute(httpPost);
            System.out.println(response.getStatusLine());
            if (response.getStatusLine().getStatusCode() == 200) {
                System.out.println(EntityUtils.toString(response.getEntity()));
            }
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != response) {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (null != httpClient) {
                        try {
                            httpClient.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    
    //执行 post 请求带参数
    @Test
    public void postWithParam() throws IOException {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost("http://localhost:8080/item/cat/list");
        //可以设置多个参数
        ArrayList<NameValuePair> parameters = new ArrayList<>();
        parameters.add(new BasicNameValuePair("id", "2"));
        //将参数集合封装成请求实体
        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parameters);
        //将请求实体放入请求中
        httpPost.setEntity(entity);
        //执行请求
        CloseableHttpResponse response = httpClient.execute(httpPost);
    
        //输出结果
        System.out.println(response.getStatusLine());
        if (response.getStatusLine().getStatusCode() == 200) {
            System.out.println(EntityUtils.toString(response.getEntity()));
        }
        //关闭资源
        if (null != response) {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (null != httpClient) {
                    try {
                        httpClient.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    常见问题和解决方案

    请求参数乱码

    设置请求的编码格式:

    obj.addHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8");
    

    响应HTTP/1.1 403 Forbidden

    网站设置了反爬虫机制,禁止非法访问,可以通过伪装成浏览器解决。

    obj.addHeader("User-Agent"," Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537. 36 (KHTML, like Gecko) Chrome/31.0.1650.63")
    
  • 相关阅读:
    asp.net HC框架
    Jenkins之构建触发器配置(转载)
    Mac系统
    Macaca命令行
    Appnium学习日记四(Calculator示例)
    Appnium学习日记三(安装Android模拟器)
    Appnium学习日记二(安装Android开发环境)
    Appnium学习日记一(下载安装Appnium)
    Jmeter的安装
    WCF返回JSON的详细配置
  • 原文地址:https://www.cnblogs.com/carlosouyang/p/11434962.html
Copyright © 2011-2022 走看看