数据交互 axios
一、axios介绍
- 官网:http://www.axios-js.com/
- 看云:https://www.kancloud.cn/yunye/axios/234845(文档托管平台,有axios的详细使用)
- 概念
axios是基于Promise创建的http库,可以用于客户端(浏览器)和node.js let p1 = new Promise((resolve,reject)=>{ //异步代码 resolve(参数), reject(错误信息) }) p1.then((res)=>{ console.log(res);//成功的回调,调用resolve,通过res获取到传递过来的数据 }).catch((err)=>{ console.log(err); //失败的回调,调用reject,err错误信息 }) 官方概念: Axios是一个基于promise的HTTP库,可以在浏览器和node.js中使用
- 特点
- 从浏览器中创建XMLHttpRequests
- 从node.js中创建http请求
- 支持Promise API
- 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换JSON数据
- 客户端支持防御[XSRF]
跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web 应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定 网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。
- 下载(1的版本有问题,不稳定)
npm i axios@0 "axios": "^0.27.2
二、 axios基本语法
- 引入axios库文件,它会暴露一个==axios()方法,这个方法下面还有axios.get()和axios.post()==两个方法
2、1 axios({})
- 类似于$.ajax(),可以发起get或者post请求,返回promise对象
axios({
url: '你要请求的接口地址',
method: 'get/post', // 默认get// get传参
params: {
// 你要传入的get方式请求的入参
},
// post传参
data: {
// 你要传入的post方式请求的入参
}
}).then((res)=>{
// 成功时候的响应res,axios有一个特点,返回的数据包含很多的配置信息
// http状态码是200或者304,执行成功的逻辑(我们关心的是后端返回的res.code)
}).catch((err)=>{
// 错误时候的响应err
// http状态码非200或者304,执行错误的逻辑
})
2、2 axios.get(url,{})
- 类似于$.get(),发起get请求,注意传参比较特别
axios.get('你要请求的接口地址', { params: { // 你要传入的get方式请求的入参,如: a:1, b:2 } }).then((res)=>{ // 成功时候的响应res,axios有一个特点,返回的数据包含很多的配置信息 // http状态码是200或者304,执行成功的逻辑 }).catch((err)=>{ // 错误时候的响应err // http状态码非200或者304,执行错误的逻辑 })
2、3 axios.post(url,{})
- 类似于$.post(),发起post请求
除了data是真正后端返回的,其他的都是axios配置的axios.post('你要请求的接口地址', { // 你要传入的post方式请求的入参,如: a:1, b:2 }).then((res)=>{ // 成功时候的响应res,axios有一个特点,返回的数据包含很多的配置信息 // http状态码是200或者304,执行成功的逻辑 }).catch((err)=>{ // 错误时候的响应err // http状态码非200或者304,执行错误的逻辑 })
三、 基本案例
3、1 局部调用axios
- 代码
<template>
<div>
<h1>this is axios--局部使用</h1>
<ul>
<li v-for="item in datalist" :key="item.id">{{ item.name }}</li>
</ul>
</div>
</template>
<script>
//1.下载 npm i axios@0
//2.引入 (在那使用,在那引入--局部)
import axios from "axios";
export default {
data() {
return {
datalist: [],
};
},
//在created,beforeMount,mounted都可以发起ajax请求,一般在mounted发起ajax请求
mounted() {
//3.请求数据 axios({url,method,params:{}/data:{}})
// axios({
// url: "http://jsonplaceholder.typicode.com/comments", //请求地址
// method: "get", //请求方式 get和post 默认就是get
// params: { postId: 1 }, //get请求带参数,放在params中
// })
// .then((res) => {
// //请求成功
// console.log("请求成功");
// //res会有很多的配置信息,但是真正的数据在data属性中
// console.log(res.data);
// //4.请求到数据,赋值,渲染到页面
// this.datalist = res.data;
// })
// .catch((err) => {
// //请求失败
// console.log("请求失败");
// console.log(err); //错误信息
// });
//4.axios.get(url,{params:{}})
axios
.get("http://jsonplaceholder.typicode.com/comments", {
params: {
postId: 1,
},
})
.then((res) => {
//请求成功
console.log("请求成功");
//res会有很多的配置信息,但是真正的数据在data属性中
console.log(res.data);
//4.请求到数据,赋值,渲染到页面
this.datalist = res.data;
})
.catch((err) => {
//请求失败
console.log("请求失败");
});
},
};
</script>
<style>
</style>
3、2 全局调用axios
- 代码
定义成全局的 在main.js中
import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false
//1.全局引入axios
import axios from "axios";
//2.添加到vue原型上
Vue.prototype.$axios = axios;
new Vue({
router,
render: h => h(App)
}).$mount('#app')
在案例的vue文件中
<template>
<div>
<h1>this is axios -- 全局使用</h1>
<ul>
<li v-for="item in datalist" :key="item.id">{{ item.name }}</li>
</ul>
</div>
</template>
<script>
//基本上所有的组件都可能会有数据请求,如果每次使用都引入一次,麻烦
//定义成全局的
//1.全局引入axiox import axios from "axios";
//2.添加到vue原型上 Vue.prototype.$axios = axios;
//3.在组件中使用
export default {
data() {
return {
datalist: [],
};
},
mounted() {
this.$axios({
url: "http://jsonplaceholder.typicode.com/comments", //请求地址
method: "get", //请求方式 get和post 默认就是get
params: { postId: 1 }, //get请求带参数,放在params中
})
.then((res) => {
//请求成功
console.log("请求成功");
//res会有很多的配置信息,但是真正的数据在data属性中
console.log(res.data);
//4.请求到数据,赋值,渲染到页面
this.datalist = res.data;
})
.catch((err) => {
//请求失败
console.log("请求失败");
console.log(err); //错误信息
});
},
};
</script>
<style>
</style>
四、如何解决跨域问题
注意:开发阶段的跨域是前端自己解决,生产环境下的跨域后端解决
- vue的配置文档:https://cli.vuejs.org/zh/config
- 创建一个vue的全局配置 vue.config.js文件(在这里写node的语法),一定要和package.json同级才生效
- 注意:配置文件一旦修改必须要重启前端
4、1方式一(简单)
- vue.config.js
// 把你封装的所有配置文件全部导出去
module.exports = {
// publicPath 可以根据你不同的环境去配置它的初始地址,一般不会修改我们就'/'
// outputDir 输出目录,用于打包。执行npm run build 默认生成dist文件夹,如果你要修改成其他文件夹名就直接设置type类型
// devServer 本地开发服务配置
devServer:{
// 设置你的代理地址(这里写后端服务器的地址)
// 以后前端访问服务器地址时,前面直接写 /
// 后端地址本来是:http://localhost:3000/api/getbanner
// 会换成:http://localhost:8080/api/getbanner
// 这样就没有跨域问题了
// proxy即代理
proxy: 'http://localhost:3000'
}
}
- 说明
- 优点:配置简单,请求资源时直接发给前端(8080)即可。
- 缺点: 不能配置多个代理,不能灵活的控制请求是否走代理(如果前端public下有资源,会走前端自己的这个public下的资源,即public为根路径)
- 工作方式: :若按照上述配置代理,当请求了前端不存在的资源时,那么该请求才会转发给服务器 (优先匹配前端public下的资源)
- 操作步骤:
1.配置vue.config.js
2.请处理`http://localhost:3000/api/getbanner` 换成 `/api/getbanner`
3.重启前端
如果修改了配置文件,前端一定要重启
4、1方式二(推荐)
- vue.config.js
module.exports = {
devServer: {
proxy: {
'/api1': { // 匹配所有以'/api1'开头的请求路径。以/api1开头,是指端口后面是/api1,再接正常路径。/api1/api/getbanenr ==> /api/getbanenr
target: 'http://localhost:3000', // 代理目标的基础路径
changeOrigin: true,
secure: false, // 默认情况下,将不接受在 HTTPS 上运行且证书无效的后端服务器。 如果需要,可以这样修改配置
pathRewrite: {'^/api1': ''} // 后端收到的请求地址中,将'/api1'替换为''
//api/getbanner
},
'/api2': { // 匹配所有以'/api2'开头的请求路径
target: 'http://localhost:3001', // 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api2': ' '}
}
}
}
}
/*
changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:3000 或 3001
changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
changeOrigin默认值为true,在react中默认为false
*/
- 说明
- 优点:可以配置多个代理,且可以灵活的控制请求是否走代理
- 缺点:配置略微繁琐,请求资源时必须加前缀
- 如
module.exports = { devServer: { proxy: { '/ujiuye': { // 匹配所有以'/pzh'开头的请求路径。以/pzh开头,是指端口后面是/pzh,再接正常路径 // 这个路径会走代理:http://localhost:8080/ujiuye/api/getbanner // 后端收到的实际是:http://localhost:3000/api/getbanner target: 'http://localhost:3000', // 代理目标的基础路径 changeOrigin: true, pathRewrite: { '^/ujiuye': '' }, // 后端收到的请求地址中,将'/pzh'替换为'' }, }, }, };
- 步骤:
1.配置vue.config.js
2.请处理`http://localhost:3000/api/getbanner`换成`/pzh/api/getbanner`
3.重启前端
环境判断
let baseURL = ''; // 基础地址
// 区分清楚是生产环境还是开发环境console.log(process.env.NODE_ENV); // 返回开发环境和生产环境不同标识
// development(开发环境)
// production(生产环境)
if (process.env.NODE_ENV === 'development') {
// 开发环境baseURL = '/pzh/api';
} else if (process.env.NODE_ENV === 'production') {
// 生产环境baseURL = 'http://localhost:3000/api';
}
五、post的入参(请求)格式
- 主要有一下三种:依据Content-Type来区分
- 成熟的后端,第一种接收和第二种接收都应该设置好,第三种接收是接收上传文件
- 普通的方式(传对象)(request payload)
Content-Type: application/json 即数据以对象的形式传输。或者JSON.stringify()转的json对象字符串 {"phone":"155","password":"111"}
- 一般表单传参(模拟表单)(传查询字符串)(form-data)比较多用
注意点:它属于表单格式传参中没有上传文件的传输方式Content-Type: application/x-www-form-urlencoded 即数据以 'a=1&b=2' 上传
- 带有上传文件的表单传参(传文件)(利用form-data包装数据)
https://developer.mozilla.org/zh-CN/docs/Web/API/FormData
Content-Type: multipart/form-data
需要用到原生的FormData函数,将数据转成这种格式(FormData就是包装上传数据的一个容器)
// 语法
let file = new FormData(); // file 就是数据对象
// 添加数据file.append('key','value')
// 取值数据file.get('key')
==============================
// 如何将对象中的数据添加到FormData中呢,需要循环遍历
let file = new FormData()
for(let attr in 入参对象){
file.append(attr, 入参对象[attr])
}
总结:
前端不用手动设置Content-Type,但是传参的数据格式不一样,默认Content-Type也是不一样的
1、普通的方式,即传入一个对象 Content-Type: application/json
2、一般表单传参,即传入查询字符串(推荐) Content-Type: application/x-www-formurlencoded
3、带有上传文件的表单传参,即传FormData对象 Content-Type: multipart/form-data
- 第三方包qs进行数据格式转换
思考:我们的数据一般都是对象,如何将对象数据转成查询字符串这种格式 方式一: 利用原生node.js方法中querystring去转化 querystring.stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' }); // 返回 'foo=bar&baz=qux&baz=quux&corge=' 方式二: qs第三方包(在vue脚手架环境下,默认自带第三方包,直接引过来用即可,不用安装)
六、接口的封装
- 目的:分门别类,好维护,方便管理
- 概述:所谓的接口封装,就是单独定义出一个模块,专门用于管理整个网站的http请求,而不是每个组件中写请求,这样方便统一管理
- 步骤
1.在src下创建request文件夹 2.request 文件夹下会有`axios.js`和`api.js`两个文件 3.axios.js引入axios库并创建http实例,api.js引入axios.js中的 http 实例,然后其它的组件引入api.js中定义的方法
6、1 request/axios.js
- 作用:这个模块用于管理 axios,并设置基础的配置(比如说拦截器等)
- 注意:vue原型上就不需要再挂载axios了
- 基础款
import axios from 'axios'; // 引入axios,因此vue的原型上就不需要axios了 let baseURL = ''; // 基础地址 if (process.env.NODE_ENV === 'development') { // 开发环境 baseURL = '/ujiuye/api'; } else if (process.env.NODE_ENV === 'production') { // 生产环境 baseURL = 'http://localhost:3000/api'; } // 调用创建新实例 let http = axios.create({ // 在这里,定义当前实例的自定义配置 baseURL: baseURL // 基础地址,它可以统一管理你的接口地址 }); // 导出新实例 export default http;
- 完美款
更完美的写法:加上请求拦截和响应拦截import axios from 'axios'; // 引入axios,因此vue的原型上就不需要axios了 let baseURL = ''; // 基础地址 console.log(process.env.NODE_ENV); // 开发环境和生产环境 if (process.env.NODE_ENV === 'development') { // 开发环境,需要做跨域代理 baseURL = '/ujiuye/api'; } else if (process.env.NODE_ENV === 'production') // 生产环境,不用做跨域代理 baseURL = 'http://localhost:3000/api'; } // 调用创建新实例 let http = axios.create({ // 在这里,定义当前实例的自定义配置 baseURL: baseURL // 基础地址,它可以统一管理你的接口地址 }); // 请求拦载 interceptors拦截器 http.interceptors.request.use((req) => { return req; }); // 响应拦载 http.interceptors.response.use((res) => { return res; }); // 导出新实例 export default http;
6、2 request/api.js
- 作用:所有的请求都在这个模块,方便管理,而不用写在每个组件中
import http from './axios'; // 引入你封装好的axios实例 import qs from 'querystring'; // 引入原生node的querystring,用它下面的stringify方法,将{ foo: 'bar', corge: '123' }转换成'foo=bar&corge=123'。也可以用第三方包 // 下面封装一系列接口 // 登录接口,post方式,有参数 // 上传的数据格式可以用json,也可以改成查询字符串。这里就是改成了查询字符串,这个要看后端接口的需求 export function login(data) { return http.post('/login',qs.stringify(data)); } // 获取banner接口,get方式,没有要传的参数 export function getBanner() { return http.get('/getbanner'); } // 注册接口,post方式,有参数 export function register(data) { return http.post('/register',qs.stringify(data)); }
6、3 使用
- 从request的api.js中引入需要的方法,在某个组件中去使用
- 在组件中使用
<template> <div> <!-- 标题 --> <v-title></v-title> <p>电 话:<input type="text" v-model='user.phone'></p> <p>昵 称:<input type="text" v-model='user.nickname'></p> <p>密 码:<input type="text" v-model='user.password'></p> <p><button @click='goRegister'>注册</button></p> </div> </template> <script> import {register} from "@/request/api.js" export default { data(){ return { user:{ phone:"", nickname:"", password:"" } } }, methods:{ goRegister(){ //1.判断,必须输入了内容才能往下执行 if(this.user.phone == "" || this.user.password == "" || this.user.nickname == ""){ alert("请输入必要内容"); return; } //2.注册 // this.$axios.post("/ujiuye/api/register",this.user) register(this.user) .then(res=>{ if(res.data.code == 200){ alert(res.data.msg); this.$router.push("/login") }else{ alert(res.data.msg); } }).catch(err=>{ console.log(err); }) } } } </script> <style scoped> p{ line-height: .8rem; text-align: center; } </style>
七、axios的并发处理(axios.all())【了解】
- 所谓并发,即同时可以调用N个接口
- 方法:
axios.all([请求方法1(),请求方法2()]) .then(res=>{ res中有两次请求的数据 }) .catch(err=>{ 错误 })
可以用axios.spread()展开并发结果。spread的参数为一个回调函数,回调函数的参数即并发调用返回的一一对应的结果
- 例
//1、单独引入axios库,并发是在某个组件中使用,封装的http上面没有all方法 import axios from "axios"; //2.引入封装好的接口,这里引了两个接口 import { getHomeGoods, getHomeCate } from "../request/api; //3.发请求 axios .all([getHomeCate(), getHomeGoods()]) .then( // spread展开,它接收一个函数,该函数的参数即返回的结果 // cate即第一个请求的结果,goods即第二个请求的结果 axios.spread((cate, goods) => { // 对第一个处理 if (cate.data.code == 200) { this.catelist = cate.data.list; } else { alert(cate.data.msg); } // 对第二个处理 if (goods.data.code == 200) { this.hotslist = goods.data.list[0].content; this.newlist = goods.data.list[1].content; this.goodslist = goods.data.list[2].content; } else { alert(goods.data.msg); } }) } // 错误统一处理 .catch(err => { console.log(err, "错误"); });
八、拦截器
都写在request/axios.js中
import axios from 'axios'; // 引入axios,因此vue的原型上就不需要axios了
// 调用创建新实例的方法
let http = axios.create({
// 在这里,定义当前实例的自定义配置
baseURL: '/api' // 基础地址,它可以统一管理你的接口地址
});
// 请求拦截器:在req.headers中添加属性,一般用于添加token
http.interceptors.request.use((req) => {
// req.headers.pzh = 123; // req请求头,在请求头中,添加一个请求属性,因此在请求的网络中,就可以看到此请求属性(一般用于添加token)
return req;
});
// 响应拦截器:1、过滤数据 2、判断token是否过期等
http.interceptors.response.use((res) => {
// console.log(res.data); // 即后端返回来的数据
// 用法一、你可以全局操作返回的数据。比如后端返回来的数据经过包装,总包含一些不需要的东西,所以我们可以只把需要的返回出去
// return res.data;
// 二、全局错误判断(重点) 比如:token过期,如果过期,我们可以让它再到登录去
return res;
});
// 导出
export default http;
8、1 axios请求拦截和路由导航守卫的区别
axios是发起ajax请求时触发,一般用于添加token,全局错误处理,返回的数据过滤
导航守卫是访问某个路由时触发,一般用于判断是否登录,某个用户是否有访问某个路由的权限等
token的概念
- token就是令牌的意思。token从哪里产生?后端
- 实际上每一个必须要登录才能看见界面中的接口,都要验证一次用户名和密码,但是每一次验证都会给服务器造成压力,因此产生了token概念
- token是前端登录后,后端产生,传递给前端,前端将token存在本地存储中。当前端要访问某些需要登录才能访问的接口时,将它带着,这样后端就只需要验证token,而不用去数据库验证用户名和密码,这样减轻了后端的压力。
- token会失去它的时效,一般重要的页面3、5分钟