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

时间:2013-11-28 12:42:37

标签: http angularjs jsonp 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;
}]);

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

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'
    }
});

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

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

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

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

我也尝试使用$ http直接使用$ 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;
});

我已经单独尝试了两个声明。在控制器中,然后在http调用本身之前的服务中。仍然无效。

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

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

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

3 个答案:

答案 0 :(得分:12)

我认为你的答案是here。根据{{​​3}},通过注入<script>标记来执行JSONP调用以从主机服务器加载脚本,主机服务器通过调用您的回调来响应,传递数据。 <script>标记生成常规浏览器请求(不是XmlHttpRequest),浏览器将发送自己的Accept标头(例如,它还会发送自己的User-Agent标头)。

我希望有一个更简单的客户端方式来做到这一点,但我认为唯一的方法可能是the wiki中建议的方式:

  

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

编辑:the referenced post是关于同样问题的(被拒绝的)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);
})

将创建一个看起来或多或少的<script>标记:

<script type="application/javascript"
        src="http://headers.jsontest.com/?callback=angular.callbacks._1">
</script>

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

angular.callbacks._1({key1: "value1", key2: "value2"});

angular.callbacks._1将包含您的success函数,并将使用数据调用它。

答案 1 :(得分:0)

虽然你所拥有的东西应该按照文档工作,但我的经验却有所不同。为了解决这个问题,我们做了以下事情:

创建一个“基本控制器”,可以在body或html标记上添加到页面中。 在该控制器中,使用$ http而不是$ httpProvider进行分配。由于您的基本控制器在初始页面加载时加载,因此它将存在于您的应用程序中运行的所有其他控制器和服务。

我不知道为什么会这样,而被禁止的方法却没有,我很乐意看到你的问题的答案比这个解决方案更好,但至少这可以让你继续前进试。

答案 2 :(得分:0)

以下对我有用 - 但是,我在“{1}}的”运行时“期间执行此操作,并且在引导期间我没有使用$http

$httpProvider

修改

这是一个有效的jsFiddle版本。检查使用Developer Tools / Firebug完成的请求,并查看是否请求function SomeCtrl($http) { $http.defaults.transformRequest.push(function (data, headersGetter) { headersGetter().Accept = "application/json, text/javascript"; return data; }); }