如果服务端部署在 foo.com 域名下,而客户端部署在 bar.com 域名下,此时从 bar.com 发出一个 AJAX 的请求到 foo.com,就会出现报错:

No 'Access-Control-Allow-Origin' header is present on the requested resource.
或者
Permission denied to call method XMLHttpRequest.open
为什么会出现以上错误呢?

这是因为所有支持Javascript的浏览器都会使用同源策略这个安全策略。

同源策略:

同源策略,它是由netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。当一个浏览器的两个tab页中分别打开来 百度和谷歌的页面当一个百度浏览器执行一个脚本的时候会检查这个脚本是属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。

跨域一般分为两种:

  1. 同域名不同端口
  2. 不同域名

下面我们来演示一下最简单的跨域:同域名不同端口

第一步:创建两个不同的项目(本例在eclipse中创建),将其部署在tomcat服务器中。

将两个项目部署在不同的端口上,需要做一下操作:

(1)、修改tomcat配置文件server.xml

修改前配置文件内容:

<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
  
  <Listener SSLEngine="on" className="org.apache.catalina.core.AprLifecycleListener"/>
  <Listener className="org.apache.catalina.core.JasperListener"/>
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"/>
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/>
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"/>
  <GlobalNamingResources>
    <Resource auth="Container" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" name="UserDatabase" pathname="conf/tomcat-users.xml" type="org.apache.catalina.UserDatabase"/>
  </GlobalNamingResources>

  <Service name="Catalina">

    <Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/>

    <Engine defaultHost="localhost" name="Catalina">
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
      </Realm>

      <Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true">
        </Host>

    </Engine>
  </Service>
</Server>
修改后配置文件内容,增加另外一个service标签(配置几个项目,可以增加几个service标签)

<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
  
  <Listener SSLEngine="on" className="org.apache.catalina.core.AprLifecycleListener"/>
  <Listener className="org.apache.catalina.core.JasperListener"/>
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"/>
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/>
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"/>
  <GlobalNamingResources>
    <Resource auth="Container" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" name="UserDatabase" pathname="conf/tomcat-users.xml" type="org.apache.catalina.UserDatabase"/>
  </GlobalNamingResources>

  <Service name="Catalina">

    <Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/>

    <Engine defaultHost="localhost" name="Catalina">
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
      </Realm>

      <Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true">
        </Host>

    </Engine>
  </Service>
  <Service name="Catalina1">
    <Connector connectionTimeout="20000" port="8090" protocol="HTTP/1.1" redirectPort="8443"/>
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/>
    <Engine defaultHost="localhost" name="Catalina1">

      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
      </Realm>

      <Host appBase="webapps1" autoDeploy="true" name="localhost" unpackWARs="true">
        </Host>

    </Engine>
  </Service>

</Server>
注意:service name,第一个connector port,engine name,host appBase的配置。

(2)、创建新的部署相关目录

在tomcat根目录下,创建host appBase所指定的目录(webapps1)。

在tomcat根目录下,conf目录下,创建engine name所指定的目录(Catalina1/localhost)

(3)、将需要部署的项目复制到host appBase所指定的目录(webapps1)下。

(4)、启动tomcat,并访问如下路径

由于第一个项目部署在8080端口,因此访问:http://localhost:8080/ajaxTest/index.jsp

第二个项目部署在8090端口,访问:http://localhost:8090/ajaxTest2/index.jsp

第二步、使第一个项目(ajaxTest)访问第二个项目(ajaxTest2)

在ajaxTest2项目中创建test.js文件,内容如下:
$(function(){
	alert("呵呵呵");
});
文件目录如下:

在ajaxTest项目中,创建index.jsp,文件中引用ajaxTest2的js文件,文件内容如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<Meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="http://localhost:8080/ajaxTest/script/jquery.js"></script>
</head>
<body>
<script type="text/javascript" src="http://localhost:8090/ajaxTest2/script/test.js"></script>
<p>
	哈哈哈
