API规范设计的那些坑我替你踩过了
接口返回数据格式混乱,前端处理起来要命
最近接手一个老项目,被API的数据格式搞得头疼不已。同一个项目里,有些接口返回的是标准格式:
{
"code": 200,
"message": "success",
"data": {
"users": [...]
}
}
有些接口又直接返回原始数据:
[
{"id": 1, "name": "张三"},
{"id": 2, "name": "李四"}
]
还有些接口甚至混用了两种格式,看心情似的。这样导致前端每个请求都要写不同的处理逻辑,维护成本极高。
统一拦截器救了我一命
折腾了半天,决定用axios拦截器统一处理。思路很简单:不管后端返回什么格式,在响应拦截器里统一转换成标准格式。
这里我踩了个坑,最初想着判断返回数据的结构来决定要不要包装。但是后来发现这样很不稳定,特别是当数组长度为0时,容易误判。干脆直接暴力处理,统一格式化。
// axios配置
import axios from 'axios';
const apiClient = axios.create({
baseURL: process.env.VUE_APP_API_BASE || 'https://jztheme.com/api',
timeout: 10000,
});
// 响应拦截器
apiClient.interceptors.response.use(
response => {
const { data } = response;
// 检查是否已经是标准格式
if (typeof data === 'object' && data.hasOwnProperty('code')) {
return response; // 已经是标准格式,直接返回
}
// 转换为标准格式
response.data = {
code: 200,
message: 'success',
data: data
};
return response;
},
error => {
// 统一错误处理
if (error.response) {
// 后端返回错误状态码
error.response.data = {
code: error.response.status,
message: error.response.statusText,
data: null
};
} else {
// 网络错误等
error.response = {
data: {
code: 500,
message: '网络连接失败,请检查网络',
data: null
}
};
}
return Promise.reject(error);
}
);
export default apiClient;
这样处理后,前端所有API调用都可以统一按标准格式处理了:
// 统一的请求处理函数
async function fetchData(apiCall) {
try {
const response = await apiCall;
const { code, message, data } = response.data;
if (code === 200) {
return data;
} else {
throw new Error(message);
}
} catch (error) {
console.error('API请求失败:', error.message);
throw error;
}
}
// 使用示例
const users = await fetchData(() => apiClient.get('/users'));
const posts = await fetchData(() => apiClient.get('/posts'));
特殊情况处理,别忘了边界情况
后面遇到一个棘手的问题:某些接口返回的是文件流,比如Excel下载。这种情况下不能进行格式化处理,否则文件就损坏了。
折腾了半天发现,需要根据content-type来区分响应类型:
apiClient.interceptors.response.use(
response => {
// 文件流不处理格式
const contentType = response.headers['content-type'];
if (contentType && (
contentType.includes('application/octet-stream') ||
contentType.includes('application/vnd.openxmlformats-officedocument') ||
contentType.includes('application/zip')
)) {
return response;
}
const { data } = response;
if (typeof data === 'object' && data.hasOwnProperty('code')) {
return response;
}
response.data = {
code: 200,
message: 'success',
data: data
};
return response;
},
error => {
// 错误处理保持不变
if (error.response) {
const contentType = error.response.headers?.['content-type'];
if (!contentType?.includes('application/json')) {
error.response.data = {
code: error.response.status,
message: '服务器内部错误',
data: null
};
} else {
error.response.data = {
code: error.response.status,
message: error.response.statusText,
data: null
};
}
} else {
error.response = {
data: {
code: 500,
message: '网络连接失败,请检查网络',
data: null
}
};
}
return Promise.reject(error);
}
);
这里又踩了个坑,error.response.headers 在某些浏览器环境下可能不存在,所以要加个安全检查。
状态码映射表,让错误信息更友好
不同系统的错误码含义可能不一样,为了统一错误提示,我搞了个错误码映射表:
const ERROR_MESSAGES = {
400: '请求参数错误',
401: '登录已过期,请重新登录',
403: '权限不足',
404: '资源不存在',
500: '服务器内部错误',
502: '网关错误',
503: '服务暂时不可用'
};
apiClient.interceptors.response.use(
response => {
// 成功处理保持不变...
},
error => {
if (error.response) {
const { status } = error.response;
const defaultMsg = ERROR_MESSAGES[status] || 请求失败 (${status});
error.response.data = {
code: status,
message: defaultMsg,
data: null
};
} else {
error.response = {
data: {
code: 500,
message: '网络连接失败,请检查网络',
data: null
}
};
}
return Promise.reject(error);
}
);
后端配合改造,长期来看还是得治本
虽然前端拦截器能解决大部分问题,但治标不治本。跟后端同学沟通了一下,准备逐步规范API返回格式。
后端这边我也提供了具体的规范建议,主要是统一返回格式和错误码定义:
// 规范的API响应格式
{
"code": 200, // 业务状态码
"message": "success", // 提示信息
"data": {}, // 具体数据
"timestamp": 1640995200 // 时间戳(可选)
}
// 分页数据格式
{
"code": 200,
"message": "success",
"data": {
"list": [],
"total": 100,
"page": 1,
"size": 10
}
}
目前还在推进中,前端先用拦截器过渡一下。等后端全部规范化后,再逐步移除前端的兼容逻辑。
踩坑提醒:缓存和重试机制要考虑进去
这里特别要注意,如果涉及到缓存或自动重试机制,要在拦截器里做好标记,避免重复处理。我在做请求重试时就遇到过问题,重试的请求被重复格式化了。
// 添加处理标记避免重复处理
apiClient.interceptors.response.use(
response => {
if (response.config._formatted) {
return response; // 已处理过的请求不再处理
}
// ... 处理逻辑
response.config._formatted = true;
return response;
},
error => {
// 错误处理也要注意标记
return Promise.reject(error);
}
);
以上是我踩坑后的总结,API规范确实是个需要前后端一起配合的事情。前端拦截器只是临时方案,最终还是要靠整个团队的规范执行。如果你有更好的方案欢迎评论区交流。

暂无评论