Ajax跨域和JSONP

    这几天做一个服务,主要是提供api给别人调用,首先我在自己的机器上调通了接口,然后把api给了他们,自然地像 http://localhost:8081/bizcenter/servlet/fileUpload 的url,要把localhost改成我的ip,比如 http://172.16.30.61:8081/bizcenter/servlet/fileUpload,结果就出现了“跨域”的问题。噢,第一次这么直接地面对跨域~换成了127.0.0.1,firefox也提示

已拦截跨源请求:同源策略禁止读取位于 http://127.0.0.1:8081/bizcenter/servlet/checkNotdonebiz 的远程资源。(原因:CORS 头缺少 'Access-Control-Allow-Origin')。
  
    说到这里,我决定先理解清楚localhost和127.0.0.1之间的区别。以前只知道两个是等价的可互换,具体区别说不出。
localhost也叫local ,正确的解释是:本地服务器
127.0.0.1在windows等系统的正确解释是:本机地址(本机服务器)
他们的解析通过本机的host文件,windows自动将localhost解析为127.0.0.1
localhot(local)是不经网卡传输!这点很重要,它不受网络防火墙和网卡相关的的限制。
127.0.0.1是通过网卡传输,依赖网卡,并受到网络防火墙和网卡相关的限制。
一般设置程序时本地服务用localhost是最好的,localhost不会解析成ip,也不会占用网卡、网络资源。
有时候用localhost可以,但用127.0.0.1就不可以的情况就是在于此。猜想localhost访问时,系统带的本机当前用户的权限去访问,而用ip的时候,等于本机是通过网络再去访问本机,可能涉及到网络用户的权限。

所以,%System32%/driver/etc/host中增加一条 127.0.0.1  localhost,就可以作个映射。

说完了localhost和127.0.0.1,再回来跨域。什么时候会出现这个情况呢
借一下网上的图,JavaScript同源策略的限制,a.com 域名下的js无法操作b.com或是c.a.com域名下的对象,同一域名下叫同源。所以你自己的web项目中所有的ajax请求本应用的所有接口,都是同源。如果ajax其他域名上的接口,那就不行了。



说说一些解决办法吧。前端和后台都有办法。
1、后台代理
既然直接用ajax去访问不同域的资源行不通,但是后台代码用Http工具类访问任何Url都是正常的,可以写一个proxy(如servlet),把要访问的跨域url用Ajax方法提交给本应用的proxy,然后proxy中再用HttpUrlConnection或HttpClient模拟http去访问跨域的url,获取到结果后再write回去。

public class ProxyServlet extends HttpServlet{

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doPost(req,resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String text = IOUtils.toString(req.getInputStream(), "utf-8");
		//resp.setHeader("Access-Control-Allow-Origin", "http://172.16.30.163:8088");
		String url= req.getParameter("proxy_url");
		String res = post(<span class="errorMessage "></span>url,text);
		resp.getWriter().write(res);
	}
	private String post(String url,String data){
		HttpPost post = new HttpPost(url);
		post.setHeader("Content-Type", "text/html;charset=UTF-8");
		String responseMsg = null;
		try {
			DefaultHttpClient httpclient = new DefaultHttpClient();
			ByteArrayEntity e = new ByteArrayEntity(data.getBytes("UTF-8"));
			post.setEntity(e);
			HttpResponse r = httpclient.execute(post);
			HttpEntity entity = r.getEntity();
			if (r.getStatusLine().getStatusCode() == 200) {
				responseMsg = EntityUtils.toString(entity, "UTF-8");
			} else {
				System.out.println("访问出错:" + url);
			}
		} catch (Exception e) {
		} finally {
			post.releaseConnection();
		}
		return responseMsg;
	}
}
这种办法还不错,不过会加重后台的负担,因为每次都要多一次http请求。


2、JSONP
JSONP(JSON with Padding)是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式)。
这是因为众所周知的<script>标签可以加载任何 url 上的资源,我们经常在<head></head>中用script引入各种js,或是本地的,或是远程的。如百度库的jquery
<script typet="text/javascript" src="http://libs.baidu.com/jquery/1.9.1/jquery.min.js"></script>。

