2013-11-28 54 views
9

我试图访问我不控制的REST服务。第一个问题是该服务不包含Access-Control-Allow-Origin头,这是一个问题,如果我理解正确,立即将我限制为JSONP。

此外,默认情况下,此服务发送XML而不是JSON,尽管它能够发送JSON。我认为它应该回应我的Accept标题,服务负责人说它看起来在我的内容类型。这意味着我需要做一个POST而不是一个GET(虽然当我刚刚得到一些静态数据时,get会更有意义,对吧?)。

固执,因为我在尝试我的Accept头。由于角只接受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直接做,而不是用$ 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的了解不够。

+1

您确定您使用的WebService是否响应您的请求的有效[JSONP](http://en.wikipedia.org/wiki/JSONP)? 我注意到的第一件事是,你没有正确设置你的'callback'查询参数。 ''resource'应该是:'...,params:{callback:'JSON_CALLBACK'},...'如果可能的话,请提供一个Plunk或者JSFiddle,这样我们就可以玩你的代码了。 – vucalur

+0

我相信它不会以有效的JSONP响应。它向我发送XML,可能是因为我没有发送正确的标题。我现在的主要问题是正确设置标题,以便服务知道向我发送json或jsonp。 – mcv

+0

我知道,这就是为什么我建议首先查看您的REQUEST URL( - :.每个使用JSONP响应的WebService都在URL查询字符串中搜索'callback',并且永远不会用JSONP响应,因为它需要'callback'参数值得这样做 它基本上提取'callback'参数值并发送:'<< callback_param_value >>(<< response_JSON_object >>);'回给你'application/javascript' – vucalur

回答

12

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

我希望有一个更简单的客户端的方式来做到这一点,但我觉得唯一的办法可能是在the referenced post的一个建议:

所以,如果你希望能够设置请求跨域调用标头 您必须在您的域上设置一个服务器端脚本,该脚本将 委托给远程域(并分别设置 标头),然后将AJAX请求发送到您的脚本。

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

一些更多的背景信息:

在角,回调会被自动管理,因此,如果您这样说:

$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函数,它将与数据一起被调用。

+0

感谢您的广泛解释。在过去的一周里,我会把这些信息拼凑在一起,但最好能在一个地方明确地表达出来。不知何故,我的印象是,客户端以某种方式处理JSONP的东西,但现在我明白服务器需要能够处理它。我想这也意味着JSONP已经过时了,因为如果您可以将JSONP支持添加到服务器,那么在您的响应中添加“Access-Control-Allow-Origin”标头可能会更容易。 – mcv

+0

它还使http://docs.angularjs.org/api/ng.$http中的“JSON漏洞保护”毫无意义,IMO。(这让我相信你可以根据服务器所有者的愿望在服务器上使用JSONP,这就需要额外的保护。) – mcv

0

虽然你有什么是应该根据文档工作,我的经验有点不同。为了解决这个问题,我们做了以下工作:

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

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

+0

似乎不起作用。你可以在JsFiddle中展示这个吗?在我的情况下,如果我在控制器或服务中添加这个,我仍然会得到'Accept:*/*'。 – mcv

+0

JsFiddle和我今天有爱/恨的关系。在尝试让小提琴起作用时,我注意到在Angular文档中除了列出默认设置正是你想要的之外,他们正在使用一个对象而不是单独设置东西: $ httpProvider.defaults.headers .get = {'My-Header':'value'}。 也许你应该检查你的服务器,看看这些接受类型是否正确处理?我们很早就遇到了同样的情况,结果是这样。 – MBielski

0

以下适用于我 - 但是,我在“运行时”期间使用$http执行此操作,而在引导过程中我没有使用$httpProvider

function SomeCtrl($http) { 

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

} 

编辑

这里是一个工作jsFiddle版本。检查使用开发人员工具/ Firebug完成的请求,并看到请求"application/json, text/javascript"

+0

等一下,transformRequest做什么?这看起来像是处理它的第三种方式,但它听起来与MBielski的解决方案非常相似。 – mcv

+0

这似乎并不奏效。至少不是在我的JsFiddle。 – mcv

+0

任何机会你有一个JsFiddle在哪里工作? – mcv