</p>
</body>
</html>
最后,启动tomcat,访问ajaxTest项目:http://localhost:8080/ajaxTest/index.jsp,页面显示结果如下:

这就是一个简单的跨域访问(跨相同域名,不同的端口),那么为什么能够访问呢?
因为像拥有src属性的标签(img,script,iframe)等等,是没有同源策略限制的,因此是可以访问的。

解决方案:使用JSONP解决AJAX跨域问题

首先我们来看一下什么是JSONP呢?
JSONP是JSON with Padding的略称。它是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式)。

JSONP解决使用GET请求提交数据的跨域问题,因此,如果使用POST请求提交数据,那么就要使用下一篇讲的CORS。

我们使用Jquery的ajax请求,来实现ajax跨域访问,来看如下代码:
是ajaxTest项目访问ajaxTest2项目中的servlet。
<script type="text/javascript">
	$(function(){
		var url = "http://localhost:8090/ajaxTest2/test";
		$.ajax({
			url:url,dataType:"jsonp",processData:false,type:"get",success:function(data){
				alert(data.name);
			},error:function(XMLHttpRequest,textStatus,errorThrown){
				alert(XMLHttpRequest.status);
				alert(XMLHttpRequest.readyState);
				alert(textStatus);
			}
			
		});
	});
</script>
两个项目中的servlet是相同的,ajaxTest调用本项目中的servlet是正常显示数据,当调用ajaxTest2的servlet时,状态显示“200”,表示服务器执行成功,并返回数据,但是客户端提示“parseerror”的错误,即为解析错误。
原来,json与jsonp的格式是存在差异的。
json格式数据:
{"id":1,"name":"小强","age":23}
jsonp格式数据:
callback({{"id":1,"age":23}})
因此,当你在前端设置为jsonp,服务器传送json格式的数据,因此出现解析错误。
接下来,我们修改服务器代码,使其返回jsonp格式数据:
@Override
	public void doPost(HttpServletRequest req,HttpServletResponse resp)
			throws servletexception,IOException {
		
		Person person = new Person();
		person.setId(1L);
		person.setName("小强");
		person.setAge(23);
		
		Gson gson = new Gson();
		String json = gson.toJson(person);
		
		String callback = req.getParameter("callback");
		
		System.out.println(callback);
		
		resp.setCharacterEncoding("UTF-8");
		resp.setContentType("text/html");
		resp.getWriter().write(callback+"("+json+")");
	}
在Jquery中,客户端传送到服务器的callback函数名每次请求都是不同的,形如
jQuery191012974677915190092_1480570734367
jQuery191042678534055955564_1480571648774
因此,我们首先需要通过执行request.getParameter("callback")获得callback函数名,然后将数据转化成json格式,最后将数据通过callback函数封装,发送给客户端,这样客户端才能够正确的解析。

Jquery插件介绍:jquery-jsonp ,下载地址:https://github.com/jaubourg/jquery-jsonp

在之前的例子基础之上,将客户端引用json插件,然后修改调用代码如下:
$(function(){
		var url = "http://localhost:8090/ajaxTest2/test";
		$.jsonp({
			url:url,callbackParameter:"callback",success:function(data,textStatus){
				alert(data.name);
				alert(textStatus);
			},error:function(msg,textStatus){
				alert(msg);
				alert(textStatus);
			}
		});
	});
必须要指定callbackParameter,值可以随便取,在这儿设置的名字,仅仅是用来服务器端调用request.getParameter("callback")取回调函数的值,具体值的设置由插件内部进行设置,在我的测试过程中为:_jqjsp
data为返回的json格式数据,textStatus如果是成功值为success。

