2013-11-28 126 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