angular在通過異步提交數據時使用了與jquery不一樣的請求頭部和數據序列化方式,導致部分后臺程序無法正常解析數據。
?
原理分析(網上的分析):
?
[javascript]?view plaincopyprint?
<span?style="font-size:14px;">對于AJAX應用(使用XMLHttpRequests)來說,向服務器發起請求的傳統方式是:獲取一個XMLHttpRequest對象的引用、發起請求、讀取響應、檢查狀態碼,最后處理服務端的響應。整個過程示例如下:</span>?? ?
?
[javascript]?view plaincopyprint?
var?xmlhttp?=?new?XMLHttpRequest();????xmlhttp.onreadystatechange?=?function()?{??????if(xmlhttp.readystate?==?4?&&?xmlhttp.status?==?200)?{??????????var?response?=?xmlhttp.responseText;??????}else?if(xmlhttp.status?==?400)?{?????????????}??};????xmlhttp.open("GET",?"http://myserver/api",?true);????xmlhttp.send();??
?
對于簡單、常用而且會經常重復的任務來說,這是一種很煩瑣的工作。如果你想復用以上過程,你應該進行封裝或者使用代碼庫。
? ? ? ? AngularJS XHR API遵守一種通常被稱為Promise的接口。由于XHR是異步調用的方法,所以服務端的響應會在未來某個不確定的時間點上返回(我們希望它立即能返回)。Promise接口規定了處理這種響應的方式,并且允許Promise的使用者以一種可預見的方式來使用它。
? ? ? ? 例如,我們要從服務端獲取一個用戶的信息,假設用來接受請求的后臺接口位于/api/user路徑上,此接口可以接受一個id屬性作為URL參數,那么使用Angular的核心$http服務發起XHR請求的方法示例如下:
[javascript] view plaincopyprint?
$http.get('api/user',?{params:?{id:'5'}??}).success(function(data,?status,?headers,?config)?{??????}).error(function(data,?status,?headers,?config)?{??????});?? ? ? ? ? 如果你是jQuery使用者,你應該會發現,AngularJS和jQuery在對異步請求的處理方面非常類似。
? ? ? ? 上面例子中使用的$http.get方法是AngularJS的核心服務$http所提供的眾多快捷方法之一。類似地,如果你想使用AngularJS向同一個URL發送POST請求,同時帶上一些POST數據,你可以像下面這樣做:
[javascript] view plaincopyprint?
var?postData?=?{text:'long?blob?of?text'};??var?config?=?{params:?{id:?'5'}};??$http.post('api/user',?postData,?config??).success(function(data,?status,?headers,?config)?{??????}).error(function(data,?status,?headers,?config)?{??????});?? ? ? ? ? 對于大多數常用的請求類型,都有類似的快捷方法,這些請求類型包括:GET、HEAD、POST、DELETE、PUT、JSONP。
一.進一步配置請求
? ? ? ? 雖然標準的請求方式使用起來比較簡單,但是,有時候會存在可配置性不佳的缺點。如果你想要實現下面這些事情就會遇到困難:
? ? ? ? a.給請求加上一些授權頭。
? ? ? ? b.修改對緩存的處理方式。
? ? ? ? c.用一些特殊的方式來變換發送出去的請求,或者變換接收到的響應。
? ? ? ? 在這些情況下,你可以給請求傳遞一個可選的配置對象,從而對請求進行深度配置。在前面的例子中,我們使用config對象指定了一個可選的URL參數。但是那里的GET和POST方法是一些快捷方式。這種深度簡化之后的方法調用示例如下:
$http(config)
? ? ? ? 下面是一個基本的偽代碼模板,用來調用前面的這個方法:
?
[javascript]?view plaincopyprint?
$http({?????method:?string,?????url:?string,?????params:?object,?????data:?string?or?object,?????headers:?object,?????transformRequest:?function?transform(data,?headersGetter)?or?an?array?of?functions,?????transformResponse:?function?transform(data,?headersGetter)?or?an?array?of?functions,?????cache:?boolean?or?Cache?object,?????timeout:?number,?????withCredentials:?boolean??});??
?
? ? ? ? GET、POST及其他快捷方法都會自動設置method參數,所以不需要手動設置。config對象會作為最后一個參數傳遞給$http.get和$http.post,所以,在所有的快捷方法內部都可以使用這個參數。你可以傳遞config對象來修改發送的請求,config對象可以設置以下鍵值。
? ? ? ? method:一個字符串,表示HTTP請求的類型,例如GET或者POST。
? ? ? ? url:URL字符串,表示請求的絕對或者相對資源路徑。
? ? ? ? params:一個鍵和值都是字符串的對象(確切來說是一個map),表示需要轉換成URL參數的鍵和值。例如:
[{key1: 'value1', key2: 'value2'}]
? ? ? ? 將會被轉換成
?key1=value&key2=value2
? ? ? ? 并會被附加到URL后面。如果我們使用js對象(而不是字符串或者數值)作為map中的值,那么這個js對象會被轉換成JSON字符串。
? ? ? ? data:一個字符串或者對象,它會被當作請求數據發送。
? ? ? ? timeout:在請求超時之前需要等待的毫秒數。
?
二.設置HTTP頭
? ? ? ? AngularJS帶有一些默認的請求頭,Angular發出的所有請求上都會帶有這些默認的請求頭信息。默認請求頭包括以下兩個:
? ? ? ? 1.Accept:appliction/json,text/pain,/
? ? ? ? 2.X-Requested-With: XMLHttpRequest
? ? ? ? 如果想設置特殊的請求頭,可以用如下兩種方法實現。
? ? ? ? 第一種方法,如果你想把請求頭設置到每一個發送出去的請求上,那么你可以把需要使用的特殊請求頭設置成AngularJS的默認值。這些值可以通過$httpProvider.defaults.headers配置對象來設置,通常會在應用的配置部分來做這件事情。所以,如果你想對所有的GET請求使用“DO NOT TRACK"頭,同時對所有請求刪除Requested-With頭,可以簡單地操作如下:
[javascript] view plaincopyprint?
angular.module('MyApp',?[]).??????config(function($httpProvider)?{??????????????????delete?$httpProvider.default.headers.common['X-Requested-With'];??????????????????$httpProvider.default.headers.get['DNT']?=?'1';??});?? ? ? ? ? 如果你只想對某些特定的請求設置請求頭,但不把它們作為默認值,那么你可以把頭信息作為配置對象的一部分傳遞給$http服務。同樣的,自定義頭信息也可以作為第二個參數的一部分傳遞給GET請求,第二個參數還可以同時接受URL參數。
$http.get('api/user', {//設置Authorization(授權)頭。在真實的應用中,你需要到一個服務里面去獲取auth令牌headers: {'Authorization': 'Basic Qzsda231231'},params: {id:5}
}).success(function() {//處理成功的情況 });
?
三.緩存響應
? ? ? ? 對于HTTP GET請求,AngularJS提供了一個開箱即用的簡單緩存機制。默認情況下它對所有請求類型都不可用,為了啟用緩存,你需要做一些配置:
[javascript] view plaincopyprint?
$http.get('http://server/myapi',?{??????cache:?true??}).success(function()?{ ? ? ? ? 這樣就可以啟用緩存,然后AngularJS將會緩存來自服務器的響應。下一次向同一個URL發送請求的時候,AngularJS將會返回緩存中的響應內容。緩存也是智能的,所以即使你向同一個URL發送多次模擬的請求,緩存也只會向服務器發送一個請求,而且在收到服務端的響應之后,響應的內容會被分發給所有請求。
? ? ? ? 但是,這樣做有些不太實用,因為用戶會先看到緩存的舊結果,然后看到新的結果突然出現。例如,當用戶即將點擊一條數據時,它可能會突然發生變化。
? ? ? ? 注意,從本質上來說,響應(即使是從緩存中讀取的)依然是異步的。換句話說,在第一次發出請求的時候,你應該使用處理異步請求的方式來編碼。
?
四.轉換請求和響應
? ? ? ? 對于所有通過$http服務發出的請求和收到的響應來說,AngularJS都會進行一些基本的轉換,包括如下內容。
1.轉換請求
? ? ? ? 如果請求的配置對象屬性中包含JS對象,那么就把這個對象序列化成JSON格式。
2.轉換響應
? ? ? ? 如果檢測到了XSRF(Cross Site Request Forgery的縮寫,意為跨站請求偽造,這是跨站腳本攻擊的一種方式)前綴,則直接丟棄。如果檢測到了JSON響應,則使用JSON解析器對它進行反序列化。
? ? ? ? 如果你不需要其中的某些轉換,或者想自已進行轉換,可以在配置項里面傳入自已的函數。這些函數會獲取HTTP的request/response體以及協議頭信息,然后輸出序列化、修改之后的版本。可以使用transformLRequest和transformResponse作為key來配置這些轉換函數,而這兩個函數在模塊的config函數中是用$httpProvider服務來配置的。
? ? ? ? 我們什么時候需要使用這些東西呢?假設我們有一個服務,它更適合用jQuery的方式來操作。POST數據使用key1=val1&key2=val2(也就是字符串)形式來代替{key1:val1, key2:val2}JSON格式。我們可以在每個請求中來進行這種轉換,也可以添加一個獨立transformRequest調用,對于當前這個例子來說,我們打算添加一個通用的transformRequest,這樣所有發出的請求都會進行這種從JSON到字符串的轉換。下面就是實現方式:
[javascript] view plaincopyprint?
var?module?=?angular.module('myApp');????module.config(function($httpProvider)?{??????$httpProvider.defaults.transformRequest?=?function(data)?{??????????????????????return?$.param(data);?????????};??});?? ?
實列配置:
在使用中發現后臺程序還是無法解析angular提交的數據,對比后發現頭部缺少‘X-Requested-With’項
所以在配置中加入:$httpProvider.defaults.headers.post['X-Requested-With'] = 'XMLHttpRequest'
下面貼入測試時的部分配置代碼:
?
[javascript]?view plaincopyprint?
angular.module('app',?[??????'ngAnimate',??????'ngCookies',??????'ngResource',??????'ngRoute',??????'ngSanitize',??????'ngTouch'??],function?($httpProvider)?{??????????$httpProvider.defaults.headers.post['Content-Type']?=?'application/x-www-form-urlencoded;charset=utf-8';??????$httpProvider.defaults.headers.post['Accept']?=?'application/json,?text/javascript,?*/*;?q=0.01';??????$httpProvider.defaults.headers.post['X-Requested-With']?=?'XMLHttpRequest';????????/**???????*?重寫angular的param方法,使angular使用jquery一樣的數據序列化方式??The?workhorse;?converts?an?object?to?x-www-form-urlencoded?serialization.???????*?@param?{Object}?obj???????*?@return?{String}???????*/??????var?param?=?function?(obj)?{??????????var?query?=?'',?name,?value,?fullSubName,?subName,?subValue,?innerObj,?i;????????????for?(name?in?obj)?{??????????????value?=?obj[name];????????????????if?(value?instanceof?Array)?{??????????????????for?(i?=?0;?i?<?value.length;?++i)?{??????????????????????subValue?=?value[i];??????????????????????fullSubName?=?name?+?'['?+?i?+?']';??????????????????????innerObj?=?{};??????????????????????innerObj[fullSubName]?=?subValue;??????????????????????query?+=?param(innerObj)?+?'&';??????????????????}??????????????}??????????????else?if?(value?instanceof?Object)?{??????????????????for?(subName?in?value)?{??????????????????????subValue?=?value[subName];??????????????????????fullSubName?=?name?+?'['?+?subName?+?']';??????????????????????innerObj?=?{};??????????????????????innerObj[fullSubName]?=?subValue;??????????????????????query?+=?param(innerObj)?+?'&';??????????????????}??????????????}??????????????else?if?(value?!==?undefined?&&?value?!==?null)??????????????????query?+=?encodeURIComponent(name)?+?'='?+?encodeURIComponent(value)?+?'&';??????????}????????????return?query.length???query.substr(0,?query.length?-?1)?:?query;??????};????????????$httpProvider.defaults.transformRequest?=?[function?(data)?{??????????return?angular.isObject(data)?&&?String(data)?!==?'[object?File]'???param(data)?:?data;??????}];??}).config(function?($routeProvider)?{??????????$routeProvider??????????????.when('/',?{??????????????????templateUrl:?'views/main.html',??????????????????controller:?'MainCtrl'??????????????})??????????????.when('/about',?{??????????????????templateUrl:?'views/about.html',??????????????????controller:?'AboutCtrl'??????????????})??????????????.otherwise({??????????????????redirectTo:?'/'??????????????});??????});??
?
轉載于:https://www.cnblogs.com/cook/p/4582872.html
總結
以上是生活随笔為你收集整理的AngularJS $http 异步后台无法获取请求参数的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。