在学习jQuery的时候进行到AJax时,需要与服务器进行交互练习。我毫不犹豫的打开了我的Tomcat服务器运行起来,并使用普通的ajax方法访问运行中的服务器网址的时候,发现
XMLHttpRequest对象使用open和send后,在与onreadystatechange绑定的函数中检查到readyState为4,这说明请求已经完成且相应已经就绪,但是此时的status却提示为0。但是我查看W3CSchool发现这个status没有为0的说明。
下面是 XMLHttpRequest 对象的三个重要的属性:
属性 | 描述 |
---|---|
onreadystatechange | 存储函数(或函数名),每当 readyState 属性改变时,就会调用该函数。 |
readyState | 存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。
|
status | 200: "OK" 404: 未找到页面 |
为什么这个status会为0呢?我在网上查了查关于这status为0的情况。
stackoverflow上面的有关回答(原问题链接:http://stackoverflow.com/questions/2000609/jquery-ajax-status-code-0):
第一个情况:
It could be possible to get a status code of 0
if you have sent an AJAX call and a refresh of the browser was triggered before getting the AJAX response. The AJAX call will be cancelled and you will get this status.
第二个情况:
In my experience, you'll see a status of 0 when either doing cross-site scripting (where access is denied) or requesting a URL that is unreachable (typo, DNS issues, etc).
第三个情况:
Same problem here when using <button οnclick="">submit</button>. Then solved by using <input type="button" οnclick="">
这三个个大概已经能够说明了会出现status为0的情况。
大概就是这个意思:
1.我向服务器发出了ajax请求,在还没有取得响应的时候我就触发了浏览器刷新(可能是又输入了其他的URL或者认为刷新浏览器等各种情况),此时获取到的status码就为0。
2.就是访问的URL错误或者不存在,或者出现DNS问题等。
3.出现了跨域请求的问题。
4.修改方法是在原来的按钮button的type="submit"改成"button“,页面不立即跳转。(这里应该还是属于第一种刷新的情况)。
很显然,我遇到的问题并不是1、2、4。那就只能是跨域请求的问题了。
跨域请求的概念:
下面列出了一些属于跨域的情况(图片摘录于互联网):
很显然,当端口不同、域名不同、协议不同的时候都属于跨域请求了。
我查看了下载个人电脑上跑的Tomcat服务器和测试ajax用的网址是不是属于跨域了。
Tomcat:http://localhost:8080/MyJavaWebApp/hello
ajax:http://localhost:49337/jQuery/Ajax/jQueryAjax.html?_ijt=ou23hf55psn5f10ogjgg1t7j1v
很明显,因为端口号的不同,这里发生了跨域请求。
当我将ajax和tomcat放在同一个端口下测试的时候确认能够访问到的。我这里在tomcat上用一个html代码测试ajax(两个端口都是8080)或者就普通的创建了一个html代码让ajax访问(两个端口都是49337),都能访问到。
那么如何去和跨域请求呢,我找到了两种方法,简单的记录一下。
1.使用XMLHttpRequest Level 2技术,也就是可以将不同的域名的服务器发出HTTP请求,这叫做“跨域资源共享”(Cross-origin resource sharing,简称CORS)。
使用这种方法相对来说比较简单容易实现,只需要浏览器支持这个功能,让服务器允许这种跨域操作。显然,浏览器的事插不上手,当然浏览器也会支持这种方法的。那么需要考虑的就是开发者的服务器上如何才能允许这种跨域操作。
servlet代码(这里将Servlet映射的URL设置为:http://localhost:8080/MyJavaWebApp/hello1):
package com.hello;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class Hello2 extends HttpServlet {
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws ServletException, IOException {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST,GET");
String username = request.getParameter("username");
String content = request.getParameter("content");
PrintWriter out = response.getWriter();
out.println("<html><head><title>hello</title></head>");
out.println("<body>");
out.println("<b>Hello," + username + "lalala</b>");
out.println("<b>Content:" + content + "</b>");
out.print("In HelloServlet");
out.println("</body></html>");
out.close();
}
}
这里的
response.setHeader("Access-Control-Allow-Origin","*");和response.setHeader("Access-Control-Allow-Methods","POST,GET");
两行代码一个设置了允许访问的源点,也就是可以由哪里来进行访问,这里设置的“*”,就是允许所有的不同域来进行访问。如果只想指定某一个端口或者域来访问的话,可以设置为“http://localhost:49337”,这样只能是这个端口下的访问了。
另一个很明显就是设置了允许访问的方法,这里设置了GET和POST方法(有点问题就是我在这里测试的时候GET方法能够成功实现,二POST方法却依然访问不到)。
使用原生JavaScript访问(访问的就是上述servlet中的信息):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Ajax</title>
</head>
<body>
<input type="button" value="提交" οnclick="Ajax();">
<div id="resText"></div>
<script type="text/javascript">
var xmlHttpReq = null;
function Ajax() {
if(window.ActiveXObject) {
xmlHttpReq = new ActiveXObject("Microsoft.XMLHTTP");
} else if (window.XMLHttpRequest) {
xmlHttpReq = new XMLHttpRequest();
xmlHttpReq.onreadystatechange = RequestCallBack;
xmlHttpReq.open("GET","http://localhost:8080/MyJavaWebApp/hello1?username=Leafage&content=nihao",true);
xmlHttpReq.send(null);
}
}
function RequestCallBack() {
if(xmlHttpReq.readyState == 4) {
if(xmlHttpReq.status == 200) {
alert(xmlHttpReq.responseText);
}
}
}
</script>
</body>
</html>
使用jQuery中的$.ajax访问:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jQueryAjax</title>
<script type="text/javascript" src="../jquery-3.1.1.js"></script>
<!--<script type="text/javascript" src="jQueryAjax.js"></script>-->
</head>
<body>
<input type="button" id="send" value="提交">
<script type="text/javascript" >
$(function () {
$("#send").click(function () {
$.ajax({
type : "GET",
url : "http://localhost:8080/MyJavaWebApp/hello1?username=Leafage&content=nihao",
dataType : "html",
success : function(data,textStatus) {
alert(data);
alert(textStatus);
},
error : function () {
alert("Error!");
}
})
})
})
</script>
</body>
</html>
成功访问:
有关CORS技术的详解参考:
http://www.ruanyifeng.com/blog/2016/04/cors.html
https://github.com/hstarorg/HstarDoc/blob/master/%E5%89%8D%E7%AB%AF%E7%9B%B8%E5%85%B3/CORS%E8%AF%A6%E8%A7%A3.md
2.使用JSONP技术。
先说说JSONP是怎么产生的:
其实网上关于JSONP的讲解有很多,但却千篇一律,而且云里雾里,对于很多刚接触的人来讲理解起来有些困难,小可不才,试着用自己的方式来阐释一下这个问题,看看是否有帮助。
1、一个众所周知的问题,Ajax直接请求普通文件存在跨域无权限访问的问题,甭管你是静态页面、动态网页、web服务、WCF,只要是跨域请求,一律不准;
2、不过我们又发现,Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有"src"这个属性的标签都拥有跨域的能力,比如<script>、<img>、<iframe>);
3、于是可以判断,当前阶段如果想通过纯web端(ActiveX控件、服务端代理、属于未来的HTML5之Websocket等方式不算)跨域访问数据就只有一种可能,那就是在远程服务器上设法把数据装进js格式的文件里,供客户端调用和进一步处理;
4、恰巧我们已经知道有一种叫做JSON的纯字符数据格式可以简洁的描述复杂数据,更妙的是JSON还被js原生支持,所以在客户端几乎可以随心所欲的处理这种格式的数据;
5、这样子解决方案就呼之欲出了,web客户端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件(一般以JSON为后缀),显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装入进去。
6、客户端在对JSON文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了,这种获取远程数据的方式看起来非常像AJAX,但其实并不一样。
7、为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
参考:http://www.cnblogs.com/dowinning/archive/2012/04/19/json-jsonp-jquery.html
简单点就是,直接ajax访问html等资源不允许,但是访问js文件的时候却是可以通过的。而正好有一种JSON的数据格式可以传输数,所以将数据作为JSON进行传输访问。然后回调函数进行操作。
jQuery代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jQueryAjax</title>
<script type="text/javascript" src="../jquery-3.1.1.js"></script>
<!--<script type="text/javascript" src="jQueryAjax.js"></script>-->
</head>
<body>
<input type="button" id="send" value="提交">
<script type="text/javascript" >
$(function () {
$("#send").click(function () {
$.ajax({
type : "GET",
data : {"username":"Leafage","content":"nihao"},
url : "http://localhost:8080/MyJavaWebApp/hello",
dataType : "jsonp",
jsonp : "callback",
jsonpCallback : "jsonpCallback",
success : function(data,textStatus) {
alert(data);
alert(textStatus);
},
error : function () {
alert("Error!");
}
})
})
})
function jsonpCallback(data) {
alert("这里是回调的jsonpCallback函数!");
alert("接收的数据为:" + data);
}
</script>
</body>
</html>
这里使用$.ajax方法进行了ajax请求。
jsonp:传递给请求服务器页面的,用来获得需要回调的函数名字,一般默认为callback,所以这个可以省略,所以作为key。
jsonpCallback:访问成功后需要回调的函数名,作为value。
所以这里的jsonp和jsonpCallback的基本形式就是:callback=jsonpCallback,在后台代码中可以通过callback得到jsonpCallback,也就是需要回调的函数的名字。
执行成功之后,优先调用jsonpCallback函数,里面的参数就是后台传过来的数据,可以在里面处理。然后执行success里面的内容。(注意:这里需要回调的函数不要写在ajax的相关函数里面去,否则无法执行,这里不能写在$(function(){})函数里面,写在外面就能调用。)
Servlet代码:
package mypack;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class HelloServlet extends HttpServlet {
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("username");
String content = request.getParameter("content");
String callbackName = request.getParameter("callback");
String returnString = callbackName + "('" + username + content + "')";
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.print(returnString);
out.close();
}
}
后台在根据callback得到需要回调的函数之后,将相关数据输入传入其中即可。
测试成功:
这里的CORS和JSONP都只是简单的实现而已,还有其他具体的差异和扩展内容。
当然跨域的方法也有其他的。
这里的内容仅做参考。