关于Ajax跨域问题之JSONP的更多相关文章

  1. ios – Realm:无法使用类型'(Object.Type)’的参数列表调用’对象’

    所以我在Realm中有一个非常简单的Book模型而我正在尝试在帮助程序类中检索所有书籍:这一行:抛出标题中的错误.我疯了还是这不是Realm文档告诉我们要做的?解决方法realm.objects()不会返回[Book]但会返回结果?.所以你必须改变userBookLibrary的类型:

  2. ios – Swift 2.0中的动态可选属性

    我已经看过这篇文章OptionaldynamicpropertiesinSwift,但我不想在NSObject中包装该类.这只是关于Realm数据库我没有nil属性,但我认为这是一个很好的方式来建模我的数据库.在可以在https://realm.io/docs/swift/latest/中找到的Realm文档中,它表示支持选项.这是我的码这是我的错误我知道这是与上面的帖子相同的代码和错误,但我很好

  3. ios – 在后台线程中写入Realm后,主线程看不到更新的数据

    >清除数据库.>进行API调用以获取新数据.>将从API检索到的数据写入后台线程中的数据库中.>从主线程上的数据库中读取数据并渲染UI.在步骤4中,数据应该是最新数据,但我们没有看到任何数据.解决方法具有runloops的线程上的Realm实例,例如主线程,updatetothelatestversionofthedataintheRealmfile,因为通知被发布到其线程的runloop.在后台

  4. ios – 尝试向我们分配IP而不是localhost或home时,NSURLSession失败

    我有一台本地运行的服务器(我的IP是192.168.0.98),并且已经尝试使用一些网络代码来访问它.最初这是通过AFNetworking完成的,但我现在用这样的NSURLSession完成了它:然后我用这3个URL运行它:>http://localhost:8080/api–>作品.>http://127.0.0.1:8080/api–>作品.>http://192.168.0.98:8080/

  5. ios – RxSwift:返回一个带有错误的新observable

    我有一个函数返回BoolObservable,具体取决于它是否正常.解决方法返回包含单个元素的可观察序列.相反,你应该使用这样的东西:Create方法从指定的subscribe方法实现创建一个可观察的序列.在你的情况下:Anonymousdisposable是在您想要中断的情况下调用的操作.假设您离开视图控制器或应用程序需要完成该服务,您不再需要再调用此请求.它非常适合视频上传或更大的内容.您可以

  6. ios – 领域:如何获取数据库的当前大小

    是否有RealmAPI方法使用RealmSwift作为数据存储来获取我的RealmSwift应用程序的当前数据库大小?

  7. ios – 如何绑定Realm对象的更改?

    在我的项目中,我试图通过MVVM工作,所以在.h文件中的VM中在.m文件中GPCity是一个RLMObject子类如何通过ReactiveCocoa绑定此?

  8. ios – Realm在更新多个对象时变慢

    在我的应用中,用户可以在集合视图中选择多个联系人.当他选择属性“isSelected”时,我将设置为true,并且collectionview刷新所选单元格.在这里,我可以识别选择和单元格突出显示之间的小延迟.但是在下一步中,我创建了一个包含所选联系人的组,最后我将属性“isSelected”设置为false.这需要50个对象(5秒)的不可接受的时间量并且需要调整.这是我取消选择所有选定联系人的代码:是否可以立即执行批量更新?解决方法尝试将for循环放在write块中:

  9. 你如何压缩iOS上的Realm DB?

    我想定期在iOS上压缩一个Realm实例来恢复空间.我认为该过程是将数据库复制到临时位置,然后将其复制回来并使用新的default.realm文件.我的问题是Realm()就像一个单例并且回收对象,所以我无法真正关闭它并告诉它打开新的default.realm文件.这里的文档(https://realm.io/docs/objc/latest/api/Classes/RLMRealm.html)建

  10. ios – Realm – 无法使用现有主键值创建对象

    我有一个对象有许多狗的人.应用程序有单独的页面,它只显示狗和其他页面显示人的狗我的模型如下我有人存储在Realm中.人有详细页面,我们取,并显示他的狗.如果狗已经存在,我会更新该狗的最新信息并将其添加到人的狗列表中,否则创建新狗,保存并将其添加到人员列表中.这适用于coredata.在尝试用他的狗更新人时,领域会抛出异常无法使用现有主键值创建对象解决方法这里的问题是,即使你正在创建一个全新的Rea

随机推荐

  1. xe-ajax-mock 前端虚拟服务

    最新版本见Github,点击查看历史版本基于XEAjax扩展的Mock虚拟服务插件;对于前后端分离的开发模式,ajax+mock使前端不再依赖后端接口开发效率更高。CDN使用script方式安装,XEAjaxMock会定义为全局变量生产环境请使用xe-ajax-mock.min.js,更小的压缩版本,可以带来更快的速度体验。

  2. vue 使用 xe-ajax

    安装完成后自动挂载在vue实例this.$ajaxCDN安装使用script方式安装,VXEAjax会定义为全局变量生产环境请使用vxe-ajax.min.js,更小的压缩版本,可以带来更快的速度体验。cdnjs获取最新版本点击浏览已发布的所有npm包源码unpkg获取最新版本点击浏览已发布的所有npm包源码AMD安装require.js安装示例ES6Module安装通过Vue.use()来全局安装示例./Home.vue

  3. AJAX POST数据中文乱码解决

    前端使用encodeURI进行编码后台java.net.URLDecoder进行解码编解码工具

  4. Koa2框架利用CORS完成跨域ajax请求

    实现跨域ajax请求的方式有很多,其中一个是利用CORS,而这个方法关键是在服务器端进行配置。本文仅对能够完成正常跨域ajax响应的,最基本的配置进行说明。这样OPTIONS请求就能够通过了。至此为止,相当于仅仅完成了预检,还没发送真正的请求呢。

  5. form提交时,ajax上传文件并更新到&lt;input&gt;中的value字段

  6. ajax的cache作用

    filePath="+escape;},error:{alert;}});解决方案:1.加cache:false2.url加随机数正常代码:网上高人解读:cache的作用就是第一次请求完毕之后,如果再次去请求,可以直接从缓存里面读取而不是再到服务器端读取。

  7. 浅谈ajax上传文件属性contentType = false

    默认值为contentType="application/x-www-form-urlencoded".在默认情况下,内容编码类型满足大多数情况。在这里,我们主要谈谈contentType=false.在使用ajax上传文件时:在其中先封装了一个formData对象,然后使用post方法将文件传给服务器。说到这,我们发现在JQueryajax()方法中我们使contentType=false,这不是冲突了吗?这就是因为当我们在form标签中设置了enctype=“multipart/form-data”,

  8. 909422229_ajaxFileUpload上传文件

    ajaxFileUpload.js很多同名的,因为做出来一个很容易。我上github搜AjaxFileUpload出来很多类似js。ajaxFileUpload是一个异步上传文件的jQuery插件传一个不知道什么版本的上来,以后不用到处找了。语法:$.ajaxFileUploadoptions参数说明:1、url上传处理程序地址。2,fileElementId需要上传的文件域的ID,即的ID。3,secureuri是否启用安全提交,默认为false。4,dataType服务器返回的数据类型。6,error

  9. AJAX-Cache:一款好用的Ajax缓存插件

    原文链接AJAX-Cache是什么Ajax是前端开发必不可少的数据获取手段,在频繁的异步请求业务中,我们往往需要利用“缓存”提升界面响应速度,减少网络资源占用。AJAX-Cache是一款jQuery缓存插件,可以为$.ajax()方法扩展缓存功能。

  10. jsf – Ajax update/render在已渲染属性的组件上不起作用

    我试图ajax更新一个有条件渲染的组件。我可以确保#{user}实际上是可用的。这是怎么引起的,我该如何解决呢?必须始终在ajax可以重新呈现之前呈现组件。Ajax正在使用JavaScriptdocument.getElementById()来查找需要更新的组件。但是如果JSF没有将组件放在第一位,那么JavaScript找不到要更新的内容。解决方案是简单地引用总是渲染的父组件。

返回
顶部