AngularJS能够通过XMLHttpRequest(XHR)和JSONP请求与各种后端交流,拥有通用的$http服务以进行XHR和JSONP调用,以及专门面向Restful后端接口的$resource服务
在本章中,将研究与后端通信的API与技术,特别是一下内容:
- 使用$http服务进行基本的XHR调用,已经进行相应的测试
- 利用$q服务提供的promise API进行有效的异步请求
- 使用$resource工厂轻松与RESTful后端进行沟通
- 根据后端需求构建自定义的与$resource类似的API
$http API快速导览
- | - |
---|---|
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: 调用目标URL
- data:请求体中送出的数据
- config:包含额外配置信息,对请求和响应都有影响
处理HTTP响应:请求可能成功或者失败,AngularJS提供两种方法以注册对应这两种结果的回调:success (200~299范围调用success,其余的状态吗则进入error)和error。
我们可以在调用$http方法返回的对象上,注册成功与失败的回调(callback)
- data:实际的响应数据
- status:响应的HTTP状态
- headers:访问HTTP响应头信息的函数
- config:请求触发时提供的配置对象
配置对象说明
- method:所用的HTTP方法
- url: 请求的目标URL
- params: URL参数
- headers: 额外的请求头
- timeout:XHR请求终止前的超时时间(毫秒)
- cache:XHR GET请求的缓存开关
- transformRequest\transferResponse:在于后端交换数据前或交换后,对数据进行处理的数据变换函数
var base_url = 'http://localhost:8080/'
angular.module('myapp1', [])
.controller('myController', function($scope, $http) {
var query = $http.get(base_url + "get");
query.success(function(data, status, headers, config) {
console.log(data);
$scope.response = data;
});
query.error(function(data, status, headers, config){
console.log('fail');
console.log(data);
});
var cars = {
toyota: {
from: 'japan',
price: 100000
},
benz: {
from: 'Germany',
price: 500000
}
}
//post和put的data接受任何Javascript对象(或者字符串),如果是对象则默认被转化为JSON字符串
var post = $http.post(base_url + '/post/car', cars);
//$http服务会视图对任何看起来像JSON的响应(以{},[])都执行从JSON字符串到Javascript对象的转化
post.success(function(data, status, headers, config) {
console.log(data);
});
});
为了测试简单的restful,你可以用sts创建一个简单点 springboot项目,随便写几个get,post的API如下:
public class HelloController {
@RequestMapping("/hello/{name}")
public String hello(@PathVariable("name") String name, Model model) {
model.addAttribute("name", name);
return "hello";
}
@RequestMapping("/get")
public @ResponseBody String hello(HttpServletResponse response) {
//为了解决cross domain的问题,你不需要allow *,你的index.html并不是部署在和springboot所在的localhost:8080的服务器上
response.addHeader("Access-Control-Allow-Origin", "*");
return "{\"firstName\": \"John\", \"lastName\": \"Smith\", \"age\": 25}";
}
@RequestMapping(value="/post/car", method=RequestMethod.POST)
public void post1(@RequestBody String body, HttpServletResponse response){
System.out.println(body);
response.addHeader("Access-Control-Allow-Origin", "*");
}
}
Promise API
promise API的主要思想是,为异步世界带来我们在同步编程世界中所享有的那些便利之处,比如链式方法调用和错误处理。
在Promise API中,通常有两个角色:一个是控制未来任务的执行(延迟调用)占位符(promise),领一个则是依赖于未来任务的执行结果(保证承诺结果,resovle、reject)
AngularJS拥有非常轻量化的Promise API实现–$q服务,位数不少的AngularJS服务(主要是$http, $timeout等)相当依赖promise API风格,所以为了有效运行这些服务,需要熟悉$q.
//此处省略$q的介绍,楼主暂时没看懂,而且觉得用处不太大,以后学了再补上。
Promise API与 http的关系, http返回的承诺对象为then,这让我们能以如下方式覆盖回调注册
var query = $http.get(base_url + "get");
query.then(function(data, status, headers, config) {
console.log(data);
$scope.response = data;
},
function(data, status, headers, config) {
console.log('fail');
console.log(data);
}
)
RESTful端点场提供CRUD操作,虽然这样的端点交互的代码通常很简单直接,不过写起来却很单调乏味。$resource服务允许我们消除重复代码,并让我们在更高的抽象级别上操作,用resources的形式去思考数据操纵,用方法调用来代替低级别的HTTTP调用。
$resource服务被分发在单独的文件中(angular-resource.js),属于专用模块(ngResource)。为了利用$resource服务,我们需要包含angular-resource.js文件,并在应用模块中声明对ngResource的模块依赖。
//HTML
<div ng-controller="myController">
<ul >
<li ng-repeat='user in users'>Name : {{user.name}}</li>
</ul>
</div>
//JS
var base_url = 'http://localhost:8080/'
angular.module('myapp1', ['ngResource'])
.factory('Users', function($resource) {
return $resource(base_url + 'get');
})
.controller('myController', function($scope, Users) {
$scope.users = Users.query();
})
resourceAPI通过 resource()方法来使用$resources服务。这个方法可以接受三个参数。
- url(必选)
我们在这里传入一个包含所有参数的,用来定位资源的参数化URL字符串模板(参数以“:”为前缀)
//这里需要注意的是如果:之前的参数是空的(:id),那么URL中的这部分会被压缩成一个.符号。
//如果我们使用的服务器要求在URL中输入端口号,例如:http://localhost:8080/:id 则必须对:转义
//http://localhost\:8080/:id
$resource('/api/users/:id.:format',{
format:'json',
id: '123'
});
- paramDefaults(可选)
第二个参数包含了发送请求时URL重参数的默认值。对象中的键会与参数名进行匹配。如果我们传入了一个没有在URL中设置过的参数,那它会以普通的查询字符串的形式被发送。
例如,如果URL字符串具有/api/users/:id这样的签名,并且我们将默认值设置为{id:’123’, name:’Ari’},那么URL最终被转化成/api/users/123?name=Ari
这里可以像上面一样硬编码一个值,也可以设置它从一个数据对象中读取动态值。 如果要设置动态之,需要在值之前加上@字符作为前缀
{id:’@_id.$oid’}
- actions(可选)
动作对象是具有自定义动作,并且可以对默认的资源动作进行扩展的hash对象。
对象的键就是自定义动作的名字,而$http设置的对象的值会对URL中相应的参数进行替换。
$resource('/api/users/:id.:format', {
format: 'json',
id: '123'
}, {
update: { methos: 'PUT' }
});
//action: {method:?, params:?, isArray:?, headers:?}
//isArray表示response是返回[]还是{}
$resource创建异步方法
$scope.users = Users.query();
console.log($scope.users.length); //这里不会获得你期望的数组长度,而会返回0
//因此你必须使用异步方法去做。
Users.query(function(users){
$scope.users = users;
console.log(users.length);
})
$resource服务自动生成两套方法。其中一套方法会在类级别,另一套方法会在实例级别
类级别
- Users.query(params, successcb, errorcb) : GET请求期望JSON数据返回,用于取得条目列表
- Users.get(params, successcb, errorcb): GET请求,期望JSON返回单个对象,用于取得单个条目
- Users.save(params,payloadData, successcb, errorcb): POST请求,请载荷中产生
- Users.delete(params, successcb, errrorcb):DELETE请求。
params孕育我们为每个动作制定参数,它们将成为URL的一部分,或者作为查询字符串中的参数。
实例级别
var user = new Users({
name : 'SuperHero'
});
user.$save();
user.$delete();
自定义方法
$resource工厂默认生成的方法对典型用例来说已经相当够用,但如果后端某些操作使用不同的HTTP动词(如PUT或者PATCH),那么在资源级别上添加自定义方法也相当容易。