接口调用方式
-
前后端数据交互接口调用方式
- 原生AJAX
- 基于jQuery的AJAX
- Promise
- fetch API 接口调用
- axios 接口调用
- async/await 接口调用
-
URL地址格式
- 传统形式的URL
schema://host:port/path?query#fragment
schema
协议。例如http https ftp
等host
域名或者IP地址port
端口,http默认端口80,可以省略path
路径,例如/abc/a/b/cquery
查询参数,例如:uname=sunny&age=12fragment
锚点(哈希Hash),用于定位页面的某个位置
- Resful形式的URL
- HTTP请求方式
GET
查询POST
添加PUT
修改DELETE
删除
- HTTP请求方式
- 传统形式的URL
异步编程
- 异步
- JavaScript的执行环境是「单线程」
- 所谓单线程,是指JS引擎中负责解释和执行JavaScript代码的线程只有一个,也就是一次只能完成一项任务
- 这个任务执行完后才能执行下一个,它会「阻塞」其他任务。这个任务可称为主线程
- 异步模式可以一起执行多个任务
- JS中常见的异步调用
- 定时任务
- ajax
- 事件函数
- 多次异步调用的依赖分析
- 多次异步调用的结果顺序不确定
- 异步调用结果如果存在依赖,需要嵌套
// 回调地狱
$.ajax({
url: 'http://localhost:3000/data',
success: function(data){
console.log(data);
$.ajax({
url: 'http://localhost:3000/data1',
success: function(data){
console.log(data);
$.ajax({
url: 'http://localhost:3000/data2',
success: function(data){
console.log(data);
}
});
}
});
}
});
异步编程Promise
用法详解
-
Promise
概述Promise
是异步编程的一种解决方案- 从语法上讲,Promise是一个对象,从它可以获取异步操作的消息
-
Promise
好处- 可以避免多层异步调用嵌套问题(回调地狱)
- Promise对象提供了简介的API,使得控制异步操作更加容易
Promise基本使用
-
Promise基本用法
- 实例化
Promise
对象,构造函数中传递函数,该函数中用于处理异步任务。 resolve
和reject
两个参数用于处理成功和失败两种情况,并通过p.then
获取处理结果
- 实例化
-
Promise用法步骤
- Promise的调入
- 我们使用new来构建一个Promise,Promise的构造函数接收一个参数,是函数
- 并且传入两个参数:resolve,reject,分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数
- 在函数体内实现异步任务
- 正常情况,resolve()输出结果
- 异常情况,reject()输出结果
- Promise实例生成以后,可以用then方法指定resolved状态和reject状态的回调函数
- 在then方法中,可以直接return数据而不是Promise对象,在后面的then中就可以接收到数据了
- Promise的调入
// Promise基本使用
var p = new Promise(function(resolve, reject){
// 这里用于实现异步任务
setTimeout(function(){
var flag = true;
if(flag) {
resolve('hello');
}else{
reject('出错了');
}
}, 100);
});
p.then(function(data){
console.log(data);
},function(info){
console.log(info);
})
Promise发送Ajax请求并处理回调地狱问题
- 基于Promise发送Ajax请求
- 关于
then()
上一个then()
的return
值是下一个then()
的调用值 - then方法指定resolved状态和reject状态的回调函数
then()
的返回值可以是一个Promise对象,也可以是一个非Promise对象
- 关于
function queryData(url) {
// 1.1 创建一个Promise实例
var p = new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState != 4) return;
if(xhr.readyState == 4 && xhr.status == 200) {
// 1.2 处理正常的情况
resolve(xhr.responseText);
}else{
// 1.3 处理异常情况
reject('服务器错误');
}
};
xhr.open('get', url);
xhr.send(null);
});
return p;
}
// 注意:这里需要开启一个服务
// 在then方法中,你也可以直接return数据而不是Promise对象
// 在后面的then中就可以接收到数据了
queryData('http://localhost:3000/data0')
.then(function(data){
console.log(data)
// 1.4 想要继续链式编程下去 需要 return
return queryData('http://localhost:3000/data1');
},function(info){
console.log(info);
return queryData('http://localhost:3000/data1');
})
.then(function(data){
console.log(data);
return queryData('http://localhost:3000/data2');
},function(info){
console.log(info);
return queryData('http://localhost:3000/data2');
})
.then(function(data){
console.log(data);
return '数据调用结束!';
},function(info){
console.log(info);
return '数据调用结束!';
})
.then(function(data){
console.log(data);
});
then方法参数中函数的返回值
-
返回Promise实例对象
- 返回的该实例对象会调用下一个then
-
返回普通值
- 返回的普通值会直接传递给下一个then,通过then参数中函数的参数接受该值
function queryData(url) {
// 1.1 创建一个Promise实例
return new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState != 4) return;
if(xhr.readyState == 4 && xhr.status == 200) {
// 1.2 处理正常的情况
resolve(xhr.responseText);
}else{
// 1.3 处理异常情况
reject('服务器错误');
}
};
xhr.open('get', url);
xhr.send(null);
});
}
// 注意:这里需要开启一个服务
// 在then方法中,你也可以直接return数据而不是Promise对象
// 在后面的then中就可以接收到数据了
queryData('http://localhost:3000/data')
.then(function(data){
return queryData('http://localhost:3000/data1');
})
.then(function(data){
return new Promise(function(resolve, reject){
setTimeout(function(){
resolve('数据调用成功!);
},1000)
})
})
.then(function(data){
console.log(data);
return '数据调用结束!'
})
.then(function(data){
console.log(data);
});
Promise常用API
- 实例方法
p.then()
得到异步任务的正确结果p.catch()
获取异常信息p.finally()
成功与否都会执行(尚且不是正式标准)
function foo() {
return new Promise(function(resolve, reject){
setTimeout(function(){
// resolve(123);
reject('error');
}, 100);
})
}
foo()
.then(function(data){
console.log(data)
})
.catch(function(data){
console.log(data)
})
.finally(function(){
console.log('finished')
});
// 等效写法
function foo() {
return new Promise(function(resolve, reject){
setTimeout(function(){
// resolve(123);
reject('error');
}, 100);
})
}
foo()
.then(function(data){
// 得到异步任务正确的结果
console.log(data)
},function(data){
// 获取异常信息
console.log(data)
})
// 成功与否都会执行(不是正式标准)
.finally(function(){
console.log('finished')
});
-
对象方法
Promise.all()
并发处理多个异步任务,所有任务都执行完成才能得到结果Promise.race()
并发处理多个异步任务,只要有一个任务完成就能得到结果
-
Promise.all()
- 此方法接受一个数组作参数,数组中的对象(p1、p2、p3)均为promise实例
- 如果不是一个promise,该项会被用
Promise.resolve
转换为一个promise - 它的状态由这三个promise实例决定
-
Promise.race()
- 此方法同样接受一个数组作参数。
- 当p1, p2, p3中有一个实例的状态发生改变(变为
fulfilled
或rejected
),p的状态就跟着改变。 - 并把第一个改变状态的promise的返回值,传给p的回调函数
function queryData(url) {
return new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState != 4) return;
if(xhr.readyState == 4 && xhr.status == 200) {
// 处理正常的情况
resolve(xhr.responseText);
}else{
// 处理异常情况
reject('服务器错误');
}
};
xhr.open('get', url);
xhr.send(null);
});
}
var p1 = queryData('http://localhost:3000/data');
var p2 = queryData('http://localhost:3000/data1');
var p3 = queryData('http://localhost:3000/data2');
// all中的参数[p1,p2,p3]和返回的结果一一对应
// ["HELLO TOM", "HELLO JERRY", "HELLO SPIKE"]
Promise.all([p1, p2, p3]).then(function(res){
console.log(res);
});
//由于p1执行较快,Promise的then()将获得结果
// 'P1'。p2,p3仍在继续执行,但执行结果将被丢弃。
Promise.race([p1, p2, p3]).then(function(res){
console.log(res);
});
FetchAPI概述与基本使用
-
Fetch概述
- 更加简单的数据获取方式,功能更强大、更灵活,可以看作是xhr的升级版
- 基于Peomise实现的
fetch
就是ajax+Promise
使用的方式- 和
jquery
提供的$.ajax()
差不多 - fetch默认是get请求
-
语法结构
- Fetch API是新的ajax解决方案 Fetch会返回Promise
- fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象
- fetch(url, options).then()
fetch(url).then(fn2)
.then(fn3)
.then(fn)
接口调用fetch用法
fetch
基本用法- 第一个参数是请求的路径,Fetch会返回Promise所以我们可以使用then 拿到请求成功的结果
- text()方法属于fetchAPI的一部分,它返回一个Promise实例对象,用于获取后台返回的数据
fetch('http://localhost:3000/fdata')
.then(function(data){
return data.text();
})
.then(function(data){
console.log(data);
})
fetch请求参数
-
常用配置选项
- method(String):HTTP请求方法,默认为GET(GET、POST、PUT、DELETE)
- body(String):HTTP的请求参数
- headers(Object):HTTP的请求头,默认为{}
-
fetch API中的HTTP请求
fetch(url, options).then()
- HTTP协议,它给我们提供了很多的方法,如POST,GET,DELETE,UPDATE,PATCH和PUT
- 默认的是 GET 请求
- 需要在
options
对象中指定对应的method
[method:请求使用的方法] post
和普通请求的时候,需要在options
中设置请求头headers
和body
-
服务器端配置方面
- 若是出现
application
方面的错误- 删除
node_modules
文件夹和package-lock.json
文件 - 在此文件夹下的
shell
中重新安装npm install
- 删除
- 对于同等级的请求头建议使用数组
- 'Access-Control-Allow-Headers', ['Content-Type', 'mytoken']
- 若是出现
const express = require('express')
const app = express()
const bodyParser = require('body-parser')
// 处理静态资源
app.use(express.static('public'))
// 处理参数 获取POST过来的数据
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// 设置允许跨域访问该服务
app.all('*', function (req, res, next) {
res.header("Access-Control-Allow-Origin", '*');
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header('Access-Control-Allow-Headers', ['Content-Type', 'mytoken']);
// res.header('Access-Control-Allow-Headers', 'mytoken'); //出错根源
next();
});
// 路由代码
// 启动监听
app.listen(3000, () => {
console.log('网络服务器已运营...')
})
- GET请求方式的参数传递
// 案例对比
fetch('/abc?id=123')
.then(data => {
return data.text();
}).then(ret => {
// 注意:这里是刚才得到的数据
console.log(ret);
})
fetch('/abc/123', {
method: 'get'
}).then(data => {
return data.text();
}).then(ret => {
// 注意:这里是刚才得到的数据
console.log(ret);
})
// GET参数传递 - 传统URL 通过url ?的形式传参
fetch('http://localhost:3000/books?id=123', {
method: 'get' // get 请求可以省略不写 默认的是GET
})
.then(function(data) {
// 它返回一个Promise实例对象,用于获取后台返回的数据
return data.text();
}).then(function(data) {
// 在这个then里面我们能拿到最终的数据
console.log(data)
});
// 后台路由接口
app.get('/books', (req, res) => {
res.send('传统的URL传递参数!' + req.query.id)
})
// GET参数传递 - restful形式的URL 通过 / 的形式传递参数
// 即 id = 456 和id后台的配置有关
fetch('http://localhost:3000/books/456', {
method: 'get' // get 请求可以省略不写 默认的是GET
})
.then(function(data) {
return data.text();
}).then(function(data) {
console.log(data)
});
// 后台路由接口
app.get('/books/:id', (req, res) => {
res.send('Restful形式的URL传递参数!' + req.params.id)
})
- DELETE请求方式的参数传递
// DELETE请求方式参数传递
// 删除id 是 id=789
fetch('http://localhost:3000/books/789', {
method: 'delete'
})
.then(function(data) {
return data.text();
}).then(function(data) {
console.log(data)
});
// 后台路由接口
app.delete('/books/:id', (req, res) => {
res.send('DELETE请求传递参数!' + req.params.id);
})
- POST请求方式的参数传递
// POST请求方式参数传递
fetch('http://localhost:3000/books', {
method: 'post',
body: 'uname=sunny&pwd=123', // 传递数据
headers: { // 设置请求头
'Content-Type': 'application/x-www-form-urlencoded' // 指定提交方式为表单提交
}
})
.then(function(data) {
return data.text();
}).then(function(data) {
console.log(data);
});
// 后台路由接口
app.post('/books', (req, res) => {
res.send('POST请求传递参数!' + req.body.uname + '---' + req.body.pwd);
})
// POST请求方式参数传递
fetch('http://localhost:3000/books', {
method: 'post',
body: JSON.stringify({
uname: 'sunny',
pwd: '456'
}),
headers: {
'Content-Type': 'application/json'
}
})
.then(function(data) {
return data.text();
}).then(function(data) {
console.log(data);
});
// 后台路由接口
app.post('/books', (req, res) => {
res.send('POST请求传递参数!' + req.body.uname + '---' + req.body.pwd);
})
- PUT请求方式的参数传递
- 和
POST
方式类似,也是两种方式
- 和
// PUT请求方式参数传递
fetch('http://localhost:3000/books/123', {
method: 'put',
body: JSON.stringify({
uname: '张三',
pwd: '789'
}),
headers: {
'Content-Type': 'application/json'
}
})
.then(function(data) {
return data.text();
}).then(function(data) {
console.log(data)
});
// 后台路由接口
app.put('/books/:id', (req, res) => {
res.send('PUT请求传递参数!' + req.params.id + '---' + req.body.uname + '---' + req.body.pwd)
})
FetchAPI响应数据格式
-
fetch API中的响应格式
- 用fetch来获取数据,如果响应正常返回,我们首先看到的是一个response对象
- 其中包括返回的一堆原始字节
- 这些字节需要在收到后,需要我们通过调用方法将其转换为相应格式的数据
- 比如JSON,BLOB或者TEXT等等
-
响应数据格式
text()
将返回体处理成字符串类型json()
返回结果和JSON.parse(responseText)一样
/* Fetch响应结果的数据格式 text() */
fetch('http://localhost:3000/json').then(function(data){
return data.text(); // // 将获取到的数据 转换成字符串
}).then(function(data){
var obj = JSON.parse(data);
console.log(data);
console.log(data.uname,data.age);
console.log(typeof data); // string
console.log(obj.uname,obj.age,obj.gender);
})
/* Fetch响应结果的数据格式 json() */
fetch('http://localhost:3000/json').then(function(data){
return data.json(); // 将获取到的数据使用 json 转换对象
}).then(function(data){
console.log(data);
console.log(data.uname);
console.log(typeof data); // object
})
axios概述与基本用法
axios
基本特性- 基于promise用于浏览器和node.js的http客户端
- 支持浏览器和node.js
- 支持promise
- 能拦截请求和响应
- 自动转换JSON数据
- 能转换请求和响应数据
axios基本用法
-
get
和delete
请求传递参数- 通过传统的url 以 ? 的形式传递参数
- restful 形式传递参数
- 通过params 形式传递参数
-
post
和put
请求传递参数- 通过选项传递参数
- 通过
URLSearchParams
传递参数
-
发送
get
请求- 引入库文件
<script src='./js/axios.js'></script>
- 拿到
ret
是一个对象,所有的对象都存在ret
的data
属性里面 - 注意:
data
属性是固定的用法,用于获取后台的实际数据
- 引入库文件
// 发送get请求
axios.get('http://localhost:3000/adata')
.then(function(ret){
console.log(ret.data);
console.log(ret);
})
// 后台接口代码
app.get('/adata', (req, res) => {
res.send('Hello axios!');
})
axios常用API
-
常用API
get
查询数据post
添加数据put
修改数据delete
删除数据
-
get
请求传递参数- 通过传统的url 以 ? 的形式传递参数
restful
形式传递参数- 通过
params
形式传递参数
// 通过传统的url 以 ? 的形式传递参数
axios.get('http://localhost:3000/axios?id=123')
.then(function(ret){
console.log(ret.data);
})
// 后台代码
app.get('/axios', (req, res) => {
res.send('axios get 传递参数' + req.query.id)
})
// restful 形式传递参数
axios.get('http://localhost:3000/axios/123')
.then(function(ret){
console.log(ret.data);
})
// 后台代码
app.get('/axios/:id', (req, res) => {
res.send('axios get (Restful) 传递参数' + req.params.id)
})
// 通过params 形式传递参数
axios.get('http://localhost:3000/axios', {
params: {
id: 789
}
}).then(function(ret){
console.log(ret.data)
})
// 后台代码
app.get('/axios', (req, res) => {
res.send('axios get 传递参数' + req.query.id)
})
delete
请求传递参数- 传参的形式和 get 请求一样
// 通过params 形式传递参数
axios.delete('http://localhost:3000/axios', {
params: {
id: 111
}
}).then(function(ret){
console.log(ret.data)
})
// 后台代码
app.delete('/axios', (req, res) => {
res.send('axios get 传递参数' + req.query.id)
})
post
请求传递参数- 通过选项传递参数
- 通过
URLSearchParams
传递参数
// 通过选项传递参数
axios.post('http://localhost:3000/axios', {
uname: 'sunny',
pwd: 123
}).then(function(ret){
console.log(ret.data);
})
// 后台代码
app.post('/axios', (req, res) => {
res.send('axios post 传递参数' + req.body.uname + '---' + req.body.pwd);
})
// 通过选项传递参数
var params = new URLSearchParams();
params.append('uname', 'Tom');
params.append('pwd', '025');
axios.post('http://localhost:3000/axios', params).then(function(ret){
console.log(ret.data);
})
// 后台代码
app.post('/axios', (req, res) => {
res.send('axios post 传递参数' + req.body.uname + '---' + req.body.pwd);
})
put
请求传递参数- 和
post
请求一样
- 和
// 通过选项传递参数
axios.put('http://localhost:3000/axios/123', {
uname: 'Jerry',
pwd: 123567
}).then(function(ret){
console.log(ret.data);
})
// 后台代码
app.put('/axios/:id', (req, res) => {
res.send('axios put 传递参数' + req.params.id + '---' + req.body.uname + '---' + req.body.pwd)
})
axios的响应结果
- 响应结果的主要属性
data
实际响应回来的数据headers
响应头信息status
响应状态码statusText
响应状态信息
// 响应 字符串 数据
axios.get('http://localhost:3000/axios?id=987')
.then(function(ret){
console.log(typeof ret.data);
console.log(ret.data);
console.log(ret);
})
// 后端代码
app.get('/axios', (req, res) => {
res.send('axios get 传递参数' + req.query.id)
})
// 响应 对象 数据
axios.get('http://localhost:3000/axios-json')
.then(function(ret){
console.log(typeof ret.data);
console.log(ret.data);
console.log(ret);
})
// 后端代码
app.get('/axios-json', (req, res) => {
res.json({
uname: 'Jerry',
age: 12
});
})
axios的全局配置
-
配置 默认地址
axios.defaults.baseURL = 'https://api.example.com';
-
配置 超时时间
axios.defaults.timeout = 2500;
-
配置 公共请求头
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
- 后台跨域配置
res.header('Access-Control-Allow-Headers', ['Content-Type', 'mytoken']);
- 后台跨域配置相同的键值用数组包含,单独写很可能会出错
-
配置 公共的
post
的Content-Type
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
/* axios响应结果与全局配置 */
// 配置 超时时间
axios.defaults.timeout = 10;
// 配置请求的基准URL地址
axios.defaults.baseURL = 'http://localhost:3000';
// 配置请求头信息
axios.defaults.headers.common['mytoken'] = 'hello';
axios.get('axios-json')
.then(function(ret){
console.log(ret.data);
})
axios拦截器
- 请求拦截器
- 请求拦截器的作用是在请求发送前进行一些操作
- 例如在每个请求体里加上token,统一做了处理如果以后要改也非常容易
axios.interceptors.request.use(function(config) {
console.log(config.url)
// 任何请求都会经过这一步 在发送请求之前做些什么
config.headers.mytoken = 'nihao';
// 这里一定要return 否则配置不成功
return config;
}, function(err){
// 对请求错误做点什么
console.log(err)
})
axios.get('http://localhost:3000/adata')
.then(function(data){
console.log(data);
})
- 响应拦截器
- 响应拦截器的作用是在接收到响应后进行一些操作
- 例如在服务器返回登录状态失效,需要重新登录的时候,跳转到登录页
axios.interceptors.response.use(function(res) {
// 在接收响应做些什么
var data = res.data;
return data;
}, function(err){
// 对响应错误做点什么
console.log(err)
})
axios.get('http://localhost:3000/adata')
.then(function(data){
console.log(data);
})
axios.interceptors.request.use(function(config) {
console.log(config.url);
// 任何请求都会经过这一步 在发送请求之前做些什么
config.headers.mytoken = 'success';
// 这里一定要return 否则配置不成功
return config;
}, function(err){
// 对请求错误做点什么
console.log(err)
})
axios.interceptors.response.use(function(res) {
// 在接收响应做些什么
var data = res.data;
console.log(data+ '响应拦截');
return data;
}, function(err){
// 对响应错误做点什么
console.log(err)
})
axios.get('http://localhost:3000/adata')
.then(function(data){
console.log(data);
})
异步函数async/await
概述与基本用法
async/await
概述async/await
是ES7引入的新语法,可以更加方便的进行异步操作async
关键字用于函数上(async函数的返回值是Promise实例对象)await
关键字用于async
函数当中(await可以得到异步的结果)
async function queryDate(id){
const ret = await axios.get('/data');
return ret;
}
queryData.then(ret=>{
console.log(ret);
})
async/await
基本用法
-
async
作为一个关键字放到函数前面- 任何一个
async
函数都会隐式返回一个promise
- 任何一个
-
await
关键字只能在使用async
定义的函数中使用- await后面可以直接跟一个 Promise实例对象
- await函数不能单独使用
-
async/await 让异步代码看起来、表现起来更像同步代码
- await函数不能单独使用,而且async函数返回的是一个Promise对象
- 可以使用then函数添加回调函数
- 当函数执行的时候,一旦遇到await函数就会先返回一个Promise对象
- 等到异步操作完成,再去执行后面的语句
axios.defaults.baseURL = 'http://localhost:3000';
/*
async function queryData(){
const ret = await axios.get('adata');
return ret.data;
}
*/
async function queryData(){
var ret = await new Promise(function(resolve, reject){
setTimeout(function(){
resolve('Hello');
},2000)
});
return ret;
}
queryData().then(function(data){
console.log(data);
})
async/await
处理多个异步请求
- 函数处理多个异步函数
- 添加await之后 当前的await 返回结果之后才会执行后面的代码
- 让异步代码看起来,表现起来更像同步代码
// 多次异步调用
axios.defaults.baseURL = 'http://localhost:3000';
async function queryData(){
var info = await axios.get('async1');
var ret = await axios.get('async2?info='+ info.data);
return ret.data;
}
queryData().then(function(data){
console.log(data);
})
// 后台路由代码
app.get('/async1', (req, res) => {
res.send('hello')
})
app.get('/async2', (req, res) => {
if(req.query.info == 'hello') {
res.send('world')
}else{
res.send('error')
}
})
案例:图书管理系统
-
图书相关的操作基于后台接口数据进行操作
-
需要调用接口的功能点
- 图书列表数据加载
GET http://localhost:3000/books
- 添加图书
POST http://localhost:3000/books
- 验证图书名称是否存在
GET http://localhost:3000/books/book/:name
- 编辑图书-根据ID查询图书信息
GET http://localhost:3000/books/:id
- 编辑图书-提交图书信息
PUT http://localhost:3000/books/:id
- 删除图书
DELETE http://localhost:3000/books/:id
- 图书列表数据加载