本文實例講述了AngularJS出現$http異步後台無法取得請求參數問題的解決方法。分享給大家供大家參考,具體如下:
angular在透過非同步提交資料時使用了與jQuery不一樣的請求頭部和資料序列化方式,導致部分後台程式無法正常解析資料。
原理分析(網路上的分析):
對於AJAX應用程式(使用XMLHttpRequests)來說,向伺服器發起請求的傳統方式是:取得一個XMLHttpRequest物件的參考、發起請求、讀取回應、檢查狀態碼,最後處理服務端的回應。整個過程範例如下:
var xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function() { if(xmlhttp.readystate == 4 && xmlhttp.status == 200) { var response = xmlhttp.responseText; }else if(xmlhttp.status == 400) { //或者可以是任何以4开头的状态码 //优雅地处理错误 } }; //建立连接 xmlhttp.open("GET", "http://myserver/api", true); //发起请求 xmlhttp.send();
對於簡單、常用且會經常重複的任務來說,這是一種很煩瑣的工作。如果你想重複使用以上流程,你應該進行封裝或使用程式碼庫。
AngularJS XHR API遵守一種通常被稱為Promise的介面。由於XHR是非同步呼叫的方法,所以服務端的回應會在未來某個不確定的時間點上回傳(我們希望它立即能回傳)。 Promise介面規定了處理這種回應的方式,並且允許Promise的使用者以一種可預見的方式來使用它。
例如,我們要從服務端獲取一個用戶的信息,假設用來接受請求的後台接口位於/api/user路徑上,此接口可以接受一個id屬性作為URL參數,那麼使用Angular的核心$http服務發起XHR請求的方法範例如下:
$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數據,你可以像下面這樣做:
var postData = {text:'long blob of text'}; //下面这一行会被当成参数附加到URL后面,所以post请求最终会变成/api/user?id=5 var config = {params: {id: '5'}}; $http.post('api/user', postData, config ).success(function(data, status, headers, config) { //成功之后做一些事情 }).error(function(data, status, headers, config) { //处理错误 });
對於大多數常用的請求類型,都有類似的
對於大多數常用的請求類型,都有類似的
對於大多數常用的請求類型,都有類似的
對於大多數常用的請求類型,都有類似的
a.給請求加上一些授權頭。
c.用一些特殊的方式來變換發送出去的請求,或變換接收到的回應。
在這些情況下,你可以給請求傳遞一個可選的配置對象,從而對請求進行深度配置。在前面的範例中,我們使用config物件指定了一個可選的URL參數。但是那裡的GET和POST方法是一些快捷方式。這個深度簡化之後的方法呼叫範例如下:
$http(config)
下面是一個基本的偽代碼模板,用來呼叫前面的這個方法:
$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 });
PO
方法都會自動設定method參數,所以不需要手動設定。 config物件會作為最後一個參數傳遞給$http.get和$http.post,所以,在所有的快捷方法內部都可以使用這個參數。你可以傳遞config物件來修改傳送的請求,config物件可以設定以下鍵值。
url:URL字串,表示請求的絕對或相對資源路徑。
params:一個鍵和值都是字串的物件(確切地說是一個map),表示需要轉換成URL參數的鍵和值。例如:[{key1: 'value1', key2: 'value2'}]
?key1=value&key2=value2
timeout:在請求逾時之前需要等待的毫秒數。
angular.module('MyApp', []). config(function($httpProvider) { //删除AngularJS默认的X-Request-With头 delete $httpProvider.default.headers.common['X-Requested-With']; //为所有GET请求设置DO NOT TRACK $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提供了一个开箱即用的简单缓存机制。默认情况下它对所有请求类型都不可用,为了启用缓存,你需要做一些配置:
$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到字符串的转换。下面就是实现方式:
var module = angular.module('myApp'); module.config(function($httpProvider) { $httpProvider.defaults.transformRequest = function(data) { //使用jQuery的param方法把JSON数据转换成字符串形式 return $.param(data); }; });
实列配置:
在使用中发现后台程序还是无法解析angular提交的数据,对比后发现头部缺少‘X-Requested-With'项
所以在配置中加入:
复制代码代码如下:
$httpProvider.defaults.headers.post['X-Requested-With'] = 'XMLHttpRequest'
下面贴入测试时的部分配置代码:
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; }; // Override $http service's default transformRequest $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: '/' }); });
希望本文所述对大家AngularJS程序设计有所帮助。