AngularJS出现$http异步后台无法获取请求参数问题的解决方法
本文实例讲述了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) { //处理错误 });
对于大多数常用的请求类型,都有类似的快捷方法,这些请求类型包括:get、head、post、delete、put、jsonp。
一.进一步配置请求
虽然标准的请求方式使用起来比较简单,但是,有时候会存在可配置性不佳的缺点。如果你想要实现下面这些事情就会遇到困难:
a.给请求加上一些授权头。
b.修改对缓存的处理方式。
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 });
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头,可以简单地操作如下:
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'项
所以在配置中加入:
下面贴入测试时的部分配置代码:
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程序设计有所帮助。