Angular服务器通信之:$http
$http服务提供了浏览器XMLHttpRequest对象的封装,并且作为Angular中和后台服务通信的底层服务,在此之上Angular还提供了一个可选模块ngResource支持与RESTFul的后端数据源进行交互。除了Angular本身提供的通信方式外,还有一个封装良好,更加优雅的第三方库Restangular可供选择使用。
在本篇文章中,主要介绍$http的使用,同时也会提及到和它相关的$httpProvider, $q, $cacheFactory等服务。
注意:本文依据Angular v1.4.1进行撰写。
快速上手
$http服务只接收一个配置对象作为参数,并且返回一个promise对象,具有success和error两个方法。
// 使用success()和error()
$http({
  method: 'GET',
  url: '/api/user/1'
}).success(function(rep) {
  // ...
}).error(function(err) {
  // ...
});
// 使用then()方法
$http({
  method: 'GET',
  url: '/api/user/1'
}).then(function(rep) {
  // 成功
}, function(err) {
  // 失败
});
// 也可以分开写
var promise = $http({method: 'GET', url: '/api/user/1'});
promise.success(function(rep) {});
promise.error(function(err) {});
promise.then(function(rep) {}, function(err) {});
如果响应状态在200和299之间,会认为响应是成功的,success回调或者then()方法的第一个函数会执行,否者error回调或者then()方法的第二个函数会得到调用。在实际开发过程中除了404外,我们会对每个客户端请求发送一个响应json数据,并带有一个状态码字段,然后客户端根据不同的状态码来做后续的数据处理。这里给出一个status的定义结构,粗略的概括了几种常见情况:
var rep_status = [
  {key: 'SUCCESS', value: 1, desc: '交互成功'},
  {key: 'NOT_LOGIN', value: 2, desc: '未登录'},
  {key: 'INVALID_REQUEST', value: 3, desc: '非法请求'},
  {key: 'INVALID_PARAM', value: 4, desc: '参数错误'},
  {key: 'INNER_ERROR', value: 5, desc: '服务器内部错误'},
  {key: 'UNKNOWN', value: 6, desc: '未知错误'}
]
对于返回api这里也统一约定如下:
// 响应成功返回
{
  status: 1,
  message: 'ok',
  data: {
    total: 200,
    object_list: [
      {
        // ....
      }
    ]
  }
}
// 出现错误后端返回
{
  status: 4,
  message: '参数错误'
}
通过success()和error()得到的响应数据只包含服务器响应数据的主体,如果想得到一个完整的对象,需要使用then()方法。一个完整的响应对象或者叫promise对象包含如下字段:
- data - {string | Object} 响应主体
- status - {number} 响应状态
- headers - {function([headerName])} 获取响应头部的函数
- config - {Object} 请求触发时提供的配置对象,后面会详细介绍
- statusText - {string} 响应状态描述
promise.then(function(rep) {
  console.log(rep.data.data.name); // jenemy
  console.log(rep.status); // 200
  console.log(rep.statusText); // OK
  console.log(rep.config.url); // /api/user/5
  console.log(rep.headers()['x-powered-by']); // Express
});
HTTP请求快捷方式
如果每次发起请求都要去配置$http一次参数略显麻烦,因此Angular对于常见的HTTP请求方式都提供了对应的快捷方法:
- GET: $http.get(url, config)
- POST: $http.post(url, data, config)
- PUT: $http.put(url, data, config)
- DELETE: $http.delete(url, config)
- HEAD: $http.head(url, config)
- JSONP: $http.jsonp(url, config)
注意:上述快捷方式中config为可选参数。
使用方式如下:
var url = '/api/user/5';
var params = {id: 5};
var data = {name: 'jenemy'};
// GET
$http.get(url).then(function(rep) {
  console.log(rep.data.data.name); // jenemy
});
$http.get(url, {params: params}).then(function(rep) {
  console.log(rep.data.data.name); // jenemy
});
// POST
$http.post(url, data).then(function(rep) {
  console.log(rep.message); // ok
});
// PUT
$http.put(url, data).then(function(rep) {
  console.log(rep.message); // ok
});
// DELETE
$http.delete(url, {params: params, data: data}).then(function(rep) {
  console.log(rep.message); // ok
})
// HEAD
$http.head(url, {params: params});
使用$http.jsonp()方法,只需要在请求url后面加上参数callback=JSON_CALLBACK,然后调用success()回调方法获取返回的数据,这里JSON_CALLBACK必需全部大写,当然也可以指定其它回调函数,但必须是定义在window下的全局函数。
var params = {name: "jenemy"};
// 使用默认的JSON_CALLBACK回调
// 服务的响应体:angular.callbacks._0({"name":"jenemy","age":25,"gender":"M"})
$http.jsonp('/api/user/5?callback=JSON_CALLBACK', {params: params}).success(function(rep) {
  console.log(rep.name); // jenemy
});
// 自定义回调方法
function handleJsonpCallback(data) {
  console.log(data.name); // jenemy
}
// 服务的响应体:handleJsonpCallback({"name":"jenemy","age":25,"gender":"M"})
$http.jsonp('/api/user/5?callback=handleJsonpCallback', {params: params}).success(function(rep) {
  alert(rep.name); // 只有callbac=JSON_CALLBACK才会执行
})
这里使用node作为jsonp响应后台举例:
app.get('/api/user/:id', function(req, res) {
  var data = {name: 'jenemy', age: 25, gender: 'M'};
  var str = req.query.callback + '(' + JSON.stringify(data) + ')';
  res.send(str);
});
$http配置属性
相对于jQuery的$.ajax()的配置来说,Angular中$http()的配置更加简洁,而又不失功能性,使用起来也相对容易些。这里会对每个配置项进行详细的说明。
method - {string}
配置希望发送的请求HTTP方法。它的值是下列各项其中之一:'GET'、'POST'、'PUT'、'DELETE'、'HEAD'、'JSONP'。
url - {string}
请求路径,可以是相对或者绝对地址。
params - {string | Object}
URL请求参数,一个字符串map或对象,会被转换成查询字体串追加在URL后面。如果值不是字体串,会被JSON序列化。
// 参数会转化为 ?name=jenemy&age=25
$http({
  method: 'GET',
  url: '/api/user/5',
  params: {
    name: 'jenemy',
    age: 25
  }
});
data - {string | Object}
作为消息体将要被发送到服务器的数据,通常在发送POST或者PUT时使用。
headers - {Object}
HTTP请求头部,可以为一个字符串map或对象,也可以是一个函数返回一个对象。
$http({
  method: 'POST',
  url: '/api/user/2',
  data: {name: "jenemy"},
  headers: {
    'Authorization': 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==',
    'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
  }
});
transformRequest与transformResponse
在Angular中可以使用转换函数对请求和响应数据进行转换,默认情况下,设置了如下转换规则:
- transformRequest:如果发送的请求配置对象中- data为一个对象或者包含一个对象,则将其格式化。
- transformResponse:如果检测到时了XSRF前缀,则直接丢弃。如果检测到了JSON响应,则使用JSON解析器(- JSON.parse())对它进行序列化。
如果需要在全局修改其默认值,只需要修改$httpProvider.defaults.transformRequest和$HttpProvider.defaults.transformResponse值即可。如果想要添加自定义的转换规则可以通过push和``unshift`添加到转换链( transformation chain)。最后觉得默认的转换方式不好用,也可以通过直接为默认转换函数赋新值的方式,而不是采用数组包装器的方式来完全重写默认转换规则。
除了在$httpProvider中设置全或者修改全局的规则外,也可以在运行时通过$http来修改转换规则,这里只需要在请求方法中的配置对象中设置transformRequest和transformResponse属性的值即可。
cache - {boolean | Cache}
默认情况下cache机制并没有开启,我们需要将请求配置对象中的cache属性设置为true(使用默认cache)或者自定义一个cache对象(使用cacheFactory构建)。当cache机制激活后,$http会缓存指定的请求url,下一次向同一个url发送的请求的时候会直接加载缓存中的数据,不会再产生一次http请求发出。
$http.get('/api/user', {
  cache: true
}).success(function(rep) {}); // 处理成功的数据
注意后续数据虽然是以缓存的方式读取的,但是响应依然是异步的。
如果想通过新建一个对象(使用$cacheFactory)来更改默认cache机制设置,简单更新一下$http.defaults.cache的值即可。一旦更新后,所有已设置cache的请求都将使用新定义的缓存对象。
var myCache = $cacheFactory('myCache');
$http({
  method: 'GET',
  url: '/api/cards',
  cache: myCache
});
如果将默认cache的值转为false,那么将只有那些自己定义了缓存对象的请求才会被缓存。
timeout - {number | Promise}
如果cache被设置为一个数值,那么请求将会在推迟timeout指定的毫秒数后再发送。如果被设置为一个promise对象,那么当该promise对象被resolve时请求会被中止。
withCredentials - {boolean}
如果该属性设置为true,那么请求对象中会设置withCredentials标记。
默认情况下,CORS请求不会发送cookie,而withCredentials标记会在请求中加入Access-Control-Allow-Credentials头,这样请求就会将目标域的cookie包含在请求中。
responseType - {string}
该选项会在请求中设置XMLHttpRequest属性,具体有那些属性可以参考XMLHttpRequest。
xsrfHeaderName与xsrfCookieName
在了解这两个配置作用前,首先要了解什么是CSRF,CSRF (Cross-site request forgery) 跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。CSRF 则通过伪装来自受信任用户的请求来利用受信任的网站。
Angular提供了一种简单通用的机制来防御XSRF攻击。首先,在客户端第一次发送get请求时,服务器响应数据会携带一个会话cookie(XSRF-TOKEN)一同发送到客户端,在后续请求发送前,$http服务会从cookie中读取一个tocken(默认为XSRF-TOKEN)并且将其设置到HTTP头部(X-XSRF-TOKEN)一同发送到服务器。然后,服务器端会判断HTTP头部是否携带X-XSRF-TOKEN值,如果该值与之前发送的会话cookie值相同,就可以判定为来自己同一domain请求,否者会拦截该请求。
明白了Angular如何处理XSRF,理解这两个配置也就一目了然了。xsrfHeaderName保存XSRF令牌的HTTP头部名称, xsrfCookieName保存XSRF令牌的cookie名称。
angular.module('app', ['ui.router'], function($httpProvider) {
  $httpProvider.defaults.xsrfCookieName = 'csrftoken';
  $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
});
注意:关于这两个属性以及CSRF不是本文讨论范围,本人也没有相关经验,更多关于CSRF请查看CSRF Token 的设计是否有其必要性?
设置HTTP请求头部
Angular默认使用application/json提交数据到服务器,如果需要修改默认设置为application/x-www-form-urlencoded的话可以在按下面的方式修改:
var app = angular.module('app', []);
app.config(function($httpProvider) {
  // POST
  $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
  // PUT
  $httpProvider.defaults.headers.put['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
  // PATCH
  $httpProvider.defaults.headers.patch['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
  // DELETE
  $httpProvider.defaults.headers.delete = { 'Content-Type' : 'application/x-www-form-urlencoded;charset=utf-8' };
})
注意在$httpProvider.defaults.headers中默认是没有DELETE方式的配置,只有PUT、POST和PATCH,因此需要为其添加一个delete字段来扩展原有对象。通过调用$http.delete()时会发现其默认使用text/plain;charset=UTF-8提交数据,这是因为我们没有定义请求头部,Content-Type使用了默认的text/plain;charset=UTF-8。
如果想每次发起请求时都带一个自定义头部,那么$httpProvider.defaults.headers.common可以派上用场,Angular默认为其默认定义了一个Accept属性,这里只需要将我们需要的值添加进这个common对象即口。
$httpProvider.defaults.headers.common['myHeader'] = 'myValue';
如果想要删除头部某个字段,只需要使用 delete 操作即可
// 删除后请求头里不再有 X-Requested-With 属性
delete $httpProvider.defaults.headers.common['X-Requested-With'];
实际上$httpProvider和$http按照C++语言来讲是指向同一个指针的两个对象,因此也可以使用$http来设置头部,只是两者适用场景不一样。
$http.defaults.headers.common['myHeader'] = 'myValue';
上述讲的都是全局(使用$httpProvider)或者某个controller中(使用$http)设置头部,如果需要为某一个单独的请求设置头部怎么做呢?其实只需要在其设置对象中的headers字段中添加即可,添加方式如下:
$http({
  method: 'POST',
  url: '/api/user/2',
  data: {name: "jenemy"},
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
  }
});
补充一点,如果请求未设置Content-Type,那么请求参数或者消息体在Chrome的开发都工具下面显示的是request payload形式,包括application/json类型也是。如果类型设置为application/x-www-form-urlencoded,那么显示为form data。稍加不注意就会影响最终数据提交,不光是Angular,jQuery也有类似的问题。
$.post()与$http.post()区别
在jQuery中使用$.post()提交数据时其默认Content-Type为application/x-www-form-urlencoded,在Chrome的开发都工具下面显示的是'Form Data',而在Angular中,其默认Content-Type为application/json,在Chrome的开发都工具下面显示的是'Request Payload'形式。这样会导致在PHP中无法使用$_REQUEST/$_POST获取到$http.post()的数据,而需要用json_decode()方法。
其实原因很简单,因为在$.post()中会序列化要提交的数据,而$http.post()不会。
// jQuery会在提交前将数据转换成字符串:"name=jenemy&age=25&addr=shanghai"
var data = {name: 'jenemy', age: 25, addr: 'shanghai'};
序列化Angular表单数据
当我们修改了$http的默认提交方式为application/x-www-form-urlencoded后,接下要做就是如何将数据序列化。实际上我们有很多种方式来实现数据序列化,可以选择使用jQuery提交的方法,也可以使用Angular内置的方法,最后也可以自己写一个序列化的方法来达到目的。
使用jQuery的$.param()方法
var data = {name: 'jenemy', age: 25, addr: 'shanghai', date: '2015-12-12 12:21:00'};
// 序列化后:name=jenemy&age=25&addr=shanghai&date=2015-12-12+12%3A21%3A00
$http({
  method: 'POST',
  url: '/api/user/2',
  data: $.param(data),
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
  }
});
注意:$.param()方法可以正常序列化嵌套子对象,但不能正确的序列化数组。
var data = {foo: "hi there", bar: { blah: 123, quux: [1, 2, 3] }};
// 输出:foo=hi+there&bar[blah]=123&bar[quux][]=1&bar[quux][]=2&bar[quux][]=3
// 很明显解析后的数组部分不是我期待的结果:foo=hi+there&bar[blah]=123&bar[quux][0]=1&bar[quux][1]=2&bar[quux][2]=3
console.log(decodeURIComponent($.param(data)));
使用Angular的$httpParamSerializer服务和$httpParamSerializerJQLike服务
var data = {name: 'jenemy', age: 25, addr: 'shanghai', date: '2015-12-12 12:21:00'};
// 序列化后:addr=shanghai&age=25&date=2015-12-12+12:21:00&name=jenemy
$http({
  method: 'POST',
  url: '/api/user/2',
  data: $httpParamSerializer(data),
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
  }
});
$httpParamSerializerJQLike服务从名字就可以看出来它和jQuery的$.param()作用是一样的,唯一不同点在于它会将字段按字母顺序排序。$httpParamSerializer服务同样会对字段进行排序,不同在于处理序列化时只会处理第一级数据,如果存在嵌套或者数组都会将其看作一个字符串。
var data = {foo: "hi there", bar: { blah: 123, quux: [1, 2, 3] }};
// 序列化后:bar={"blah":123,"quux":[1,2,3]}&foo=hi+there
console.log(decodeURIComponent($httpParamSerializer(_data)));
使用transformRequest在最后发送前使用自定义序列化方法
由于jQuery和Angular提供的方法都无法满足我们的需求,所以我们需要手工来处理序列化问题,下面是目前所在公司使用的序列化方案:
var app = angular.module('app', ['ui.router'], function($httpProvider) {
  // 设置 Content-Type
  $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
  // 序列化方法
  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;
  };
  $httpProvider.defaults.transformRequest = [function(data) {
    return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data;
  }];
});
// 配置路由
app.config(function($stateProvider, $$urlRouterProvider) {
  // ...
});
接下来我们在另外一个controller中发送一个请求:
angular.module('demoController', [])
  .controller('demoPageCtrl', ['$scope', '$http', function($scope, $http) {
    // 要提交的数据
    var data = {foo: "hi there", bar: { blah: 123, quux: [1, 2, 3] }};
    $http({
      method: 'POST',
      url: '/api/user/2',
      data: data,
    }).success(function(rep) {
      alert('数据提交成功!');
    });
  }]);
最终我们提交的数据被序列化成了:foo=hi%20there&bar%5Bblah%5D=123&bar%5Bquux%5D%5B0%5D=1&bar%5Bquux%5D%5B1%5D=2&bar%5Bquux%5D%5B2%5D=3,我们使用decodeURIComponent()解码后为foo=hi there&bar[blah]=123&bar[quux][0]=1&bar[quux][1]=2&bar[quux][2]=3,格式完全Ok。
回顾上面所做的工作,在app中把POST提交数据格式改成了application/x-www-form-urlencoded,然后在数据发送前进行了序列化。接下我们在隶属app下面的所有controller中提交数据都是以 Form Data 的形式发送,但是如果想在某个单独提交时还是使用application/json格式,我们立刻能想到的是再在每个config中设置一次头部,方式如下:
var data = {foo: "hi there", bar: { blah: 123, quux: [1, 2, 3] }};
$http({
  method: 'POST',
  url: '/api/user/2',
  data: data,
  headers: {
    'Content-Type': 'application/json;charset=utf-8'
  }
}).success(function(rep) {
  alert('数据提交成功!');
});
哎呀,怎么失败了,马上查看服务器返回的错误信息:Error: invalid json,然后再看看发送过去的数据。哦!原来我们发送过去的是序列化后的数据。然后回头看看我们下面这三行代码后知道怎么做了
$httpProvider.defaults.transformRequest = [function(data) {
    // PS: 只有当传入的参数为一个对象时才会序列化
    return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data;
  }];
然后解决办法是使用JSON.stringify()直接把要发送的数据序列化为JSON字符串,这样服务器就能够正常解析了
var data = {foo: "hi there", bar: { blah: 123, quux: [1, 2, 3] }};
$http({
  method: 'POST',
  url: '/api/user/2',
  data: JSON.stringify(data), // 序列化为json字符串
  headers: {
    'Content-Type': 'application/json;charset=utf-8'
  }
}).success(function(rep) {
  alert('数据提交成功!');
});
使用拦截器(interceptors)
拦截器尽管名字听起来有点高大上,但是只是一个$http服务的基础中间件。首先我们将$httpProvider对象输出到控制台
{
  "defaults": {
    "transformResponse": [null],
    "transformRequest": [null],
    "headers": {
      "common": {
          "Accept": "application/json, text/plain, */*"
      },
      "post": {
          "Content-Type": "application/json;charset=utf-8"
      },
      "put": {
          "Content-Type": "application/json;charset=utf-8"
      },
      "patch": {
          "Content-Type": "application/json;charset=utf-8"
      }
    },
    "xsrfCookieName": "XSRF-TOKEN",
    "xsrfHeaderName": "X-XSRF-TOKEN",
    "paramSerializer": "$httpParamSerializer"
  },
  "interceptors": [],
  "$get": ["$httpBackend", "$$cookieReader", "$cacheFactory", "$rootScope", "$q", "$injector", null]
}
可以看到$httpProvider中有一个interceptors数组,而所谓的拦截器也就只是一个简单注入到了该数组中的常规服务工厂。
var app = angular.module('app', []);
app.factory('myInterceptor', function($log) {
    $log.debug('$log输出啦。。。');
    var myInterceptor = {
      response: function(rep) {
        console.log(rep);
        return rep;
      }
    }
  });
app.config(function($httpProvider) {
  $httpProvider.interceptors.push('myInterceptor');
});
当我们在页面中向后端发起http请求时就可以看控制台中输出了我们的调试信息和服务器返回的数据。
一共有四种拦截器,两种成功拦截器,两种失败拦截器。
- request
该方法会在$http发送请求到后台之前执行,因此可以在这里对设置对象进行修改,或者创建一个新的设置对象,它需要返回一个更新过的设置对象,或者一个可以返回新的设置对象的promise。
app.factory('myInterceptor', function($q) {
  var interceptors = {
    request: function(req) {
      // 修改 Content-Type
      req.headers['Content-Type'] = 'application/json;charset=utf-8';
      // 替换掉要发送的数据
      req.data = {'so', 'magic'};
      // 返回修改后的对象
      return req; // 或者 $q.when(req)
    }
  };
  return interceptors;
});
- response
该方法会在$http接收到从后台过来的响应之后执行,它可以对响应进行修改,或者创建一个新的响应,它需要返回一个更新过的响应,或者一个可以返回新响应的promise。
- requestError
Angular会在上一个请求拦截器抛出错误,或者promise被reject时调用此拦截器。
- responseError
Angular会在上一个响应拦截器抛出错误,或者promise被reject时调用此拦截器。
由于作者本人经验不足,对拦截器就介绍一点,感兴趣的读者可以移步angularjs中的interceptor和挺好的例子深入了解。
封装$http服务,统一管理请求url
Angular为我们提供了一个轻量级的Promise API实现:$q服务,它很好的和$http,'$resource'以及Angular的渲染体系结合在一起。在文章前部分提到过$http调用会返回一个promise对象,因此可以充分利用$q服务提供的方法对返回数据做更加灵活的处理。
在实际项目中,我们还可以对$http服务作一层封装,比如封装成HttpService,然后可以将服务器返回的错误信息进行一次拦截处理,提供统一的错误提示。封装示例如下:
angular.module('httpService', [])
  .service('HttpService', function($http, $q) {
    var service = {
      get: function(url, config) {
        return handleRepData('get', url, null, config);
      },
      post: function(url, data, config) {
        return handleRepData('post', url, data, config);
      },
      put: function(url, data, config) {
        return handleRepData('post', url, data, config);
      },
      delete: function(url, config) {
        return handleRepData('delete', url, null, config);
      }
    }
    function handleRepData(method, url, data, config) {
      var promise;
      var defer = $q.defer();
      switch (method) {
        case 'get':
          promise = $http.get(url, config);
          break;
        case 'post':
          promise = $http.post(url, data, config);
          break;
        case 'put':
          promise = $http.put(url, data, config);
          break;
        case 'delete':
          promise = $http.delete(url, config)
      }
      promise.then(function(rep) {
        if (rep.data.success || rep.data.status === 1) {
          defer.resolve(rep.data);
        } else {
          var errorMsg = rep.data.message || '哦,出错啦!但是后端没有给任何信息。';
          // 弹出错误信息,或者重定向到404页面
          alert(errorMsg);
        }
      }, function() {
        defer.reject('出错了');
      })
      return defer.promise;
    }
    return service;
  });
然后,将所有的请求url按项目统一放置在一个服务下面:
angular.module('urlService', [])
  .factory('UrlService', function(HttpService) {
    var service = {
      // 数据源
      cms: {
        dataSource: {
          // 新建
          new: function(params_) {
            return HttpService.post('/api/datasource/', params_).then(function(data_) {
              return data_;
            });
          },
          // 更新
          update: function(params_, name_) {
            return HttpService.put('/api/datasource/' + name_ +'/', params_).then(function(data_) {
              return data_;
            });
          },
          // 详情
          detail: function(params_, name_) {
            return HttpService.get('/api/datasource/'+ name_ +'/', {params: params_}).then(function(data_) {
              return data_;
            });
          },
          // 删除
          delete: function(params_, name_) {
            return HttpService.delete('/api/datasource/'+ name_ +'/', params_).then(function(data_) {
              return data_;
            });
          },
          {
            // ...
          }
        }
      },
      // 店铺管理
      store: {
        shop: {
          // 新建
          create: function(params_) {
            return HttpService.post('/api/shop/create/', params_).then(function(data_) {
              return data_;
            });
          },
          // 查重
          checkDuplicate: function(params_) {
            return HttpService.post('/api/shop/search_duplicate/', params_).then(function(data_) {
              return data_;
            });
          },
          // 搜索
          search: function(params_) {
            return HttpService.get('/api/shop/search/', params_).then(function(data_) {
              return data_;
            });
          },
          {
            // ...
          }
        }
      },
      {
        // ...
      }
    };
    return service;
  });
最后我们在控制器中引入UrlService服务:
angular.module('cmsDataSourceController', [])
  .controller('cmsDataSourcePageCtrl', ['$scope', 'UrlService', function($scope, UrlService) {
    var params = {name: name, alias: alias, cate: cate, conds: conds, tags: tags};
    UrlService.cms.dataSource.new(params).then(function(rep) {
      tips('数据源创建成功!');
      $('.btn-update').text('修改数据源');
    });
  }]);
总结
在处理ajax的过程中,涉及到的东西还远远不止这些,本文只是初步了探讨了Angular中$http服务使用方法,分享了自己在公司中学到的一点点实战经验。
文章参考
- How can I post data as form data instead of a request payload?
- Understanding angular $http interceptors
- angularjs中的interceptor和挺好的例子
- $http 服务
- Make AngularJS $http service behave like jQuery.ajax()
Angular服务器通信之:$http的更多相关文章
- AngularJs 与服务器通信 $http, $q, $resource
		$http服务是AngularJS系统自带的,可以用来进行网络通信.获取远程服务器的数据.要记住的是,$http是对浏览器XMLHttpRequest的封装,也就是说,它其实是Ajax. $http( ... 
- Google Play Store —与google服务器通信时出现问题
		机子:MX4 前几天刷完机后出现登录Google Play Store “与google服务器通信时出现问题”,今天试了好几种方法,来总结一下 1.修改最新Hosts文件 2.SmartHosts ... 
- Android操作HTTP实现与服务器通信(转)
		Android操作HTTP实现与服务器通信 本示例以Servlet为例,演示Android与Servlet的通信. 众所周知,Android与服务器通信通常采用HTTP通信方式和Socket通信方 ... 
- vuejs与服务器通信
		vuejs与服务器通信 与服务器通信 Vue 实例的原始数据 $data 能直接用 JSON.stringify() 序列化.社区贡献了一个插件 vue-resource,提供一种容易的方式与 RES ... 
- 通信服务器群集——跨服务器通信Demo(源码)
		对于一些基于TCP Socket的大型C/S应用来说,能进行跨服务器通信可能是一个绕不开的功能性需求.出现这种需求的场景类似于下面描述的这种情况. 假设,我们一台TCP应用服务器能同时承载10000人 ... 
- 无法在Web服务器上启动调试,与Web服务器通信时出现身份验证错误
		问题描述: 我使用的是修改hosts,模拟真实网址来进行调试的.具体是这样的:我修改hosts文件,把某个域名,如www.163.com映射为127.0.0.1,然后在IIS信息管理器中,创建一个网站 ... 
- 客户端(android,ios)与服务器通信
		android,ios客户端与服务器通信为了便于理解,直接用PHP作为服务器端语言 其实就是一个 http请求响应的过程序,先从 B/S模式说起浏览器发起http请求,服务器响应请求,并把数据返回给浏 ... 
- [转]浏览器如何和Web服务器通信
		http://hi.baidu.com/ywqme/item/b5297014b2e58f4e6826bb74 概述 普通网民打开网页,访问网站,并不需要了解所谓HTTP协议.作为软件工程师,了解一下 ... 
- 使用ajax与服务器通信的步骤
		使用ajax与服务器通信的步骤: 1. 创建一个XMLHttpRequest对象 2. 创建url,data,通过xmlHttpRequest.send() 3. 服务器端接收ajxa的请求,做相应处 ... 
随机推荐
- 第四题 (List)写一个函数reverseList,该函数能够接受一个List,然后把该List 倒序排列。 例如:  List list = new ArrayList();  list.add(“Hello”);  list.add(“World”);  list.add(“Learn”); //此时list 为Hello World Learn  r
			package zuoye; import java.util.ArrayList; import java.util.List; public class Reverse01 { public st ... 
- pc打开手机站提示切换为手机屏幕
			.turn { position: absolute; width: 100%; height: 100%; left:; top:; background: url(../images.png) c ... 
- Android Binder机制原理(史上最强理解,没有之一)(转)
			原文地址: http://blog.csdn.net/universus/article/details/6211589 Binder是Android系统进程间通信(IPC)方式之一.Linux已经拥 ... 
- 常用CSS样式 持续更新
			+ CSS + a标签 - 去除a标签下划线 a{ text-decoration:none; } - 未被访问状态下的a标签去除下划线 a:link{ text-decoration:none; } ... 
- TCP/IP体系结构-测试人员必须理解的
			如果还想在测试这条路上继续走下去的话,那么下面这些东西就是我们必须去掌握的,至少你还不想止步于简单的黑盒测试--其实,一直想去接触Linux下的应用测试,这样能学到东西会很多,而且会非常的受用.之前听 ... 
- Q promise API简单翻译
			详细API:https://github.com/kriskowal/q/wiki/API-Reference Q提供了promise的一种实现方式,现在在node中用的已经比较多了.因为没有中文的a ... 
- Linux SSH使用公钥私钥实现免登陆
			公钥和私钥(我是文盲,钥字之前都是读yao,这是多音字这里应该念yue),是成对出现的,一旦任何一个做了更改都会验证失败. 1.免登陆的实现: 使用下例中ssky-keygen和ssh-copy- ... 
- javase swing
			package com.test; import java.awt.BorderLayout; import java.awt.Color; import java.awt.event.ActionE ... 
- ASP.NET网站限制访问频率
			最近做了一个免费发短信的小网站(http://freesms.cloudapp.net/),但发现最近有人破解了我的验证码,以每3秒/条的速度用我的短信服务来发他的广告.更换验证码程序和过滤关键字只是 ... 
- Objective-C对象模型及应用
			引言 简介 与Runtime交互 Runtime术语 消息 动态方法解析 消息转发 健壮的实例变量(Non Fragile ivars) Objective-C Associated Objects ... 
