我正在尝试访问我无法控制的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几乎一无所知。
答案 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;
});
}
。