Angular:$ http和$ resource忽略自定义标头.为什么?

mcv*_*mcv 9 jsonp http angularjs angularjs-resource

我正在尝试访问我无法控制的REST服务.第一个问题是该服务不包含Access-Control-Allow-Origin标头,这是一个问题,如果我理解正确,立即限制我使用JSONP.

此外,默认情况下,此服务发送XML而不是JSON,但它能够发送JSON.我认为它应该响应我的Accept标头,负责服务的人说它看着我的Content-Type.这意味着我需要做一个POST而不是一个GET(虽然当我只是获取一些静态数据时更有意义,对吗?).

像我一样顽固,我正在尝试我的Accept标头.由于Angular只接受JSON,我希望它Accept: application/json默认使用标头,但它没有,它忽略了我手动设置它的尝试:

app.config(['$httpProvider', function($httpProvider){
    console.log($httpProvider.defaults.headers.common);
    delete $httpProvider.defaults.headers.common['X-Requested-With'];
    $httpProvider.defaults.headers.post['Accept'] = 'application/json, text/javascript';
    $httpProvider.defaults.headers.post['Content-Type'] = 'application/json; charset=utf-8';
    $httpProvider.defaults.headers.post['Access-Control-Max-Age'] = '1728000';
    $httpProvider.defaults.headers.common['Access-Control-Max-Age'] = '1728000';
    $httpProvider.defaults.headers.common['Accept'] = 'application/json, text/javascript';
    $httpProvider.defaults.headers.common['Content-Type'] = 'application/json; charset=utf-8';
    $httpProvider.defaults.useXDomain = true;
}]);
Run Code Online (Sandbox Code Playgroud)

我在实际资源中再次这样做:

return $resource('http://foo.com/getStuff', {}, {
    fetch: {
        method:'JSONP',
        params: params,
        headers: {
            'Accept':'application/json, text/javascript',
            'Content-Type':'application/json; charset=utf-8'
        },
        isArray:false,
        callback: 'JSON_CALLBACK'
    }
});
Run Code Online (Sandbox Code Playgroud)

但是,请求标头仍然包含Accept: */*.

我的问题是:为什么?为什么Angular会忽略我的标题?我怎么能让它使用正确的标题呢?

而且:有没有办法在POST中使用JSONP?

编辑:最初我使用的是Angular 1.0.7,但我只是尝试了1.2.3并得到了相同的结果.标题被忽略,但每个人都声称这是实现目标的方法.

我也尝试直接使用$ http,而不是$ resource,使用相同的结果.

编辑2:这是一个JSFiddle.它是匿名的,并不使用我的真实服务器,但使用Firebug /开发人员工具,您可以验证它是否Accept: */*在两个调用上发送,尽管我尝试设置application/json标头.这是我真正的问题.在我的真实服务器上,我得到了一个XML结果,尽管我的真实服务器能够发送JSON.

(真正的服务器是否支持jsonp目前不太相关.这个虚拟服务器显然没有,但是没关系.我只关心标题.)

编辑3:我尝试了以下建议的两种解决方案:

$http.defaults.headers.common['Accept'] = 'application/json, text/javascript';

$http.defaults.transformRequest.push(function (data, headersGetter) {
    headersGetter().Accept = "application/json, text/javascript";
    return data;
});
Run Code Online (Sandbox Code Playgroud)

我已经分别尝试了两个陈述.在控制器中,然后在http调用本身之前的服务中.仍然无法正常工作.

有人可以给我一个JsFiddle,这显示有效吗?

编辑4:我注意到当我使用GET而不是JSONP时,Accept标头是正确的.但随后响应被拒绝,因为它没有正确的标头.

JSONP调用应该具有哪种标头?因为JSONP调用中有更多标题,但没有任何标识将其标识为JSONP.服务器是否必须具有明确的JSONP支持才能实现此功能?我突然意识到我对jsonp几乎一无所知.

Spi*_*man 12

我想你的答案就在这里.根据wiki,通过注入<script>标记来执行JSONP调用以从主机服务器加载脚本,主机服务器通过调用您的回调来响应,传递数据.甲<script>标签产生一个常规浏览器请求(不是XmlHttpRequest),并且浏览器将发送它自己的Accept报头(它也发送它自己的用户代理报头,例如).

我希望有一个更容易的客户端方式来做到这一点,但我认为唯一的方法可能是在引用的帖子中建议的方式:

因此,如果您希望能够为跨域调用设置请求标头,则必须在您的域上设置服务器端脚本,该脚本将调用委托给远程域(并设置相应的标头),然后发送AJAX请求到你的脚本.

编辑:是一个关于同样问题的(被拒绝的)jQuery错误报告.

更多背景信息:

在有角度的情况下,回调是自动管理的,所以如果你这样说:

$http({
    method: "JSONP",
    url: "http://headers.jsontest.com?callback=JSON_CALLBACK",
}).success(function(data) {
    console.log('Return value:');
    console.log(data);
}).error(function(data) {
    console.log('Error!');
    console.log(data);
})
Run Code Online (Sandbox Code Playgroud)

<script>将创建一个看起来或多或少像这样的标签:

<script type="application/javascript"
        src="http://headers.jsontest.com/?callback=angular.callbacks._1">
</script>
Run Code Online (Sandbox Code Playgroud)

回复的内容http://headers.jsontest.com/?callback=angular.callbacks._1将是:

angular.callbacks._1({key1: "value1", key2: "value2"});
Run Code Online (Sandbox Code Playgroud)

angular.callbacks._1将包含您的success函数,它将与数据一起调用.