http请求
# Mock
平台使用 vite-plugin-fake-server (opens new window) 插件并集成了 @faker-js/faker (opens new window),支持常用的 post
、get
请求方式,仅用于本地开发,生产环境请务必删除 mock
# 基础用法
① 来到 mock (opens new window) 文件存放目录,比如添加 login
接口,采用 post
请求,参考下面代码。
点击查看
import { MockMethod } from "vite-plugin-mock";
export default [
{
url: "/login",
method: "post",
response: ({ body }) => {
return {
success: true,
data: {},
};
},
},
] as MockMethod[];
② 上面我们添加了 login
接口,接着来到 api (opens new window) 目录,比如添加 user.ts
文件,参考下面代码
点击查看
import { http } from "@/utils/http";
// 这里定义返回值类型,使接口拥有良好的类型推导
export type UserResult = {
/** 是否请求成功 */
success: boolean;
data: {
/** 用户名 */
username: string;
/** 当前登陆用户的角色 */
roles: Array<string>;
/** `token` */
accessToken: string;
/** 用于调用刷新`accessToken`的接口时所需的`token` */
refreshToken: string;
/** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx') */
expires: Date;
};
};
/** 登录接口 */
export const getLogin = (data?: object) => {
return http.request<UserResult>("post", "/login", { data });
};
③ 上面导出了 getLogin
接口,接下来我们来到需要使用这个接口的地方导入,参考下面代码
点击查看
<script setup lang="ts">
import { getLogin } from "@/api/user";
async function onLogin() {
let result = await getLogin({ username: "admin", password: "admin123" });
if (result.success) {
console.log(result.data);
}
}
</script>
<template>
<el-button @click="onLogin">点击登录</el-button>
</template>
您可以注意到我们在上面 ②
步骤中定义了接口返回值类型 UserResult
,在使用时就会获得良好的类型推导,如下图
# 如何快速删除 mock
来到 build/plugins.ts (opens new window) 文件,将 import { viteMockServe } from "vite-plugin-mock";
注释,最后把 这里选中 (opens new window) 的都注释即可
# Axios
# 中文文档
# 基本请求方式
平台封装了 axios (opens new window),支持的请求方式请看 具体参考 (opens new window),下面举例常用的四种请求方式
# get
请求
点击查看
import { http } from "@/utils/http";
// params传参
export const textRequest = (params?: object) => {
return http.request("get", "/xxx", { params });
};
// url拼接传参
export const textRequest = (params?: object) => {
return http.request("get", "/xxx?message=" + params);
};
# post
请求
点击查看
import { http } from "@/utils/http";
// params传参
export const textRequest = (params?: object) => {
return http.request("post", "/xxx", { params });
};
// data传参
export const textRequest = (data?: object) => {
return http.request("post", "/xxx", { data });
};
# delete
请求
点击查看
import { http } from "@/utils/http";
// params传参
export const textRequest = (params?: object) => {
return http.request("delete", "/xxx", { params });
};
// data传参
export const textRequest = (data?: object) => {
return http.request("delete", "/xxx", { data });
};
# put
请求
点击查看
import { http } from "@/utils/http";
// params传参
export const textRequest = (params?: object) => {
return http.request("put", "/xxx", { params });
};
// data传参
export const textRequest = (data?: object) => {
return http.request("put", "/xxx", { data });
};
# http.request
传参解析
http.request 源码 (opens new window),参数解析如下
参数属性 | 说明 | 类型 |
---|---|---|
method | 请求方式 | RequestMethods |
url | 请求地址 | string |
param | 请求参数 | AxiosRequestConfig |
axiosConfig | 自定义axios 配置 | PureHttpRequestConfig |
提示
当然平台不仅提供了 http.request
方法让您发送请求,还提供两种额外的请求方式 http.post (opens new window)、http.get (opens new window),当然这两种也是基于 http.request
封装的,也会走请求拦截和响应拦截,如果您需要脱离平台封装的请求,可以直接使用 axios
,类似参考 src/views/welcome/index.vue (opens new window)
# 如何与后台联调
# 跨域与解决办法
# 什么是跨域
跨域本质是浏览器基于同源策略的一种安全手段。同源策略(Sameoriginpolicy),是一种约定,它是浏览器最核心也最基本的安全功能。当协议(protocol)、主机(host)、端口(port)其中一项不相同的时候就会产生跨域。跨域只产生在浏览器,这也是为什么您用 postman
请求接口时不会有跨域问题的原因
# 解决办法
① 本地开发的话,我们来到 vite.config.ts (opens new window),参考下面代码配置本地跨域代理即可
proxy: {
"/api": {
// 这里填写后端地址
target: "http://127.0.0.1:3000",
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, "")
}
}
② 配置好上面的跨域代理后,来到 src/api (opens new window) 目录,这里我们先新建一个 utils.ts
文件用来配置环境,参考下面代码
注意:这里又分两种写法
第一种(也是最常用的一种,满足大多数场景,首选这种写法哦):比如后端使用 spring cloud
微服务架构,不同服务可能会部署在不同机器上,这时候前端必定产生跨域,推荐部署前端项目首选 nginx (opens new window),可以利用 nginx
的代理转发功能来解决跨域问题
utils.ts
export const baseUrlApi = (url: string) => `/api/${url}`;
第二种:前端打包直接丢给后端,放到后端项目里一起部署,协议、主机、端口都相同了,就没有跨域问题
utils.ts
export const baseUrlApi = (url: string) =>
process.env.NODE_ENV === "development"
? `/api/${url}`
: `http://127.0.0.1:3000/${url}`;
③ 写完上面的 utils.ts
后,继续在 src/api (opens new window) 目录,我们再建一个 user.ts
文件,导出这个接口,供页面调用,参考下面代码
user.ts
import { http } from "@/utils/http";
import { baseUrlApi } from "./utils";
/** 登录 */
export const getLogin = (data?: object) => {
return http.request<any>("post", baseUrlApi("login"), { data });
};
④ 上面都完成后,我们在需要调用该接口的地方调用即可,参考 src/store/modules/user.ts (opens new window)
# 多个后端地址如何联调
大致步骤和上面差不多,这里我们就简单写一下,具体对比上面的就行
① 在 vite.config.ts
写入下面代码
proxy: {
// 第一个代理后端地址
"/api": {
target: "http://127.0.0.1:3000",
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, "")
},
// 第二个代理后端地址
"/otherApi": {
target: "http://127.0.0.1:3290",
changeOrigin: true,
rewrite: path => path.replace(/^\/otherApi/, "")
},
// websocket地址(知识点:wss只能在https安全协议下使用)
// "/wsApi": {
// target: "ws://localhost:3000",
// ws: true,
// },
// ...依此类推,有几个后端地址就写几个后端地址
}
② 在 utils.ts
里我们采用第一种写法,参考下面代码
utils.ts
// 第一个代理后端地址
export const baseUrlApi = (url: string) => `/api/${url}`;
// 第二个代理后端地址
export const baseUrlOtherApi = (url: string) => `/otherApi/${url}`;
③ 第一个代理后端地址上面建的是 user.ts
文件,那么第二个代理后端地址我们就建个 other.ts
文件吧,参考下面代码
other.ts
import { http } from "@/utils/http";
import { baseUrlOtherApi } from "./utils";
/** 该接口采用 http://127.0.0.1:3290 后端地址 */
export const getOther = (data?: object) => {
return http.request<any>("get", baseUrlOtherApi("other"), { data });
};
最后,需要在哪里调用,引入使用即可
# 如何声明接口返回值类型
可以看到上面的写法 http.request
后面跟的都是 any
,这很不友好,参考下面友好类型写法即可
import { http } from "@/utils/http";
import { baseUrlApi } from "./utils";
// 定义 login 接口返回值类型为 UserResult
export type UserResult = {
/** 是否请求成功 */
success: boolean;
data: {
/** 用户名 */
username: string;
/** 当前登陆用户的角色 */
roles: Array<string>;
/** `token` */
accessToken: string;
/** 用于调用刷新`accessToken`的接口时所需的`token` */
refreshToken: string;
/** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx') */
expires: Date;
};
};
/** 登录 */
export const getLogin = (data?: object) => {
return http.request<UserResult>("post", baseUrlApi("login"), { data });
};
# 平台封装的请求可以设置额外的 axios 配置吗
当然可以。点击查看这里,可以看到自定义 axios
配置中 axiosConfig
作为最后一个参数
import { http } from "@/utils/http";
import { baseUrlApi } from "./utils";
export const testRequest = (data?: object) => {
return http.request<any>(
"post",
baseUrlApi("login"),
{ data },
// 自定义的axios配置在下面对象填写即可
{
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
}
);
};
# JWT Token
(内置无感刷新 token
解决方案)
实现原理:后端返回两个 token
(一个用来请求,一个用来刷新)和过期时间,前端将其进行本地存储,每当接口请求时,把本地存储的过期时间与本地当前时间对比,如果 token
过期就把当前请求暂存,然后去请求刷新 token
接口,获取到新 token
后,再 触发 (opens new window) 暂存的请求
# 信息存储
具体看 src/utils/auth (opens new window) 文件,里面有很详细的备注
# 细节处理
① 不需要携带 token
的接口我们设置了 请求白名单 (opens new window)
② 当 token
过期后,平台会暂存请求,直到拿到新 token
才会请求,避免了当页面有多个请求会重复刷新 token
的问题。核心代码有三处:一 (opens new window)、二 (opens new window)、三 (opens new window)
生产环境 Mock 可以和真实接口共存吗?
当然可以共存,不过可能会遇到 Mock
干扰真实接口的问题。如果您在开发环境接口没问题,但是到生产环境出问题了,在排除后端问题前提下可以尝试 删除 Mock