uni-app 与 Spring Boot 后端对接:请求封装与错误处理
·
移动端和后端的对接远不止"调接口"三个字——上传 token 怎么带、网络断了怎么提示、统一错误码映射、超时重试。这篇文章把 uni-app 对接 Spring Boot 的所有坑和最佳实践一次讲清。
一、统一的请求封装
// utils/request.js
import { useUserStore } from '@/stores/user'
const BASE_URL_MAP = {
development: 'http://192.168.1.100:8080', // 局域网调试
production: 'https://api.your-domain.com'
}
const BASE_URL = BASE_URL_MAP[process.env.NODE_ENV]
const request = (options) => {
return new Promise((resolve, reject) => {
const userStore = useUserStore()
uni.request({
url: BASE_URL + options.url,
method: options.method || 'GET',
data: options.data || {},
header: {
'Content-Type': 'application/json',
'Authorization': userStore.token ? `Bearer ${userStore.token}` : '',
...options.header
},
timeout: options.timeout || 15000,
success: (res) => {
const { code, data, msg } = res.data
// 成功
if (code === 200) {
resolve(data)
return
}
// Token 过期
if (code === 401) {
userStore.logout()
uni.reLaunch({ url: '/pages/login/index' })
reject(new Error('登录已过期'))
return
}
// 业务异常
uni.showToast({ title: msg || '操作失败', icon: 'none' })
reject(new Error(msg))
},
fail: (err) => {
// 网络层面的错误
const errorMsg = getNetworkErrorMsg(err)
uni.showToast({ title: errorMsg, icon: 'none' })
reject(err)
}
})
})
}
// 网络错误信息映射
function getNetworkErrorMsg(err) {
if (err.errMsg?.includes('timeout')) return '请求超时,请重试'
if (err.errMsg?.includes('fail')) return '网络异常,请检查网络'
return '请求失败'
}
export default request
二、与 Spring Boot 后端的格式约定
2.1 统一响应格式
// AjaxResult.java
public class AjaxResult extends HashMap<String, Object> {
public static final int SUCCESS = 200;
public static final int UNAUTHORIZED = 401;
public static final int FORBIDDEN = 403;
public static final int ERROR = 500;
public static AjaxResult success(Object data) {
AjaxResult result = new AjaxResult();
result.put("code", SUCCESS);
result.put("data", data);
result.put("msg", "操作成功");
return result;
}
public static AjaxResult error(int code, String msg) {
AjaxResult result = new AjaxResult();
result.put("code", code);
result.put("msg", msg);
return result;
}
}
2.2 后端全局异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public AjaxResult handleBusinessException(BusinessException e) {
return AjaxResult.error(e.getCode(), e.getMessage());
}
@ExceptionHandler(Exception.class)
public AjaxResult handleException(Exception e) {
log.error("系统异常", e);
return AjaxResult.error(500, "系统异常,请联系管理员");
}
}
2.3 分页结果格式约定
// PageResult.java
public class PageResult {
private List<?> rows;
private long total;
public static AjaxResult page(List<?> rows, long total) {
Map<String, Object> map = new HashMap<>();
map.put("rows", rows);
map.put("total", total);
return AjaxResult.success(map);
}
}
// Controller
@GetMapping("/list")
public AjaxResult list(@RequestParam int pageNum, @RequestParam int pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<Customer> list = customerService.selectList();
return PageResult.page(list, new PageInfo<>(list).getTotal());
}
三、文件上传对接
3.1 前端上传
// api/upload.js
export async function uploadFile(filePath, type = 'image') {
return new Promise((resolve, reject) => {
uni.uploadFile({
url: BASE_URL + '/common/upload',
filePath: filePath,
name: 'file',
formData: { type },
header: {
Authorization: 'Bearer ' + useUserStore().token
},
success: (res) => {
const { code, data, msg } = JSON.parse(res.data)
if (code === 200) resolve(data.url)
else reject(new Error(msg))
},
fail: reject
})
})
}
3.2 后端接收
@PostMapping("/common/upload")
public AjaxResult upload(@RequestParam("file") MultipartFile file) {
String url = fileService.upload(file);
return AjaxResult.success(Map.of("url", url));
}
四、WebSocket 实时推送(uni-app 版)
车间电子看板需要实时展示生产进度,用 WebSocket:
// utils/websocket.js
class WebSocketClient {
constructor() {
this.socketTask = null
this.reconnectTimer = null
this.reconnectCount = 0
}
connect() {
const userStore = useUserStore()
this.socketTask = uni.connectSocket({
url: `wss://api.your-domain.com/ws/mes?token=${userStore.token}`,
success: () => console.log('WebSocket 连接中...')
})
this.socketTask.onOpen(() => {
console.log('WebSocket 已连接')
this.reconnectCount = 0
})
this.socketTask.onMessage((res) => {
const data = JSON.parse(res.data)
// 分发给对应页面
uni.$emit('ws-message', data)
})
this.socketTask.onClose(() => {
this.reconnect()
})
this.socketTask.onError(() => {
this.reconnect()
})
}
reconnect() {
if (this.reconnectCount >= 5) return
this.reconnectCount++
this.reconnectTimer = setTimeout(() => {
this.connect()
}, 3000 * this.reconnectCount) // 递增等待
}
send(data) {
if (this.socketTask) {
this.socketTask.send({ data: JSON.stringify(data) })
}
}
close() {
if (this.socketTask) {
this.socketTask.close()
}
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer)
}
}
}
export const wsClient = new WebSocketClient()
五、错误处理体系
// utils/errorHandler.js
export const ERROR_MAP = {
400: '请求参数有误',
401: '登录已过期',
403: '没有操作权限',
404: '请求的资源不存在',
500: '服务器异常',
502: '网关异常',
503: '服务暂时不可用'
}
export function handleHttpError(code, defaultMsg) {
const msg = ERROR_MAP[code] || defaultMsg || '未知错误'
if (code === 401) {
// 避免多次弹窗
uni.showModal({
title: '登录过期',
content: '您的登录已过期,请重新登录',
showCancel: false,
success: () => {
const userStore = useUserStore()
userStore.logout()
}
})
} else if (code === 403) {
uni.showToast({ title: msg, icon: 'none' })
} else {
uni.showToast({ title: msg, icon: 'none' })
}
}
六、开发调试技巧
6.1 真机调试本地后端
// 使用局域网 IP
const BASE_URL = 'http://192.168.1.100:8080'
// 如果后端加了跨域
// 小程序不需要关心跨域,App 端需要后端配置 CORS
// 后端 CORS 配置
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedMethods("*")
.allowedHeaders("*")
.allowCredentials(true);
}
};
}
}
6.2 请求日志
// 开发环境下打印请求日志
if (process.env.NODE_ENV === 'development') {
console.log(`[Request] ${options.method} ${options.url}`)
console.log('[Request Data]', options.data)
}
七、总结
uni-app 对接 Spring Boot 的核心要点:
| 要点 | 做法 |
|---|---|
| 统一响应格式 | 后端 AjaxResult,前端根据 code 分流 |
| Token 传递 | 请求拦截器自动注入 Header |
| 文件上传 | uni.uploadFile + MultipartFile |
| 实时推送 | uni.connectSocket + Spring WebSocket |
| 错误码映射 | 前后端一致维护 ERROR_MAP |
| 真机调试 | 局域网 IP + CORS 配置 |
如果你也在独立开发产品,或者对制造业数字化感兴趣,欢迎关注这个公众号。我会持续分享从代码到产品的全过程——包括成功的经验,也包括踩过的坑。
一个人的产品之路,不孤单。👇
原创作者 MqCode(全栈开发者,印刷包装行业 MES+CRM 系统独立开发),欢迎自由转发。
更多推荐



所有评论(0)