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'项

所以在配置中加入:

代码如下:

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

(0)

相关推荐

  • Angularjs的$http异步删除数据详解及实例

    Angularjs的$http异步删除数据详解及实例 有人会说删除这东西有什么可讲的,写个删除的service,controller调用一下不就完了. 嗯...看起来是这样,但是具体实现起来真的有这么简单吗?首先有以下几个坑 怎么确定数据是否删除成功? 怎么同步视图的数据库的内容? 1.思路 1.实现方式一 删除数据库中对应的内容,然后将$scope中的对应的内容splice 2.实现方式二 删除数据库中对应的内容,然后再reload一下数据(也就是再调用一次查询方法,这种消耗可想而知,并且还要

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

    本文实例讲述了AngularJS出现$http异步后台无法获取请求参数问题的解决方法.分享给大家供大家参考,具体如下: angular在通过异步提交数据时使用了与jQuery不一样的请求头部和数据序列化方式,导致部分后台程序无法正常解析数据. 原理分析(网上的分析): 对于AJAX应用(使用XMLHttpRequests)来说,向服务器发起请求的传统方式是:获取一个XMLHttpRequest对象的引用.发起请求.读取响应.检查状态码,最后处理服务端的响应.整个过程示例如下: var xmlht

  • Java Spring Controller 获取请求参数的几种方法详解

    Java Spring Controller 获取请求参数的几种方法  1.直接把表单的参数写在Controller相应的方法的形参中,适用于get方式提交,不适用于post方式提交.若"Content-Type"="application/x-www-form-urlencoded",可用post提交 url形式:http://localhost:8080/SSMDemo/demo/addUser1?username=lixiaoxi&password=1

  • Express无法通过req.body获取请求传递的数据解决方法

    目录 前言 1.问题描述 2. 解决办法 2.1 解决JSON内容格式 2.2.解决x-www-form-urlencoded内容格式 3.附 3.1.获取get请求参数 3.2.封装XMLHttpRequest 4.总结 前言 最近尝试重新封装XMLHttpRequest,在发post请求的时候,发现express通过req.body获取不到数据,req.body打印出来是一个空对象. 网上也试了网上各种办法,还是不成功,最后发现需要在XMLHttpRequest请求时设置一个请求头,来标识发

  • springMVC获取请求参数的几种方式汇总

    目录 一.前言 二.初步认识 三.servletAPI 四.方法参数 五.@RequestParam 六.实体类 七.总结 一.前言 大家好,我是卷心菜,大二学生一枚. 大家在学习springMVC框架的时候,一定学习过使用这个框架来获取请求的参数,那么各位小伙伴们,获取请求参数有几种方法呢?使用哪种方式最好呢?在什么时候使用这些方法呢?那么这一篇文章,我就带大家来看一看这些问题的答案. 废话不多说,满满的干货,赶快来看看吧~ 二.初步认识 那么什么是请求参数呢? 可以直接在请求地址中给于请求参

  • SpringMVC获取请求参数实现方法介绍

    目录 一.通过ServletAPI获取 二.通过控制器方法的形参获取请求参数 三.@RequestParam 四.@RequestHeader 五.@CookieValue 六.通过POJO获取请求参数 七.解决获取请求参数的乱码问题 我们已经学习过@RequestMapping了,学的属性可能比较多,但是我们常用的也就value和method.所以说我们已经可以把我们的浏览器发送的请求和控制器方法来创建映射关系了. 一.通过ServletAPI获取 将HttpServletRequest作为控

  • SpringMVC实现获取请求参数方法详解

    目录 1.通过ServletAPI获取 2.通过控制器方法的形参获取请求参数 3.@RequestParam 4.@RequestHeader 5.@CookieValue 6.通过POJO获取请求参数 7.解决获取请求参数的乱码问题 1.通过ServletAPI获取 将HttpServletRequest作为控制器方法的形参,此时HttpServletRequest类型的参数表示封装了当前请求的请求报文的对象 <a th:href="@{/testServletAPI(username=

  • 学习SpringMVC——如何获取请求参数详解

    @RequestParam,你一定见过:@PathVariable,你肯定也知道:@QueryParam,你怎么会不晓得?!还有你熟悉的他(@CookieValue)!她(@ModelAndView)!它(@ModelAttribute)!没错,仅注解这块,spring mvc就为你打开了五彩斑斓的世界.来来来,不要兴(mi)奋(hu),坐下来,我们好好聊聊这么些个注解兄弟们~~~(wait, 都没有听过? 好,来,你坐前排,就你!)  一.spring mvc如何匹配请求路径--"请求路径哪家

  • Action获取请求参数的三种方式

    方式一:Action本身作为Model对象,通过属性注入(Setter)方法讲参数数据封装到Action中 具体为:在Action中,提供和参数名相同的几个属性,并为其提供set方法,那么,该参数会被自动封装 到该几个属性中. 方式二:创建独立的Model对象,还是通过属性注入的方法将请求数据封装到Model对象中 具体为:另外创建一个专门的类,并在其中添加几个和请求参数名相同的属性,,此时,在表单中,需要讲参数名, 写成Model的一个具体对象名.参数名的方式,然后在Action中,需要定义一

  • 基于express中路由规则及获取请求参数的方法

    express中常见的路由规则 主要使用的路由规则是get和post两种,即 var express = require('express'); var app = express(); app.get(); // get和post两种请求方式 app.post(); app.get()和app.post()的第一个参数为请求路径,第二个参数为处理请求的回调函数:回调函数有两个参数,分别为req和res,代表请求信息和响应信息. 获取请求路径和请求体中的各种参数 路径请求及对应获取请求路径的形式

  • Django Python 获取请求头信息Content-Range的方法

    request请求头信息的键会加上HTTP_转换成大写存到request.META中 因此你只需要 content_range = request.META['HTTP_CONTENT_RANGE'] 这样就可以获取到Content-Range的信息. django官网的解释: A standard Python dictionary containing all available HTTP headers. Available headers depend on the client and

随机推荐