使用 vue 开发时,频繁使用到了 axios 这个网络请求库,这里对其做一个发起请求时携带参数方式的小结。
一、基本使用
1.1. GET 请求
注意:GET请求无请求体,可以是可以有请求体body的,但是不建议带。
为什么不建议带请求体?
:::details 由文心大模型 3.5
生成
在HTTP协议中,GET请求通常用于请求数据,而POST请求则通常用于提交数据。这是因为GET和POST请求在语义和使用上有一些不同:
- 语义上的不同:
- GET请求:通常用于请求数据,它的语义是“获取”或“查询”。浏览器会把GET请求的结果(响应体)直接展示给用户。
- POST请求:通常用于提交数据,它的语义是“提交”或“更新”。POST请求的结果通常是一个操作的结果,而不是直接展示给用户的数据。
- 请求体的处理:
- GET请求:虽然GET请求可以包含请求体(body),但大多数HTTP客户端和服务器在处理GET请求时都会忽略请求体。这是因为GET请求的设计初衷就是为了从服务器获取数据,而不是提交数据。GET请求的查询参数应该放在URL的查询字符串中,而不是请求体中。
- POST请求:POST请求通常包含请求体,用于提交数据给服务器。服务器会处理POST请求的请求体来获取提交的数据。
- 缓存和书签:
- GET请求是幂等的和安全的,这意味着多次执行相同的GET请求应该得到相同的结果,并且不会改变服务器上的任何数据。因此,浏览器通常会对GET请求进行缓存。如果GET请求包含请求体,这可能会导致缓存行为不一致或不可预测。
- 由于GET请求的URL通常会被浏览器记录在历史记录或书签中,如果URL中包含了敏感信息(这些信息通常应该放在请求体中),那么这些信息可能会被泄露。
- URL长度限制:
- 浏览器和服务器通常对URL的长度有一定的限制。如果GET请求包含大量的数据在URL中(通过查询参数),这可能会导致URL超过长度限制。
- 安全性:
- 将敏感信息(如密码、私钥等)放在GET请求的URL中是不安全的,因为这些信息可能会被记录在浏览器历史、服务器日志或代理缓存中。这些信息应该通过POST请求放在请求体中,并使用适当的加密和身份验证机制来保护。
综上所述,虽然技术上GET请求可以包含请求体,但出于上述原因,通常不建议在GET请求中包含请求体。在实际开发中,应该根据请求的性质和目的选择合适的HTTP方法,并遵循相应的最佳实践。
:::
1.1.1. 使用 GET 方式进行无参请求
接口
public ResResult getAllUser(){ | |
List<User> list = userService.list(); | |
return ResResult.okResult(list); | |
} |
请求
axios({ | |
url:'http://localhost:8080/get/getAll', | |
method:'get' | |
}).then(res=>{ | |
console.log(res.data.data) | |
}) |
1.1.2. 使用 GET 方式请求,参数值直接放在路径中
接口
public ResResult getUserById( Long id){ | |
User user = userService.getById(id); | |
return ResResult.okResult(user); | |
} |
请求
axios({ | |
url:'http://localhost:8080/get/1', | |
method:'get' | |
}).then(res=>{ | |
console.log(res.data.data) | |
}) |
1.1.3. 使用 GET 方式请求,参数拼接在路径中
拼接方式 ①
使用 ? 进行参数拼接
接口
public ResResult getUserByIds( Long id){ | |
User user = userService.getById(id); | |
return ResResult.okResult(user); | |
} |
请求
axios({ | |
url:'http://localhost:8080/get?id=1', | |
method:'get' | |
}).then(res=>{ | |
console.log(res.data.data) | |
}) |
拼接方式 ②
使用 params 【单个参数】
接口
public ResResult getUserByIds( Long id){ | |
User user = userService.getById(id); | |
return ResResult.okResult(user); | |
} |
请求
axios({ | |
url:'http://localhost:8080/get', | |
params:{ | |
id:'2' | |
}, | |
method:'get' | |
}).then(res=>{ | |
console.log(res.data.data) | |
}) |
拼接方式 ③
使用 params 【多个参数】
接口
@GetMapping("/get") | |
public ResResult getUserByIds(@RequestParam("id") Long id,@RequestParam("username") String username){ | |
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); | |
wrapper.eq(User::getUsername,username); | |
wrapper.eq(User::getId,id); | |
User user = userService.getOne(wrapper); | |
return ResResult.okResult(user); | |
} |
请求
axios({ | |
url:'http://localhost:8080/get', | |
params:{ | |
id:'2', | |
username:'swx' | |
}, | |
method:'get' | |
}).then(res=>{ | |
console.log(res.data.data) | |
}) |
当 POST 有参请求且是简写时,要以 JSON 格式请求
axios.post('http://localhost:8080/post',"id=2&username=swx").then(res=>{ | |
console.log(res.data.data) | |
}).catch(err=>{ | |
console.log('timeout') | |
console.log(err) | |
}) |
1.1.4. GET 请求的简写方式
无参时:
axios.get('http://localhost:8080/get/getAll').then(res=>{ | |
console.log(res.data.data) | |
}).catch(err=>{ | |
console.log('timeout') | |
console.log(err) | |
}) |
有参时:
axios.get('http://localhost:8080/get',{params:{id:'2',username:'swx'}}).then(res=>{ | |
console.log(res.data.data) | |
}).catch(err=>{ | |
console.log('timeout') | |
console.log(err) | |
}) |
1.2. POST 请求
注意:POST 请求的有参、无参请求与如上的 GET 是一样的,只不过是请求方式名换一下。
如下是 POST 请求简写与传入配置项写法时,关于请求体格式的一点区别:
接口
var express = require('express') | |
var path = require('path') | |
var bodyParser = require('body-parser') | |
const { json } = require('body-parser') | |
var app = express() | |
app.use(express.static(path.join(__dirname, 'public'))) | |
app.use(bodyParser.urlencoded({ extended: false })) | |
app.use(bodyParser.json()) | |
app.get('/a', function(req, res) { | |
console.log(req.query) | |
res.send({ "id": 1, "name": "张三" }) | |
}) | |
app.listen(3000, function() { | |
console.log('app is runing...') | |
}) |
请求
写法 ①
如果使用 Axios 的 POST 请求的简写形式,需要将数据以 JSON 格式传递。
axios.post('/a', { | |
"id": 5, | |
"name": "ssss" | |
}).then(response => { | |
console.log('/a1', response.data) | |
}, error => { | |
console.log('错误', error.message) | |
}) |
请求
写法 ②
如果将数据直接作为请求体传递,不需要将数据写成JSON格式。axios会根据请求头的Content-Type自动处理数据格式。
axios({ | |
method: 'POST', | |
url: '/a', | |
data: { | |
id: 1, | |
name: "张三" | |
} | |
}) | |
.then(response => { | |
console.log('/a', response.data) | |
return response.data | |
}, error => { | |
console.log('错误', error.message) | |
}) |
二、请求失败处理
axios.get('http://localhost:8080/get',{params:{id:'2',username:'swx'}}).then(res=>{ | |
console.log(res.data.data) | |
}).catch(err=>{ | |
console.log('timeout') | |
console.log(err) | |
}) |
三、axios 并发请求
方式 1
接口
public ResResult getAllUser(){ | |
List<User> list = userService.list(); | |
return ResResult.okResult(list); | |
} | |
public ResResult getUserByIdt( Long id){ | |
User user = userService.getById(id); | |
return ResResult.okResult(user); | |
} |
请求
axios.all([ | |
axios.get('http://localhost:8080/get/getAll'), | |
axios.get('http://localhost:8080/get/get',{params:{id:'1'}}) | |
]).then(res=>{ | |
//返回的是数组,请求成功返回的数组 | |
console.log(res[0].data.data), | |
console.log(res[1].data.data) | |
}).catch(err=>{ | |
console.log(err) | |
}) |
方式2:使用spread方法处理返回的数组
<script> | |
axios.all([ | |
axios.get('http://localhost:8080/get/getAll'), | |
axios.get('http://localhost:8080/get/get',{params:{id:'1'}}) | |
]).then( | |
axios.spread((res1,res2)=>{ | |
console.log(res1.data.data), | |
console.log(res2.data.data) | |
}) | |
).catch(err=>{ | |
console.log(err) | |
}) | |
</script> |
四、axios全局配置
axios.defaults.baseURL='http://localhost:8080'; //全局配置属性 | |
axios.defaults.timeout=5000; //设置超时时间 | |
//发送请求 | |
axios.get('get/getAll').then(res=>{ | |
console.log(res.data.data) | |
}); | |
axios.post('post/getAll').then(res=>{ | |
console.log(res.data.data) | |
}); |
五、axios实例
//创建实例 | |
let request = axios.create({ | |
baseURL:'http://localhost:8080', | |
timeout:5000 | |
}); | |
//使用实例 | |
request({ | |
url:'get/getAll' | |
}).then(res=>{ | |
console.log(res.data.data) | |
}); | |
request({ | |
url:'post/getAll', | |
method:'post' | |
}).then(res=>{ | |
console.log(res.data.data) | |
}) |
六、axios拦截器
axios提供了两大类拦截器:
- 一种是请求方向的拦截(成功的、失败的)
- 一种是响应方向的拦截(成功的,失败的)
拦截器作用:
比如:请求之前在请求头加token、强制登录
响应的时候可以进行相应的数据处理
请求拦截器
//创建实例 | |
let request = axios.create({ | |
baseURL:'http://localhost:8080', | |
timeout:5000 | |
}); | |
//配置axios拦截器 | |
request.interceptors.request.use(config=>{ | |
console.log("请求进来了...") | |
console.log("请求成功方向") | |
console.log(config.data.data) | |
//放行请求,这一步很重要,否则报错 | |
return config; | |
},err=>{ | |
console.log("请求进来了...") | |
console.log("请求失败方向") | |
console.log(err) | |
}); | |
//如果没有创建实例,则使用以下方式 | |
//配置axios拦截器 | |
// axios.interceptors.request.use(config=>{ | |
// console.log("请求进来了...") | |
// console.log("请求成功方向") | |
// console.log(config) | |
// //放行请求 | |
// return config; | |
// },err=>{ | |
// console.log("请求进来了...") | |
// console.log("请求失败方向") | |
// console.log(err) | |
// }); | |
//使用实例 | |
request({ | |
url:'get/getAll' | |
}).then(res=>{ | |
console.log(res.data.data) | |
}); |
响应拦截器
//创建实例 | |
let request = axios.create({ | |
baseURL:'http://localhost:8080', | |
timeout:5000 | |
}); | |
//配置axios拦截器 | |
request.interceptors.response.use(config=>{ | |
console.log("响应进来了...") | |
console.log("响应成功方向") | |
console.log(config.data.data) | |
//放行响应 | |
return config; | |
},err=>{ | |
console.log("响应进来了...") | |
console.log("响应失败方向") | |
console.log(err) | |
}); | |
//使用实例 | |
request({ | |
url:'get/getAll' | |
}).then(res=>{ | |
console.log(res.data.data) | |
}); |
七、vue 中封装 axios
封装在request.js中
//导入axios | |
import axios from 'axios' | |
//创建axios实例 | |
const service = axios.create({ | |
baseURL: process.env.VUE_APP_BASE_API, | |
timeout: 5000 | |
}) | |
//请求拦截器 | |
service.interceptors.request.use( | |
config => { | |
if (store.getters.token) { | |
config.headers['token'] = getToken() | |
} | |
//放行请求 | |
return config | |
}, | |
error => { | |
console.log(error) | |
return Promise.reject(error) | |
} | |
) | |
//响应拦截器 | |
service.interceptors.response.use( | |
response => { | |
//返回的数据 | |
const res = response.data | |
if (res.code !== 200) { | |
Message({ | |
message: res.message || 'Error', | |
type: 'error', | |
duration: 5 * 1000 | |
}) | |
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired; | |
if (res.code === 50008 || res.code === 50012 || res.code === 50014) { | |
// to re-login | |
MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', { | |
confirmButtonText: 'Re-Login', | |
cancelButtonText: 'Cancel', | |
type: 'warning' | |
}).then(() => { | |
store.dispatch('user/resetToken').then(() => { | |
location.reload() | |
}) | |
}) | |
} | |
return Promise.reject(new Error(res.message || 'Error')) | |
} else { | |
return res | |
} | |
}, | |
error => { | |
console.log('err' + error) // for debug | |
Message({ | |
message: error.message, | |
type: 'error', | |
duration: 5 * 1000 | |
}) | |
return Promise.reject(error) | |
} | |
) | |
export default service |
哪个模块需要发送请求直接引入即可,将以上实例导入
比如:此模块的所有请求接口:api下的skuInfo.js
//导入axios实例 | |
const api_name = '/admin/product/skuInfo' | |
export default { | |
getPageList(page, limit, searchObj) { | |
return request({ | |
url: `${api_name}/${page}/${limit}`, | |
method: 'get', | |
params: searchObj | |
}) | |
}, | |
save(role) { | |
return request({ | |
url: `${api_name}/save`, | |
method: 'post', | |
data: role | |
}) | |
}, | |
//新人专享 | |
isNewPerson(id, status) { | |
return request({ | |
url: `${api_name}/isNewPerson/${id}/${status}`, | |
method: 'get' | |
}) | |
}, | |
} |
list.vue页面中使用
//先导入 | |
import api from '@/api/product/skuInfo' | |
| |
api.getPageList(this.page, this.limit, this.searchObj).then( | |
response => { | |
debugger | |
this.list = response.data.records | |
this.total = response.data.total | |
| |
// 数据加载并绑定成功 | |
this.listLoading = false | |
} | |
) | |
} | |
| |
api.save(this.skuInfo).then(response => { | |
if (response.code) { | |
this.$message({ | |
type: 'success', | |
message: response.message | |
}) | |
this.$router.push({ path: '/product/skuInfo/list' }) | |
this.saveBtnDisabled = false | |
} | |
}) | |
| |
//新人专享 | |
handleNewPersonChange(index, row) { | |
api.isNewPerson(row.id, row.isNewPerson).then(response => { | |
this.$message({ | |
type: 'info', | |
message: '操作成功' | |
}) | |
this.fetchData() | |
}) | |
} | |
} |
main.js中引入使用
import * as API from '@/api' | |
| |
Vue.prototype.$API = API |
八、立足当前项目
service / index.ts
import { BASE_URL, TIME_OUT } from './config' | |
import HYRequest from './request' | |
const hyRequest = new HYRequest({ | |
baseURL: BASE_URL, | |
timeout: TIME_OUT | |
}) | |
export default hyRequest |
service / config / index.ts
// 1.区分开发环境和生产环境 | |
// export const BASE_URL = 'http://coderwhy.dev:8000' | |
// export const BASE_URL = 'http://codercba.prod:8000' | |
// 2.代码逻辑判断, 判断当前环境 | |
// vite默认提供的环境变量 | |
// console.log(import.meta.env.MODE) | |
console.log(import.meta.env.DEV) // 是否开发环境 | |
console.log(import.meta.env.PROD) // 是否生产环境 | |
console.log(import.meta.env.SSR) // 是否是服务器端渲染(server side render) | |
let BASE_URL = '' | |
if (import.meta.env.PROD) { | |
BASE_URL = 'http://codercba.prod:8000' | |
} else { | |
BASE_URL = 'http://coderwhy.dev:8000' | |
} | |
console.log(BASE_URL) | |
// 3.通过创建.env文件直接创建变量 | |
console.log(import.meta.env.VITE_URL) | |
export const TIME_OUT = 10000 | |
export { BASE_URL } |
service / request / index.ts
import axios from 'axios' | |
import type { AxiosInstance } from 'axios' | |
import type { HYRequestConfig } from './type' | |
// 拦截器: 蒙版Loading/token/修改配置 | |
/** | |
* 两个难点: | |
* 1.拦截器进行精细控制 | |
* > 全局拦截器 | |
* > 实例拦截器 | |
* > 单次请求拦截器 | |
* 2.响应结果的类型处理(泛型) | |
*/ | |
class HYRequest { | |
instance: AxiosInstance | |
// request实例 => axios的实例 | |
constructor(config: HYRequestConfig) { | |
this.instance = axios.create(config) | |
// 每个instance实例都添加拦截器 | |
this.instance.interceptors.request.use( | |
(config) => { | |
// loading/token | |
return config | |
}, | |
(err) => { | |
return err | |
} | |
) | |
this.instance.interceptors.response.use( | |
(res) => { | |
return res.data | |
}, | |
(err) => { | |
return err | |
} | |
) | |
// 针对特定的hyRequest实例添加拦截器 | |
this.instance.interceptors.request.use( | |
config.interceptors?.requestSuccessFn, | |
config.interceptors?.requestFailureFn | |
) | |
this.instance.interceptors.response.use( | |
config.interceptors?.responseSuccessFn, | |
config.interceptors?.responseFailureFn | |
) | |
} | |
// 封装网络请求的方法 | |
// T => IHomeData | |
request<T = any>(config: HYRequestConfig<T>) { | |
// 单次请求的成功拦截处理 | |
if (config.interceptors?.requestSuccessFn) { | |
config = config.interceptors.requestSuccessFn(config) | |
} | |
// 返回Promise | |
return new Promise<T>((resolve, reject) => { | |
this.instance | |
.request<any, T>(config) | |
.then((res) => { | |
// 单词响应的成功拦截处理 | |
if (config.interceptors?.responseSuccessFn) { | |
res = config.interceptors.responseSuccessFn(res) | |
} | |
resolve(res) | |
}) | |
.catch((err) => { | |
reject(err) | |
}) | |
}) | |
} | |
get<T = any>(config: HYRequestConfig<T>) { | |
return this.request({ ...config, method: 'GET' }) | |
} | |
post<T = any>(config: HYRequestConfig<T>) { | |
return this.request({ ...config, method: 'POST' }) | |
} | |
delete<T = any>(config: HYRequestConfig<T>) { | |
return this.request({ ...config, method: 'DELETE' }) | |
} | |
patch<T = any>(config: HYRequestConfig<T>) { | |
return this.request({ ...config, method: 'PATCH' }) | |
} | |
} | |
export default HYRequest |
service / request / type.ts
import type { AxiosRequestConfig, AxiosResponse } from 'axios' | |
// 针对AxiosRequestConfig配置进行扩展 | |
export interface HYInterceptors<T = AxiosResponse> { | |
requestSuccessFn?: (config: AxiosRequestConfig) => AxiosRequestConfig | |
requestFailureFn?: (err: any) => any | |
responseSuccessFn?: (res: T) => T | |
responseFailureFn?: (err: any) => any | |
} | |
export interface HYRequestConfig<T = AxiosResponse> extends AxiosRequestConfig { | |
interceptors?: HYInterceptors<T> | |
} |