import { request } from '@/utils/request.js' // 宠物分类tab export function apiGetPetTypeList() { return request({ url: "/app/pet/pet", method: 'GET', header: { 'ngrok-skip-browser-warning': 'true' } }) } // 获取宠物品种列表 export function apiGetPetBreeds(petClass,petBreed) { return request({ url: '/app/pet/breeds', method: 'GET', data:{ petClass, petBreed }, header: { 'ngrok-skip-browser-warning': 'true' // 特殊header } }) } // 添加宠物 export function apiaddPet(data) { return request({ url: "/app/pet/addPet", method: 'POST', data }) } 怎么像这样封装
时间: 2025-07-22 20:51:15 浏览: 3
<think>我们参考用户提供的引用内容,以及用户之前的问题(关于GET请求携带参数),来封装一个更完善的HTTP请求方法。
目标:封装GET和POST请求,支持统一添加请求头(如token),统一处理加载状态,统一处理错误提示,返回Promise便于异步处理。
改进点:
1. 将公共配置提取(如baseUrl、请求头)
2. 统一处理请求参数:GET请求的参数应该拼接到URL上(注意数组等特殊情况的处理),POST请求参数放在data中
3. 统一处理响应:根据业务逻辑判断请求成功与否(例如状态码为200,且自定义的业务状态码成功)
4. 统一错误处理(网络错误、服务器错误等)
5. 使用拦截器(可选)或封装函数来处理公共逻辑
但是,注意引用[1]和[2]中的封装方式,它们都是直接封装成函数,没有使用拦截器。我们这里也采用函数封装的方式。
步骤:
1. 创建http.js文件,封装request函数(支持GET和POST)
2. 设置baseUrl
3. 从本地存储获取token,并设置到请求头
4. 显示加载中(uni.showLoading)
5. 对于GET请求,将参数拼接到URL(注意:需要处理数组、特殊字符等,但这里我们先做简单处理,使用JSON.stringify会导致问题,所以一般只支持基本类型)
如果遇到数组,我们可以使用qs库(需要安装)或者自己处理成重复的key,但为了简单,我们先不处理数组,提示用户参数不要用数组。
6. 返回Promise,在请求成功时resolve,失败时reject
7. 在成功响应中,可以判断业务状态码(如res.data.code),如果不是成功状态,可以统一提示错误并reject(这里根据业务来,有的业务错误不提示,由调用方处理)
8. 隐藏加载中(在finally中隐藏?但uni.request的success和fail回调中都需要隐藏,所以我们在两个回调中都调用uni.hideLoading)
注意:引用[1]中把token直接拼接在URL上,这样不安全,建议放在header中。
我们参考引用[2]的方式,将token放在请求头。
设计:
请求配置:
method: 方法(必须)
url: 相对路径(必须)
data: 参数(可选)
headers: 额外的请求头(可选)
封装两个函数:get和post,它们都调用一个公共的request函数。
代码结构:
http.js
=======
const baseUrl = 'http://api.example.com/';
// 公共请求函数
function request(method, url, data, customHeader = {}) {
return new Promise((resolve, reject) => {
// 显示加载中
uni.showLoading({
title: '加载中...'
});
// 获取token
const token = uni.getStorageSync('token') || '';
// 设置请求头
const header = {
'Content-Type': method === 'POST' ? 'application/json' : 'application/x-www-form-urlencoded',
'Authorization': token ? `Bearer ${token}` : '',
...customHeader
};
// 处理GET请求的URL参数
let requestUrl = baseUrl + url;
if (method === 'GET' && data) {
// 将参数对象转换为查询字符串
const params = Object.keys(data).map(key => {
// 注意:这里只处理基本类型,数组和对象需要调用方自己处理成字符串,或者我们使用第三方库如qs
return `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`;
}).join('&');
requestUrl += `?${params}`;
}
uni.request({
url: requestUrl,
method: method,
data: method !== 'GET' ? data : {},
header: header,
success: (res) => {
// 隐藏加载
uni.hideLoading();
// 这里可以处理HTTP状态码,比如200-299之间认为成功
if (res.statusCode >= 200 && res.statusCode < 300) {
// 如果业务状态码也是成功的,则resolve,否则根据业务处理
// 假设业务返回的数据中有一个code字段,0表示成功
if (res.data.code === 0) {
resolve(res.data);
} else {
// 业务错误,可以提示
uni.showToast({
title: res.data.msg || '业务错误',
icon: 'none'
});
reject(res.data);
}
} else {
// HTTP状态码错误
uni.showToast({
title: `请求错误:${res.statusCode}`,
icon: 'none'
});
reject(res);
}
},
fail: (err) => {
uni.hideLoading();
uni.showToast({
title: '网络错误,请稍后重试',
icon: 'none'
});
reject(err);
}
});
});
}
// 封装get方法
function get(url, data, header) {
return request('GET', url, data, header);
}
// 封装post方法
function post(url, data, header) {
return request('POST', url, data, header);
}
export default { get, post };
但是,注意:上述代码中,对于GET请求,我们手动拼接了URL,而POST请求的Content-Type设置为application/json,并且数据以JSON格式发送。
问题:
1. 对于GET请求,如果参数是数组怎么办?比如:{ ids: [1,2,3] },我们希望转换成 ?ids=1&ids=2&ids=3 或者 ?ids=1,2,3
这里我们不做复杂处理,因为UniApp的uni.request在GET请求中,如果传入data,它实际上会自动拼接到URL上,但是它的处理方式是将数组转成逗号分隔的字符串(ids=1,2,3)。所以我们可以不用手动拼接,直接使用uni.request的data参数,让系统自己处理。
因此,改进:我们不在GET请求中手动拼接URL,而是直接使用uni.request的data参数,因为uni.request内部会处理。
修改request函数:
if (method === 'GET') {
// 直接使用uni.request的data,它会自动拼接到URL
// 所以不需要修改url
requestUrl = baseUrl + url;
} else {
requestUrl = baseUrl + url;
}
然后,在uni.request中,data参数根据方法传入:
data: method === 'GET' ? data : data,
但是注意:这样GET请求中,数组参数会被转成逗号分隔,可能不符合后端要求。如果后端要求不同的格式,需要自行处理。
所以,我们保留手动拼接的方式,但是提供一个参数处理函数,这里我们为了简单,使用内置的URLSearchParams(注意兼容性)或者自己写一个序列化函数。
由于UniApp运行环境包括小程序,而小程序不支持URLSearchParams,所以我们自己写一个简单的:
function serialize(data) {
if (!data) return '';
return Object.keys(data).map(key => {
const value = data[key];
// 如果是数组,则转换成 key=value1&key=value2 的形式
if (Array.isArray(value)) {
return value.map(v => `${encodeURIComponent(key)}=${encodeURIComponent(v)}`).join('&');
}
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
}).join('&');
}
然后在GET请求中:
if (method === 'GET' && data) {
const queryString = serialize(data);
if (queryString) {
requestUrl += (requestUrl.includes('?') ? '&' : '?') + queryString;
}
}
但是这样处理数组会变成重复的key,例如:ids=1&ids=2&ids=3,这是后端常用的方式。
考虑到这个序列化函数可能不够通用,我们也可以引入qs库(需要安装),但为了减少依赖,我们可以根据项目需要选择。
这里,我们假设项目暂时不需要处理数组,那么我们可以先不处理,或者使用简单的序列化(只处理基本类型和数组,且数组转换成重复key)。
我们写一个简单的序列化函数(支持数组转重复key,对象转JSON字符串):
function serialize(data) {
const parts = [];
for (let key in data) {
if (data.hasOwnProperty(key)) {
const value = data[key];
if (value === null || typeof value === 'undefined') {
continue;
}
if (Array.isArray(value)) {
value.forEach(v => {
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`);
});
} else if (typeof value === 'object') {
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(JSON.stringify(value))}`);
} else {
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
}
}
}
return parts.join('&');
}
然后使用这个函数来序列化GET参数。
但是,如果参数是嵌套对象,我们这里将对象转成JSON字符串,需要后端配合解析。
根据需求,我们这里先采用简单方式,因为大多数情况参数是简单的。
综合以上,我们给出一个封装示例,并加上注释。
注意:用户提供的引用中,有在URL上直接拼接name和toker,我们这里改为从header中传递token。
最终代码示例(http.js):</think>在 UniApp 中封装 HTTP 请求方法可以提高代码复用性和可维护性。以下是一个完整的封装方案,结合了 GET 和 POST 请求的统一处理:
### 完整封装方案 (http.js)
```javascript
// 配置文件 config.js
const config = {
baseUrl: 'https://api.example.com', // 基础API地址
timeout: 10000, // 请求超时时间
tokenKey: 'auth_token' // 本地存储的token键名
}
export default config;
```
```javascript
// 封装的请求方法 http.js
import config from './config.js';
class HttpRequest {
constructor() {
this.baseUrl = config.baseUrl;
this.timeout = config.timeout;
}
// 核心请求方法
request(method, endpoint, data = {}) {
return new Promise((resolve, reject) => {
// 显示加载提示
uni.showLoading({ title: '加载中...', mask: true });
// 获取本地存储的token
const token = uni.getStorageSync(config.tokenKey) || '';
// 请求配置
const requestConfig = {
url: `${this.baseUrl}${endpoint}`,
method: method.toUpperCase(),
data: method.toUpperCase() === 'GET' ? {} : data,
timeout: this.timeout,
header: {
'Content-Type': 'application/json',
'Authorization': token ? `Bearer ${token}` : ''
},
success: (res) => this._handleSuccess(res, resolve, reject),
fail: (err) => this._handleFail(err, reject)
};
// GET请求特殊处理
if (method.toUpperCase() === 'GET' && Object.keys(data).length) {
const queryString = this._serializeParams(data);
requestConfig.url += `?${queryString}`;
}
uni.request(requestConfig);
});
}
// 处理成功响应
_handleSuccess(res, resolve, reject) {
uni.hideLoading();
// 统一处理HTTP状态码
if (res.statusCode >= 200 && res.statusCode < 300) {
// 根据业务状态码进一步处理
if (res.data.code === 0) { // 假设0表示成功
resolve(res.data);
} else {
this._showErrorToast(res.data.message || '业务逻辑错误');
reject(res.data);
}
} else {
this._showErrorToast(`请求失败: ${res.statusCode}`);
reject(res);
}
}
// 处理失败请求
_handleFail(err, reject) {
uni.hideLoading();
this._showErrorToast('网络连接失败');
reject(err);
}
// 序列化GET参数
_serializeParams(params) {
return Object.keys(params)
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
.join('&');
}
// 显示错误提示
_showErrorToast(message) {
uni.showToast({
title: message,
icon: 'none',
duration: 2000
});
}
// GET请求快捷方法
get(endpoint, params = {}) {
return this.request('GET', endpoint, params);
}
// POST请求快捷方法
post(endpoint, data = {}) {
return this.request('POST', endpoint, data);
}
}
const http = new HttpRequest();
export default http;
```
### 使用示例
```javascript
import http from '@/utils/http.js';
// GET请求示例
export const fetchProducts = (category, page = 1) => {
return http.get('/products', {
category,
page,
page_size: 10
});
};
// POST请求示例
export const login = (username, password) => {
return http.post('/auth/login', {
username,
password
});
};
// 在页面中使用
async function getUserData() {
try {
const response = await fetchProducts('electronics', 1);
console.log('产品列表:', response.data);
} catch (error) {
console.error('获取产品失败:', error);
}
}
```
### 封装特点与优势
1. **统一配置管理**
- 基础 URL 和超时时间统一配置
- Token 自动从本地存储获取并添加到请求头[^1]
2. **自动化处理**
- GET 参数自动序列化并拼接到 URL
- 统一处理加载状态和错误提示
- 自动隐藏加载动画
3. **错误处理机制**
- 区分网络错误和业务错误
- 统一错误提示方式
- 支持自定义错误消息
4. **安全机制**
- Token 通过 Authorization 头传递而非 URL 参数[^1]
- 参数自动 URL 编码防止注入攻击
5. **扩展性**
- 可轻松添加 PUT、DELETE 等方法
- 支持添加请求拦截器和响应拦截器
### 最佳实践建议
1. **敏感参数处理**
```javascript
// 安全示例:敏感数据放在POST请求体中
http.post('/payment', {
card_number: encrypt(cardNumber), // 加密处理
amount: 100
});
```
2. **添加请求拦截器**
```javascript
// 在构造函数中添加
constructor() {
// ...
uni.addInterceptor('request', {
invoke: (args) => {
// 统一添加时间戳
if (args.method === 'GET') {
args.url += args.url.includes('?') ? '&' : '?';
args.url += `_t=${Date.now()}`;
}
}
});
}
```
3. **处理复杂数据结构**
```javascript
// 数组参数处理
get('/search', {
ids: [1, 2, 3],
// 转换为: ids=1&ids=2&ids=3
});
// 嵌套对象处理
post('/filter', {
filters: JSON.stringify({
price: { min: 100, max: 500 },
brands: ['A', 'B']
})
});
```
4. **超长参数处理**
```javascript
// 当GET参数过长时自动转为POST
if (queryString.length > 1500) {
console.warn('GET参数过长,自动转为POST');
return this.request('POST', endpoint, params);
}
```
此封装方案提供了统一的请求处理流程,支持 GET/POST 方法,包含错误处理、加载状态管理、安全机制等完整功能,可直接集成到 UniApp 项目中[^1][^2][^3]。
阅读全文
相关推荐














