欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

分布式系统中的跨域请求问题

程序员文章站 2022-04-10 15:43:42
1 跨域请求 在构建分布式系统时,将门户系统(负责前端页面展示的控制器)和获取数据的系统(从数据库获取资料)分开。在开发过程中,会遇到跨域请求问题。 什么是跨域请求 跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。 1. 资源跳转: A链接、重定向、表单提交 2. 资源嵌入 ......

1 跨域请求

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

分布式系统中的跨域请求问题

什么是跨域请求

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

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

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

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

    分布式系统中的跨域请求问题

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

    分布式系统中的跨域请求问题

解决方式

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

  • 若使用前端 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:\n" + 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")