我们做个简单的演示。有一个远程url--> http://localhost:8080/pro1/a.action,上面的资源是一个json串{"name":"我来自pro1"}。我还有一个应用http://localhost:8081/pro2/b.html,从b.html到a.action是跨域的。 在b.html中构建并执行一段js:
function CreateScript() {
    var src = http://localhost:8080/pro1/a.action;
   $("<script><//script>").attr("src", src).appendTo("body");
}
在控制台可以看见报js语法错误了。原来用script标签加载完后,会立即把响应结果当js语句去执行,很明显 {"name":"我来自pro1"} 不是合法的js语句啊。但如果是返回的是jsonpcallback({"name":"我来自pro1"}),只要有jsonpcallback这个方法,就是合法的语句了。服务端怎么知道你页面上定义了什么方法啊对不对。我们可以告诉服务端嘛,http://localhost:8080/pro1/a.action?callback=jsonpcallback。这样服务端获取callback参数,在返回的时候,把JSON再包一层callback,变成jsonpcallback({"name":"我来自pro1"})就行了。
是的,没错。这就是JSONP的原理。

这是最原始的写法,谁正直会去写这么多啰嗦的代码来实现一个功能还不好维护。所以Jquery提供了Jsonp的快捷方式。
    $.ajax({ 
            url : "http://localhost:8080/pro1/a.action?callback=jsonpcallback",
            type : "post",
            data : {"cust_id":1},
            async : true,
            dataType : "jsonp",
            jsonp:"jsonpcallback",  
            //jsonpCallback:"fff",
            error : function(XMLHttpRequest, textStatus, errorThrown) {
            },
            success : function(data) {
                console.log(data);
            }
        });      
这里有两个属性要注意一下。
jsonp:指明回调函数的参数名,这个名字是要在request.getParameter()中用的。你写的jsonpcallback,那getParameter()就要用jsonpcallback。
jsonpCallback:如果不指定这个,可以看到jquery是随机建了一个方法叫做jQuery19101256806997398623_1464157164926。
http://localhost:8080/pro1/a.action?jsonpcallback=jQuery19101256806997398623_1464157164926&cust_id=1。我们可以指定方法名,这样管理请求更容易,也能方便地提供回调函数和错误处理。你也可以在想让浏览器缓存GET请求的时候,指定这个回调函数名。比如设成 jsonCallbakk:deal,就变成
http://localhost:8080/pro1/a.action?jsonpcallback=deal&cust_id=1
其底层的实现,估计是这样
 function success_jsonpCallback(data)
 {
     success(data);
 }

综上所述:

1、ajax和jsonp本质上是不同的东西。ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本并执行。

2、ajax与jsonp的区别不在于是否跨域,ajax通过服务端代理一样可以实现跨域,jsonp本身也可以做同域的数据的获取。

3、没有关于 JSONP 调用的错误处理。如果动态脚本插入有效,就执行调用;如果无效,就静默失败。失败是没有任何提示的。例如,不能从服务器捕捉到 404 错误,也不能   取消或重新开始请求。不过,等待一段时间还没有响应的话,就不用理它了。(未来的 jQuery 版本可能有终止 JSONP 请求的特性)。

4、JSONP 的另一个主要缺陷是被不信任的服务使用时会很危险。因为 JSONP 服务返回打包在函数调用中的 JSON 响应,而函数调用是由浏览器执行的,这使宿主 Web 应用程序更容易受到各类攻击。如果打算使用 JSONP 服务,了解它能造成的威胁非常重要。


3、Access-Control-Allow-Origin

这个比较简单。Access-Control-Allow-Origin是HTML5中定义的一种服务器端返回Response header,用来解决资源的跨域权限问题。只有当目标页面的response中,包含了Access-Control-Allow-Origin 这个header,并且它的值里有我们自己的域名时,浏览器才允许我们拿到它页面的数据进行下一步处理。

nginx中的配置

<!--在nginx.conf中配置-->  
http {  
  ......  
  add_header Access-Control-Allow-Origin *;  
  add_header Access-Control-Allow-Headers X-Requested-With;  
  add_header Access-Control-Allow-Methods GET,POST,OPTIONS;  
  ......  
} 
java中,response.setHeader("Access-Control-Allow-Origin", "*"); *代表一切请求的来源都可以接受。生产环境上不会这么搞,用具体的url代替,多个url用 , 隔开。




注:还有一个问题,ajax跨域上传文件 这个还没有研究好。不过有个想法用一个nginx作转发看看行不行。再见


 
一些资料参考自: JSONP详解   
                             Access-Control-Allow-Origin与跨域


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值