代理模式
代理模式:由于一个对象不能直接引用另一个对象,所以需要通过代理对象在这两个对象之间起到中介的作用。
需求1:用户上传图片的请求路径与网站不是同一个服务器(域),导致跨域问题。
//当前域 www.aaa.com
$.ajax({
url:'http://www.bbb.com/upload',
success(res){
// 由于跨域无法获取返回的数据
}
})
// 浏览器控制台报错(由于跨域产生的错误)
问题:由于浏览器的同源策略的限制,JavaScript不允许跨域访问。
优化:使用代理模式。
运用例子
1、站长统计
通过img之类的标签通过src属性可以向其它域下的服务器发送请求,不过由于是get请求且是单向,所以不会有响应数据。
// 统计代理
const Count = (function(){
// 缓存图片(参考备忘录模式)
const _img = new Image()
//返回统计函数
return function(param){
//统计请求字符串
const str = 'http://www.count.com/aa.gif'
//拼接请求字符串
for(let i in param){
str += i + '=' + param[i]
}
//发送统计请求
_img.src=str
}
})();
// 测试用例,统计num
Count({num:10})
2、jsonp
通过script标签,使用src属性事情get请求,在src指向的url(请求地址)上面添加一些字段信息,服务器获取这些字段,再相应地生成一份内容。
//前端浏览器页面
<script type="text/JavaScript">
//回调函数,打印请求数据与响应数据
function jsonpCallBack(res,req){
console.log(res,req)
}
</script>
<script type="text/JavaScript" src="http://localhost/test/jsonp.php?callback=jsonpCallBack&data=getJsonPData"></script>
//另外一个域的服务器请求接口
<?php
//后端获取请求字段数据,并生成返回内容
$data - $_GET['data'];
$callback = $_GET['callback']
echo $callbacl."('success','".$data."')"
?>
代理模板方案:
具体思路:
不同域之间相互调用对方的页面是有限制的,
那么自己域中的两个页面相互之间的调用是可以的,
即代理页面B调用被代理页面A中对象的方式是可以的,
那么,要实现这种方式我们只需要在被访问的域中,
请求返回的Header重定向到代理页面中,
并在代理页面中处理被代理的页面A即可。
具体实施:
将自己的域成为X域,另外的域称为Y域,
X域中要有一个被代理页面,即A页面。
在A页面中应具备三个部分:
- 发送请求的模块,如form表单提交,负责向Y域发送请求,并提供额外两组数据。
其一是要执行的回调函数名称。
其二是X域中代理模板所在的路径,并将target模板指向内联框架。 - 一个内联框框,如iframe,负责提供第一个部分中form表单响应目标target的指向,并抢嵌入到X域中的代理页面作为子页面,即B页面。
- 一个回调函数,负责处理返回的数据。
// X域中被代理的页面A
<script type="text/JavaScript">
function callback(data){
console.log('成功接收数据',data)
}
</script>
<iframe name="proxyIframe" id="proxyIframe"></iframe>
<form action="http://localhost/test/proxy" method="post" target="proxyIframe">
<input type="text" name="callback" value="callback">
<input type="text" name="proxy" value="http://localhost:8080/proxy.html">
<input type="submit" value="提交">
</form>
其次在X域中也要有一个代理页面,主要负责处理页面URL中searcher部分的数据解析出来(例如http://www.a.com?type=1&title=hello中的?type=1&title=hello),并将数据重新组装好,调用A页面的回调函数,将组装好的数据作为参数传入父页面中定以的回调函数中并执行。
// X域中代理页面B
<script type="text/JavaScript">
// 页面加载后执行
window.onload = function(){
// 如果不在A页面中返回,不执行
if(top == self) return
// 获取并解析searcher中的数据
const arr = localtion.search.substr(1).split('&')
//预定以函数名称以及参数集
let fn,args;
for(let i=0,len = arr.length,item;i<len;i++){
//解析search中的每组数据
item = arr[i].split('=')
//判断是否为回调函数
if(item[0]=='callback'){
//设置回调函数
fn = item[1]
}else if(item[0] = 'arg'){//判断是否是参数集
//设置参数集
args = item[1]
}
}
try{
//执行A页面中预设的回调函数
eval('top.' + fn +'("'+ args +'")');
}catch(e)}{}
}
</script>
最后Y域中的被请求的接口文件C,它的主要工作是将从X域过来的请求的数据解析并获取回调函数字段与代理模板路径字段数据,并打包返回,并将自己的Header重定向为X域的代理模板B所在的路径。
<?php
$proxy = $_POST["proxy"];
$callback = $_POST["callback"]
header("location":".$proxy"?callback=".$callback"&arg=success);
>
测试结果
控制台输出是
成功接收数据 success
代理模式可以解决系统自己的耦合度以及系统资源开销大的问题,通过代理对象和保护被代理对象,使被代理对象的扩展性不受外界的影响。也可通过代理对象解决某一交互或者某一需求中造成的大量系统开销。
当然,物流代理模式在处理系统、对象之间的耦合度问题还是在解决系统资源开销问题,都将构建出一个辅助的代理对象,增加系统的辅助的辅助度,同时也增加了一定的系统开销,但是这种开销往往是可接受的。