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 相关文章推荐
使用户点击后退按钮使效三行代码
Jul 07 Javascript
js 新浪的一个图片播放图片轮换效果代码
Jul 15 Javascript
同一页面多个商品倒计时JS 基于面向对象的javascript
Feb 16 Javascript
通过百度地图获取公交线路的站点坐标的js代码
May 11 Javascript
jQuery 网易相册鼠标移动显示隐藏效果实现代码
Mar 31 Javascript
js事件驱动机制 浏览器兼容处理方法
Jul 23 Javascript
关于javascript的一些知识以及循环详解
Sep 12 Javascript
提高Web性能的前端优化技巧总结
Feb 27 Javascript
利用JS制作万年历的方法
Aug 16 Javascript
解决使用layui的时候form表单中的select等不能渲染的问题
Sep 18 Javascript
JavaScript图像放大镜效果实现方法详解
Jun 28 Javascript
JS原生实现轮播图的几种方法
Mar 23 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 数组的创建、调用和更新实现代码
2009/03/09 PHP
Php图像处理类代码分享
2012/01/19 PHP
如何使用php输出时间格式
2013/08/31 PHP
php中的boolean(布尔)类型详解
2013/10/28 PHP
PHP反射机制用法实例
2014/08/28 PHP
PHP不使用递归的无限级分类简单实例
2016/11/05 PHP
Laravel 错误提示本地化的实现
2019/10/22 PHP
JavaScript DOM 学习第三章 内容表格
2010/02/19 Javascript
JS实现鼠标单击与双击事件共存
2014/03/08 Javascript
JQuery 给元素绑定click事件多次执行的解决方法
2014/09/09 Javascript
JavaScript操作HTML元素和样式的方法详解
2015/10/21 Javascript
JavaScript实现的Tween算法及缓冲特效实例代码
2015/11/03 Javascript
JavaScript开发Chrome浏览器扩展程序UI的教程
2016/05/16 Javascript
纯JavaScript 实现flappy bird小游戏实例代码
2016/09/27 Javascript
a标签置灰不可点击的实现方法
2017/02/06 Javascript
ES6新增数据结构WeakSet的用法详解
2017/08/07 Javascript
vue指令只能输入正数并且只能输入一个小数点的方法
2018/06/08 Javascript
python 实现自动远程登陆scp文件实例代码
2017/03/13 Python
Python通过OpenCV的findContours获取轮廓并切割实例
2018/01/05 Python
matplotlib.pyplot画图 图片的二进制流的获取方法
2018/05/24 Python
浅谈关于Python3中venv虚拟环境
2018/08/01 Python
详解Python 字符串相似性的几种度量方法
2019/08/29 Python
Django项目中使用JWT的实现代码
2019/11/04 Python
Python读取JSON数据操作实例解析
2020/05/18 Python
实例讲解Python 迭代器与生成器
2020/07/08 Python
HTML5实现多张图片上传功能
2016/03/11 HTML / CSS
澳洲女装时尚在线:Blue Bungalow
2018/05/05 全球购物
护理工作感言
2014/01/16 职场文书
倡议书格式模板
2014/05/13 职场文书
社团活动总结怎么写
2014/06/30 职场文书
幼儿园小班见习报告
2014/10/31 职场文书
单位委托函范文
2015/01/29 职场文书
自我推荐信格式模板
2015/03/24 职场文书
汽车4S店前台接待岗位职责
2015/04/03 职场文书
忆童年!用Python实现愤怒的小鸟游戏
2021/06/07 Python
关于Redis的主从复制及哨兵问题
2022/06/16 Redis