AngularJS出现$http异步后台无法获取请求参数问题的解决方法


Posted in Javascript onNovember 03, 2016

本文实例讲述了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'项

所以在配置中加入:

$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程序设计有所帮助。

Javascript 相关文章推荐
js不是基础的基础
Dec 24 Javascript
深入理解javascript学习笔记(一) 编写高质量代码
Aug 09 Javascript
简单的邮箱登陆的提示效果类似于yahoo邮箱
Feb 26 Javascript
jQuery中before()方法用法实例
Dec 25 Javascript
使用Meteor配合Node.js编写实时聊天应用的范例
Jun 23 Javascript
微信小程序之拖拽排序(代码分享)
Jan 21 Javascript
用ES6写全屏滚动插件的示例代码
May 02 Javascript
Vue实现导航栏点击当前标签变色功能
Aug 19 Javascript
JavaScript中callee和caller的区别与用法实例分析
Jun 28 Javascript
浅析Angular 实现一个repeat指令的方法
Jul 21 Javascript
js实现烟花特效
Mar 02 Javascript
Ajax实现页面无刷新留言效果
Mar 24 Javascript
Centos7 中安装 Node.js v4.4.4
Nov 03 #Javascript
[原创]javascript typeof id==='string'?document.getElementById(id):id解释
Nov 02 #Javascript
微信小程序  audio音频播放详解及实例
Nov 02 #Javascript
微信小程序 vidao实现视频播放和弹幕的功能
Nov 02 #Javascript
bootstrap基础知识学习笔记
Nov 02 #Javascript
AngularJS深入探讨scope,继承结构,事件系统和生命周期
Nov 02 #Javascript
Bootstrap table的使用方法
Nov 02 #Javascript
You might like
最令PHP初学者们头痛的十四个问题
2007/01/15 PHP
php利用header函数实现文件下载时直接提示保存
2009/11/12 PHP
php根据日期显示所在星座的方法
2015/07/13 PHP
PHP判断手机是IOS还是Android
2015/12/09 PHP
ie和firefox中img对象区别的困惑
2006/12/27 Javascript
JavaScript Timer实现代码
2010/02/17 Javascript
基于Jquery的仿照flash放大图片效果代码
2011/03/16 Javascript
JS 无限级 Select效果实现代码(json格式)
2011/08/30 Javascript
Visual Studio中js调试的方法图解
2014/06/30 Javascript
js图片闪动特效可以控制间隔时间如几分钟闪动一下
2014/08/12 Javascript
js实现多选项切换导航菜单的方法
2015/02/06 Javascript
AngularJS 让人爱不释手的八种功能
2016/03/23 Javascript
相册展示PhotoSwipe.js插件实现
2016/08/25 Javascript
js实现table添加行tr、删除行tr、清空行tr的简单实例
2016/10/15 Javascript
jQuery中的100个技巧汇总
2016/12/15 Javascript
jQuery无刷新上传之uploadify简单代码
2017/01/17 Javascript
jQuery表格的维护和删除操作
2017/02/03 Javascript
Js利用prototype自定义数组方法示例
2017/10/20 Javascript
javascript实现循环广告条效果
2017/12/12 Javascript
在vue中实现点击选择框阻止弹出层消失的方法
2018/09/15 Javascript
解决layer图标icon不加载的问题
2019/09/04 Javascript
功能完善的小程序日历组件的实现
2020/03/31 Javascript
Openlayers实现点闪烁扩散效果
2020/09/24 Javascript
Vue仿Bibibili首页的问题
2021/01/21 Vue.js
Python中类型检查的详细介绍
2017/02/13 Python
python实现求解列表中元素的排列和组合问题
2018/03/15 Python
使用python实现ftp的文件读写方法
2019/07/02 Python
python网络编程 使用UDP、TCP协议收发信息详解
2019/08/29 Python
pytorch 常用函数 max ,eq说明
2020/06/28 Python
北承题目(C++)
2012/05/16 面试题
会计职业生涯规划书
2014/01/13 职场文书
本科毕业生应聘自荐信范文
2014/06/26 职场文书
个人工作总结范文2014
2014/11/07 职场文书
2015年个人自我剖析材料
2014/12/29 职场文书
pytorch--之halfTensor的使用详解
2021/05/24 Python
万能密码的SQL注入漏洞其PHP环境搭建及防御手段
2021/09/04 SQL Server