5. 服务端交互
.env 文件
开始之前
框架中使用 Axios HTTP 库,您可能需要了解 vite 环境变量和模式章节
1. 配置文件有(框架中的根目录)
.env # 全局默认配置文件,不论什么环境都会加载合并
.env.development # 开发环境下的配置文件
.env.production # 生产环境下的配置文件
2. 命名规则
为了防止意外地将一些环境变量泄漏到客户端,只有以 VITE_
为前缀的变量才会暴露给经过 vite 处理的代码。例如下面这个文件中:
DB_PASSWORD = foobar
VITE_SOME_KEY = 123
只有 VITE_SOME_KEY
会被暴露为 import.meta.env.VITE_SOME_KEY
提供给客户端,而 DB_PASSWORD
则不会。
axios 封装
1. 框架中创建 axios
文件路径:/@/utils/request.ts
- 配置新建一个 axios 实例,对
import.meta.env.VITE_API_URL
不了解?请移步 env-文件
const service = axios.create({
baseURL: import.meta.env.VITE_API_URL as any,
timeout: 50000,
headers: { "Content-Type": "application/json" },
});
2. 添加请求拦截器
注意 axios 版本
v1.x.x 之后是没有 common
字段了,注意去 package.json 中查看 axios
版本
service.interceptors.request.use(
(config) => {
// 在发送请求之前做些什么 token
if (Session.get("token")) {
// config.headers!['Authorization'] = `${Session.get('token')}`;
config.headers.common["Authorization"] = `${Session.get("token")}`;
}
return config;
},
(error) => {
// 对请求错误做些什么
return Promise.reject(error);
}
);
3. 添加响应拦截器
注意高亮处
的判断,根据 后端接口返回的参数
做具体判断,否则可能 拿不到接口返回的数据
。此处根据需求自行修改,非固定。
service.interceptors.response.use(
(response) => {
// 对响应数据做点什么
const res = response.data;
if (res.code && res.code !== 0) {
// `token` 过期或者账号已在别处登录
if (res.code === 401 || res.code === 4001) {
Session.clear(); // 清除浏览器全部临时缓存
window.location.href = "/"; // 去登录页
ElMessageBox.alert("你已被登出,请重新登录", "提示", {})
.then(() => {})
.catch(() => {});
}
return Promise.reject(service.interceptors.response);
} else {
return res;
}
},
(error) => {
// 对响应错误做点什么
if (error.message.indexOf("timeout") != -1) {
ElMessage.error("网络超时");
} else if (error.message == "Network Error") {
ElMessage.error("网络连接错误");
} else {
if (error.response.data) ElMessage.error(error.response.statusText);
else ElMessage.error("接口路径找不到");
}
return Promise.reject(error);
}
);
使用说明
1. 统一 api 文件夹
/src
下新建 /src/api 文件夹。建议每一个模块,都新建一个文件夹(文件夹名称与模块名称相同,方便维护)。
如:login 模块
,api 文件夹下新建 /@/api/login
文件夹
2. 统一 api 管理
- 前端定义接口函数,格式看自己喜欢咋用就咋用,不一定非要使用以下写法格式。框架内目前写法 /src/api/login/index.ts
如:/@/api/login/index.ts
目录下,选择方法定义:
方法一
// 先引入经过自定义全局封装的 axios
import request from "/@/utils/request";
/**
* 用户登录
* @param params 要传的参数值
* @returns 返回接口数据
*/
export function signIn(params: object) {
return request({
url: "/user/signIn",
method: "post",
data: params,
});
}
方法二
// 先引入经过自定义全局封装的 axios
import request from "/@/utils/request";
export default function () {
/**
* 用户登录
* @param params 要传的参数值
* @returns 返回接口数据
*/
const signIn = (params: object) => {
return request({
url: "/user/signIn",
method: "post",
data: params,
});
};
// 这里继续添加
...
return {
signIn,
// 这里继续添加
...
};
}
方法三
// 先引入经过自定义全局封装的 axios
import request from "/@/utils/request";
/**
* 用户登录
* @param params 要传的参数值
* @returns 返回接口数据
*/
export function signIn(params: object) {
return request({
url: "/user/signIn",
method: "post",
data: params,
});
}
/**
* 统一批量导出
* @method signIn 用户登录接口
*/
const apiLogin = {
signIn: (params: object) => {
signIn(params);
},
// 这里继续添加
...
};
// 统一批量导出
export default apiLogin;
- 前端界面使用接口函数(方法与
1.1、前端定义接口函数
相对应)
方法一
<script lang="ts">
import { onMounted } from 'vue';
import { signIn } from '/@/api/login';
export default {
name: 'xxxx',
setup() {
onMounted(() => {
signIn({xxx:xxx参数}).then(res => {}).catch(err => {}).finally(() => {})
});
// 或者
// onMounted(async () => {
// const res = await signIn({xxx:xxx参数})
// });
},
};
</script>
方法二
<script lang="ts">
import { onMounted } from 'vue';
import apiLogin from '/@/api/login';
export default {
name: 'xxxx',
setup() {
onMounted(() => {
apiLogin().signIn({xxx:xxx参数}).then(res => {}).catch(err => {})
});
// 或者
// const { signIn } = apiLogin();
// onMounted(() => {
// signIn({xxx:xxx参数}).then(res => {}).catch(err => {})
// });
},
};
</script>
方法三
<script lang="ts">
import { onMounted } from 'vue';
import apiLogin from '/@/api/login';
export default {
name: 'xxxx',
setup() {
onMounted(() => {
apiLogin.signIn({xxx:xxx参数}).then(res => {}).catch(err => {})
});
// 或者
// onMounted(async () => {
// const res = await apiLogin.signIn({xxx:xxx参数})
// });
},
};
</script>
跨域处理
出于浏览器的一种安全机制同源策略影响,来限制不同源的网站不能通信。同源就是域名、协议、端口一致。
1. 最常见跨域代码
Access to XMLHttpRequest at 'xxx/menu/adminMenu.json'
from origin 'http://localhost:8888'
has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin'
header is present on the requested resource.
2. 跨域处理
线上:nginx 配置反向代理
本地
自定义代理
server.proxy,为开发服务器配置自定义代理规则。
解决:查看文章:vue cli 4.0+ 解决前端跨域问题
源跨域 api
代码:
const url = "https://gitee.com";
/**
* 获取后端动态路由菜单(admin)
* @link 参考:https://gitee.com/zuohuaijun/Admin.NET/tree/next/Web/src/menu
* @param params 要传的参数值,非必传
* @returns 返回接口数据
*/
export function getMenuAdmin(params?: object) {
return request({
url: `${url}/menu/adminMenu.json`,
method: "get",
params,
});
}
处理跨域后代码:(根目录 vite.config.ts server.proxy,为开发服务器配置自定义代理规则)
// 根目录 vite.config.ts
server: {
proxy: {
'/随便定义': {
target: '填写跨域的目标域名',
ws: true,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/随便定义/, ''),
},
},
},
// /@/api/menu/index.ts
// 使用 `/随便定义/` 代替 `填写跨域的目标域名`
export function getMenuAdmin(params?: object) {
return request({
url: '/随便定义/menu/adminMenu.json',
method: 'get',
params,
});
}
其它请求示例
1. 下载文件
import request from "/@/utils/request";
// 下载文件
export function downloadFile(params) {
return request({
responseType: "blob",
url: "xxxx",
method: "get",
params,
});
}
2. 清除请求头 token
import request from "/@/utils/request";
// 清除请求头 token
export function deleteToken(params) {
return request({
transformRequest: [
(data, headers) => {
delete headers.Authorization;
return data;
},
],
headers: { "Content-Type": "application/x-www-form-urlencoded" },
url: `xxxx/${params.id}`,
method: "get",
});
}