jQuery.ajax方法获取不到跨域的响应头?

//jQuery.ajax,complete和always都获取不到
               $.ajax({
                    url: 'http://localhost:9733/Home/AjaxIndex',
                    type: 'get',
                    dataType: 'script',
                    complete: function (jqXHR) {
                        console.log(jqXHR.getAllResponseHeaders())
                        console.log('=================================')
                    }
                })
                .always(function (d, s, jqXHR) {
                    console.log(jqXHR.getAllResponseHeaders())
                    console.log('=================================')
                })
 
//原生XMLHttpRequest
                var xhr = new XMLHttpRequest()
                xhr.open('get', 'http://localhost:9733/Home/AjaxIndex')
                xhr.onload = function () {
                    console.log('load:', xhr.getAllResponseHeaders())
                    console.log('=================================')
                }
                xhr.send()
                

结果(懒得截图,直接复制了):

=================================

=================================
load: Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Content-Type: text/html; charset=utf-8
Cache-Control: private

=================================

原因是ajax获取不到跨域的响应头吗?查看了下文档,好像没有详细的说明
但是感觉很不合理啊,原生的XMLHttpRequest都能获取到,为什么ajax封装之后获取不到
如果说我要获取跨域的响应头,只能用原生的XMLHttpRequest来做了?

阅读 12.5k
7 个回答

被人一句话点醒了
jQuery.ajax的跨域是使用<script>来实现(不论dataType是script还是jsonp,但是同过json的部分跨域是可以获取到响应头的),不使用XMLHttpRequest,所以自然获取不到响应头,服务器也不需要设置CORS相关的头部信息
元素的XMLHttpRequest能获取到响应头,但是需要服务器设置相关的CORS头部信息
其实就是jsonp和CORS的区别
让我产生疑惑的是跨域的时候jqXHR对象竟然保留了getResponseHeader这种无用的方法,既然jqXHR是内部封装的,为什么不在跨域的时候简化jqXHR,不然也容易给人以误解
如果有对jQuery源码研究比较清楚的dalao,还望指点,感激不尽

我也遇到这种情况,不过,我使用的是java作为服务器语言。

因为在使用跨域的方式进行前后端的交互时,浏览器只会使用默认的header,如图所示:

图片描述

因为,我们需要在服务端开放权限,允许客户端使用服务端自定义的header值,即使用该字段Access-Control-Expose-Headers控制权限,如代码所示:

      /**
     * 在业务处理器处理请求之前被调用 如果返回false
     * 从当前的拦截器往回执行所有拦截器的afterCompletion(),
     * 再退出拦截器链, 如果返回true 执行下一个拦截器,
     * 直到所有的拦截器都执行完毕 再执行被拦截的Controller
     * 然后进入拦截器链,
     * 从最后一个拦截器往回执行所有的postHandle()
     * 接着再从最后一个拦截器往回执行所有的afterCompletion()
     *
     * @param request
     * @param response
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
//        允许跨域
        response.setHeader(HEADER_ACCESS_CONTROL_ALLOW_ORIGIN.key(), request.getHeader(HEADER_ACCESS_CONTROL_ALLOW_ORIGIN.value()));
        response.setHeader(HEADER_ACCESS_CONTROL_ALLOW_METHODS.key(), HEADER_ACCESS_CONTROL_ALLOW_METHODS.value());
        response.setHeader(HEADER_ACCESS_CONTROL_MAX_AGE.key(), HEADER_ACCESS_CONTROL_MAX_AGE.value());
        response.setHeader(HEADER_ACCESS_CONTROL_ALLOW_HEADERS.key(), HEADER_ACCESS_CONTROL_ALLOW_HEADERS.value());
//        允许session
        response.setHeader(HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS.key(), HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS.value());
//      允许客户端在header中使用哪些数值
        response.setHeader(ACCESS_CONTROL_EXPOSE_HEADERS.key(), ACCESS_CONTROL_EXPOSE_HEADERS.value());
        return true;
    }

枚举类

public enum HeaderEnum {

    //    解决跨域的问题
    HEADER_ACCESS_CONTROL_ALLOW_ORIGIN("Access-Control-Allow-Origin", "Origin"),
    HEADER_ACCESS_CONTROL_ALLOW_METHODS("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH"),
    HEADER_ACCESS_CONTROL_MAX_AGE("Access-Control-Max-Age", "3600"),
    HEADER_ACCESS_CONTROL_ALLOW_HEADERS("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"),
    //        解决session的问题
    HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS("Access-Control-Allow-Credentials", "true"),
    ACCESS_CONTROL_EXPOSE_HEADERS("Access-Control-Expose-Headers", "name,session,userId");

    HeaderEnum(String key, String value) {
        this.key = key;
        this.value = value;
    }

    /**
     * 响应key值
     */
    private String key;

    /**
     * 响应value值
     */
    private String value;

    public String key() {
        return key;
    }

    public String value() {
        return value;
    }

}

这样客户端通过 xhr.getResponseHeaders(key) 获取响应的key值

 $.ajax({
            'type': 'head',
            'url': constant.server_path + "/index",
            'success': function (data, status, xhr) {
                var header = {
                    //登录用户名
                    "name": xhr.getResponseHeader('name'),
                    // 登录session
                    "session": xhr.getResponseHeader('session'),
                    // 用户编号
                    "userId": xhr.getResponseHeader('userId'),
                };
                console.log("name:" + header.name + ",session:" + header.session + ",userId:" + header.userId);
                console.log(xhr.getAllResponseHeaders())
            },
            'error': function (data, status, xhr) {

            }
        })

ajax和浏览器作为客户端,只能根据服务器的响应作出对应的处理

不是你获取不到跨域的响应头
而是你请求的服务器链接禁止跨域或者没有设置允许跨域的头

排查方法
1.用浏览器自带的调试器点击network
2.筛选:xhr
3.点击url信息
4.查看Response Headers 看看有没有如下headers

Access-Control-Request-Headers:x-requested-with
Access-Control-Request-Method:GET,POST
  • 跨域请求一般会先发送信使请求OPTIONS,如果信使请求访问被服务器拒绝,则不会发送后面的请求

  • 看了下你的头部,Access-Control-Allow-Methods: GET, POST, PUT, DELETE中,没有OPTIONS方法,建议加上OPTIONS方法再试一下

这种的应该后台做跨域处理

可以用来传输跨域cookie
分别设置

crossDomain: true,
xhrFields: {
    withCredentials: true
}
$.ajax({
    type: "post",
    url: 'xxxxx',
    dataType: 'json',
    crossDomain: true,
    xhrFields: {
        withCredentials: true
    },
    data: data,
    success: function(data, status, xhr) {
        //do something
    },
    error: function(jqXHR, textStatus, errorThrown) {
        console.log(jqXHR, textStatus, errorThrown);
    }
});

根本不能跨域,服务端如果没有设置允许跨域你根本获取不到数据

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进