ajax及js跨域解决方案

最近做的一个东东需要使用html5上传文件到后端,在本域中根本木有什么问题,但处理文件上上传的后端是在子域名下的,我们知道浏览器因为安全的问题是不允许这个操作的。

那么有没有什么方法能够解决这个问题呢?方法当然是有的,但都不够完美。本处只介绍一种方案,此方案恰能解决我们这个提交上传请求到子域的问题。让我们从头开始吧,下面的代码包含许多的细节,可做为html5上传组件编写的参考:

第一步:创建XMLHttpRequest用于处理上传请求,参考下面的代码:

var xhr = new XMLHttpRequest();

需要注意的是,处理跨域请求返回值时有些特别的地方,见代码:

xhr.onreadystatechange = function() {
   if (xhr.readyState == 4) {
       if (xhr.status == 200) {
            callback(xhr.responseText);
        }
    }
};

如果我们要显示进度的话,需要使用XMLHttpRequest的upload,我们看一下如何使用这个吧,还是上代码:

var upload = xhr.upload;
if (upload)upload.onprogress = function(ev) {
    onProgress(ev.loaded);
} else {
    onProgress(-1)// 不支持进度
}

第二步、我们开始创建FormData,对于不支持FormData的浏览器,可以根据RFC关于Multipart的说明去自己实现一个FormData,不复杂,就是对一些无法得到二制的浏览器使用base64提交,对于支持的浏览器构建multipart的form,本文不是这个主题的,所以就点到此为止啦。

xhr.open("post", toUrl, true);
var formData = new FormData();
formData.append("FileData", formFile);
xhr.send(formData);

另一种不需要FormData上传的方法,有好多网站在用。

xhr.setRequestHeader('Content-Type', 'application/octet-stream');
xhr.setRequestHeader('Content-Disposition', 'attachment; name="FileData"; filename="' + encodeURIComent(fromfile.name) + '"');
if (xhr.sendAsBinary && fromfile.getAsBinary) xhr.sendAsBinary(fromfile.getAsBinary());
else xhr.send(fromfile);

拖文件上传的代码也不列出了,也不复杂。下面我们看服务器端如何处理这个请求。

第三步:服务器端处理请求

服务器端需要实现doOptions的方法,客户端在执行跨域请求时会发出一个option的预请求询问服务器是否能执行真正的请求,它在发起这个请求时会把当前域放到header的Origin中。我们看一下这个方法如何实现吧:

@Override
protected void doOptions(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String origin = request.getHeader("Origin");
    // 检查origin是否在允许的范围内
    ......
    // 如果在设置返回值应该是这样的
  response.reset();
    response.resetBuffer();
    response.setHeader("Access-Control-Allow-Origin", origin);
    response.setHeader("Access-control-Allow-Methods", "POST, OPTIONS");
    response.setHeader(Access-Control-Max-Age", "30");
    return;
}

OK啦,客户端得到这个方法的header后判断是不是能执行真实的请求,如果能的话就会发起这个真实的请求,例如本例,允许发出POST的请求。

客户端发起了请求后,服务器端执行完毕返回结果,一切看起来没有什么问题啦,但是,客户端此时是能接收到数据的,js和ajax却得不到这个数据。(从firebug中能看到返回数据的字节大小正好和服务器返回结果一致,却在response区看不到任何数据)这个问题显然,还是跨域滴问题,请求是不跨了,但返回结果的域还是个外域对于这个上传请求来说,于是解决方案有了,见下:

@Override
protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
    String origin = request.getHeader("Origin");
    // 判断是否允许
  .......
    response.reset();
    response.resetBuffer();
    response.setHeader("Access-Control-Allow-Origin", origin);
    // 下面可写doPost的真实方法了
    .......
}

好了,现在我们就完成了跨域提交文件到服务器上的所有代码了。

参考资料:
https://developer.mozilla.org/En/HTTP_access_control
http://www.w3.org/TR/access-control/
-EOF-


Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>