主要是针对非IE的CORS实现,因为IE的CORS直接使用XDomainRequest,比较简单,功能也比较简单。在此是针对非IE中如何实现跨源。
主要有两个方面:1.如何使用PreflightRequest实现功能较丰富跨域请求,功能丰富体现在:可以发送自定义头部信息;可以使用get和post以外的方法;可以发送不同类型的主体内容。摘录一段文档:
Unlike simple requests(discussed above),"preflighted" requests first send an HTTP request by the@H_502_8@OPTIONSmethod to the resource on the other domain,in order to determine whether the actual request is safe to send. Cross-site requests are preflighted like this since they may have implications to user data. In particular,a request is preflighted if:
(1)It uses methodsotherthanGET,HEAD
orPOST
. Also,ifis used to send request data with a Content-Type
application/x-www-form-urlencoded,multipart/form-data
text/plainrequest sends an XML payload to the server usingapplication/xml
text/xmlispreflighted.(2)It sets custom headers in the request (e.g. the request uses a header such asx-zd)
Note:Starting inGecko2.0,thetext/plain
,application/x-www-form-urlencoded
,andmultipart/form-data
data encodings can all be sent cross-site without preflighting. PrevIoUsly,onlytext/plain
could be sent without preflighting.
源码实例:js部分:
function crosFunc(){
var xhr=new XMLHttpRequest();
xhr.onreadystatechange=function(){
if(xhr.readyState==4){
if(xhr.status>=200&&xhr.status<=300||xhr.status==304){
console.log("--->"+xhr.getAllResponseHeaders());//为空,不能访问返回的头信息
console.log(xhr.responseText);
console.log(console.log(document.cookie));//username=444 undefined 因为同源策略的影响,访问不到从服务器返回来的cookie
}
else{
alert("cros Failed!");
}
}
}
if('withCredentials' in xhr){//检测xhr是否支持CORS,如果不支持,说明是ie,就只能使用XDomainRequest进行跨域
console.log("it is true");
}
xhr.open('get','http://202.197.66.228:8083/TestCORS/ServerServlet',true);
xhr.setRequestHeader('x-zd','zd');//发送自定义头部
xhr.send(null);
}
crosFunc();
</script>
servlet部分:注意红色部分的代码,要在doOptions中加入一些头信息,这样浏览器才会把结果交给js,否则就会出错
public class ServerServlet extends HttpServlet { public ServerServlet() { super(); } public void destroy() { super.destroy(); // Just puts "destroy" string in log // Put your code here } public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { this.execute(request,response); } public void doPost(HttpServletRequest request,response); } private void execute(HttpServletRequest request,HttpServletResponse resp){ resp.setCharacterEncoding("utf-8"); Cookie [] cs=request.getCookies(); System.out.println("cookies:"+( cs==null?0:cs.length)); Cookie c=new Cookie("tongguo","true"); resp.addCookie(c); resp.addHeader("Access-Control-Allow-Origin","http://localhost:8083");//注意此处 List<UserInfo> tempt=_getSomeInfos(); JSONObject jobj=new JSONObject(); jobj.put("rows",tempt); System.out.println(jobj); byte[] jsonBytes; try { jsonBytes = jobj.toString().getBytes("utf-8"); resp.setContentLength(jsonBytes.length); resp.getOutputStream().write(jsonBytes); resp.getOutputStream().flush(); resp.getOutputStream().close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override protected void doOptions(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException { resp.addHeader("Access-Control-Allow-Origin","http://localhost:8083"); resp.addHeader("Access-Control-Max-Age","1728000"); resp.addHeader("Access-Control-Allow-Methods","GET,POST,OPTIONS"); resp.addHeader("Access-Control-Allow-Headers","User-Agent,Origin,Cache-Control,Content-type,x-zd,Date,Server,withCredentials");
//注意此处不要再调用super.dooptions方法
} private List<UserInfo> _getSomeInfos() { List<UserInfo> result=new ArrayList<UserInfo>(); UserInfo u=null; for(int i=0;i<30;i++){ u=new UserInfo(); u.setUsername("zhang"+i); if(i%2==0){ u.setSex("Female"); u.setAddress("四川"); u.setSchool("CSU"); }else{ u.setSex("Male"); u.setAddress("湖南长沙"); u.setSchool("CWNU"); } result.add(u); } Collection nuCon = new Vector(); nuCon.add(null); result.removeAll(nuCon); return result; } }
结果:发出了两次请求。第一次是options请求,第二次是get请求。
2.是使用CORS,发送一些带凭据的跨域请求,所谓的凭据就是一些cookie,HTTP认证什么的。摘录一段文档:
The most interesting capability exposed by bothXMLHttpRequest
and Access Control is the ability to make "credentialed"requests that are cognizant of HTTPCookies and HTTPAuthentication information. By default,in cross-siteinvocations,browsers will
一般默认情况下都是不启用带有凭据的跨域,因此如果想要启用的话,只需将XMLHttpRequest的withCredentials属性设置为True,注意此处从服务器返回来的cookie信息以及响应头信息,可以通过浏览器显示出来,但是在js中不能用代码进行访问。
js代码:
<script type="text/javascript"> function crosFunc(){ var xhr=new XMLHttpRequest(); xhr.onreadystatechange=function(){ if(xhr.readyState==4){ if(xhr.status>=200&&xhr.status<=300||xhr.status==304){ console.log("--->"+xhr.getAllResponseHeaders());//为空,不能访问返回的头信息 console.log(xhr.responseText); console.log(console.log(document.cookie));//username=444 undefined 因为同源策略的影响,访问不到从服务器返回来的cookie } else{ alert("cros Failed!"); } } } if('withCredentials' in xhr){//检测xhr是否支持CORS,如果不支持,说明是ie,就只能使用XDomainRequest进行跨域 console.log("it is true"); } xhr.open('get',true); setCookie("username","444",12); xhr.setRequestHeader('x-zd','zd'); xhr.withCredentials = "true";//向服务器说明我要发送带凭据的请求 xhr.send(null); } crosFunc(); function setCookie(key,value,hour){ var _cookie=key+"="+encodeURIComponent(value); if(hour>0){ var date=new Date(); date.setTime(date.getTime()+hour*3600*1000); _cookie+=";expires="+date.toGMTString(); } document.cookie=_cookie; }
java代码,只是在doOptions和execute方法中各加入了同一行代码:
resp.addHeader("Access-Control-Allow-Credentials","true");
结果:也是发出了两个请求,options请求和上面的一样,只看get请求:
刷新该页面,可以发现,发出带有cookie的请求:
同时在控制台查看响应的头部,主体内容,和cookie信息: