request.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. /**
  2. * 网络请求工具类
  3. * 封装uni-app的request方法
  4. */
  5. import errorHandler from './errorHandler.js';
  6. import { apiBaseUrl } from '@/common/config.js';
  7. // 基础URL,可以根据环境变量等动态设置
  8. const BASE_URL = apiBaseUrl;
  9. // 请求超时时间
  10. const TIMEOUT = 60000;
  11. // 请求拦截器
  12. const requestInterceptor = (config) => {
  13. // 获取token
  14. const token = uni.getStorageSync('token');
  15. // 获取 CSRF token
  16. const csrfToken = uni.getStorageSync('csrfToken');
  17. // 设置请求头
  18. if (!config.header) {
  19. config.header = {};
  20. }
  21. // 添加token到请求头
  22. if (token) {
  23. config.header['Authorization'] = `Bearer ${token}`;
  24. }
  25. // 添加 CSRF token 到请求头
  26. if (csrfToken) {
  27. config.header['X-CSRF-Token'] = csrfToken;
  28. }
  29. // 添加内容类型
  30. if (!config.header['Content-Type']) {
  31. config.header['Content-Type'] = 'application/json';
  32. }
  33. // 添加基础URL
  34. if (!config.url.startsWith('http')) {
  35. config.url = BASE_URL + config.url;
  36. }
  37. // 如果是 POST 请求,也可以添加到请求体
  38. if (config.method === 'POST' && config.data) {
  39. config.data._csrf = csrfToken;
  40. }
  41. return config;
  42. };
  43. // 错误提示
  44. const showError = (message, duration = 2000) => {
  45. // 避免重复显示相同的错误提示
  46. if (showError.lastMessage === message && Date.now() - showError.lastTime < 3000) {
  47. return;
  48. }
  49. uni.showToast({
  50. title: message,
  51. icon: 'none',
  52. duration: duration
  53. });
  54. // 记录最后显示的错误信息和时间
  55. showError.lastMessage = message;
  56. showError.lastTime = Date.now();
  57. };
  58. // 初始化错误提示状态
  59. showError.lastMessage = '';
  60. showError.lastTime = 0;
  61. // 错误码映射表
  62. const ERROR_CODE_MAP = {
  63. 400: '请求参数错误',
  64. 401: '登录已过期,请重新登录',
  65. 403: '没有权限执行此操作',
  66. 404: '请求的资源不存在',
  67. 500: '服务器错误,请稍后重试',
  68. 502: '网关错误',
  69. 503: '服务不可用,请稍后重试',
  70. 504: '网关超时'
  71. };
  72. // 响应拦截器
  73. const responseInterceptor = (response) => {
  74. // 这里可以对响应数据做统一处理
  75. if (response.statusCode === 200) {
  76. // 服务器正常响应
  77. const { data } = response;
  78. // 检查是否有特定错误码
  79. if (data.status === 999 || data.code === 999) {
  80. // 微信 code 无效错误
  81. const error = new Error(data.message || '微信登录失败: code无效');
  82. error.status = 999;
  83. return Promise.reject(error);
  84. }
  85. // 处理成功响应 - 适配不同的返回格式
  86. if (data.status === 2000 || data.code === 0 || data.code === 2000) {
  87. // 如果返回的是完整数据对象,直接返回
  88. if (data.data) {
  89. return data.data;
  90. }
  91. // 如果没有data字段,但有其他用户信息字段,返回整个对象
  92. if (data.openid || data.userId || data.user_id) {
  93. return data;
  94. }
  95. // 默认返回
  96. return data;
  97. } else {
  98. // 业务错误
  99. const error = errorHandler.handleBusinessError(data);
  100. return Promise.reject(error);
  101. }
  102. } else {
  103. // HTTP错误
  104. const error = errorHandler.handleHttpError(response.statusCode, response);
  105. return Promise.reject(error);
  106. }
  107. };
  108. // 请求方法
  109. const request = (options = {}) => {
  110. return new Promise((resolve, reject) => {
  111. // 应用请求拦截器
  112. options = requestInterceptor(options);
  113. // 设置超时时间
  114. if (!options.timeout) {
  115. options.timeout = TIMEOUT;
  116. }
  117. // 发起请求
  118. uni.request({
  119. ...options,
  120. success: (res) => {
  121. try {
  122. // 应用响应拦截器
  123. const data = responseInterceptor(res);
  124. resolve(data);
  125. } catch (error) {
  126. errorHandler.logError(error, 'request.responseInterceptor');
  127. reject(error);
  128. }
  129. },
  130. fail: (err) => {
  131. const error = errorHandler.handleRequestFail(err);
  132. errorHandler.logError(error, 'request.fail');
  133. reject(error);
  134. }
  135. });
  136. });
  137. };
  138. // 封装常用请求方法
  139. const http = {
  140. get(url, data = {}, options = {}) {
  141. return request({
  142. url,
  143. data,
  144. method: 'GET',
  145. ...options
  146. });
  147. },
  148. post(url, data = {}, options = {}) {
  149. return request({
  150. url,
  151. data,
  152. method: 'POST',
  153. ...options
  154. });
  155. },
  156. put(url, data = {}, options = {}) {
  157. return request({
  158. url,
  159. data,
  160. method: 'PUT',
  161. ...options
  162. });
  163. },
  164. delete(url, data = {}, options = {}) {
  165. return request({
  166. url,
  167. data,
  168. method: 'DELETE',
  169. ...options
  170. });
  171. },
  172. // 上传文件
  173. upload(url, filePath, name = 'file', formData = {}, options = {}) {
  174. return new Promise((resolve, reject) => {
  175. // 获取token
  176. const token = uni.getStorageSync('token');
  177. // 设置请求头
  178. const header = options.header || {};
  179. if (token) {
  180. header['Authorization'] = `Bearer ${token}`;
  181. }
  182. // 添加基础URL
  183. if (!url.startsWith('http')) {
  184. url = BASE_URL + url;
  185. }
  186. uni.uploadFile({
  187. url,
  188. filePath,
  189. name,
  190. formData,
  191. header,
  192. success: (res) => {
  193. try {
  194. // 上传接口可能返回的是字符串
  195. if (typeof res.data === 'string') {
  196. res.data = JSON.parse(res.data);
  197. }
  198. // 应用响应拦截器
  199. const data = responseInterceptor({
  200. statusCode: res.statusCode,
  201. data: res.data
  202. });
  203. resolve(data);
  204. } catch (error) {
  205. reject(error);
  206. }
  207. },
  208. fail: (err) => {
  209. showError('文件上传失败');
  210. reject(err);
  211. }
  212. });
  213. });
  214. }
  215. };
  216. export default http;