9

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

此外,默认情况下,此服务发送 XML 而不是 JSON,尽管它能够发送 JSON。我认为它应该响应我的 Accept 标头,负责服务的人员说它会查看我的 Content-Type。这意味着我需要做一个 POST 而不是 GET(尽管当我只是获取一些静态数据时 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 而不是使用 $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 的了解还不够。

4

3 回答 3

12

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

我希望有一种更简单的客户端方法可以做到这一点,但我认为唯一的方法可能是参考帖子中建议的方法:

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

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

更多背景信息:

在 Angular 中,回调是自动管理的,所以如果你这样说:

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

于 2013-12-05T15:53:11.517 回答
0

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

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

我不知道为什么这有效而被禁止的方法无效,我很想看到比这种解决方法更好的问题的答案,但至少这可以让您再次推进开发。

于 2013-12-04T15:12:41.950 回答
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"有请求。

于 2013-12-04T16:27:21.410 回答