request.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  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. console.log('response:', response);
  75. // 这里可以对响应数据做统一处理
  76. if (response.statusCode === 200||response.statusCode === 400) {
  77. // 服务器正常响应
  78. const { data } = response;
  79. // 检查是否有特定错误码
  80. if (data.status === 999 || data.code === 999) {
  81. // 特殊业务错误:例如职位已截止申请
  82. const error = new Error((data && (data.msg || data.message)) || '请求失败');
  83. error.status = 999;
  84. error.code = 999;
  85. error.data = data;
  86. return Promise.reject(error);
  87. }
  88. // 处理成功响应 - 适配不同的返回格式
  89. if (data.status === 2000 || data.code === 0 || data.code === 2000) {
  90. // 如果返回的是完整数据对象,直接返回
  91. if (data.data) {
  92. return data.data;
  93. }
  94. // 如果没有data字段,但有其他用户信息字段,返回整个对象
  95. if (data.openid || data.userId || data.user_id) {
  96. return data;
  97. }
  98. // 默认返回
  99. return data;
  100. } else {
  101. // 业务错误
  102. const error = errorHandler.handleBusinessError(data);
  103. return Promise.reject(error);
  104. }
  105. } else {
  106. // HTTP错误
  107. const error = errorHandler.handleHttpError(response.statusCode, response);
  108. return Promise.reject(error);
  109. }
  110. };
  111. // 请求方法
  112. const request = (options = {}) => {
  113. return new Promise((resolve, reject) => {
  114. // 应用请求拦截器
  115. options = requestInterceptor(options);
  116. // 设置超时时间
  117. if (!options.timeout) {
  118. options.timeout = TIMEOUT;
  119. }
  120. // 发起请求
  121. uni.request({
  122. ...options,
  123. success: (res) => {
  124. try {
  125. // 应用响应拦截器
  126. const data = responseInterceptor(res);
  127. resolve(data);
  128. } catch (error) {
  129. errorHandler.logError(error, 'request.responseInterceptor');
  130. reject(error);
  131. }
  132. },
  133. fail: (err) => {
  134. const error = errorHandler.handleRequestFail(err);
  135. errorHandler.logError(error, 'request.fail');
  136. reject(error);
  137. }
  138. });
  139. });
  140. };
  141. // 封装常用请求方法
  142. const http = {
  143. get(url, data = {}, options = {}) {
  144. return request({
  145. url,
  146. data,
  147. method: 'GET',
  148. ...options
  149. });
  150. },
  151. post(url, data = {}, options = {}) {
  152. return request({
  153. url,
  154. data,
  155. method: 'POST',
  156. ...options
  157. });
  158. },
  159. put(url, data = {}, options = {}) {
  160. return request({
  161. url,
  162. data,
  163. method: 'PUT',
  164. ...options
  165. });
  166. },
  167. delete(url, data = {}, options = {}) {
  168. return request({
  169. url,
  170. data,
  171. method: 'DELETE',
  172. ...options
  173. });
  174. },
  175. // 上传文件
  176. upload(url, filePath, name = 'file', formData = {}, options = {}) {
  177. return new Promise((resolve, reject) => {
  178. // 获取token
  179. const token = uni.getStorageSync('token');
  180. // 设置请求头
  181. const header = options.header || {};
  182. if (token) {
  183. header['Authorization'] = `Bearer ${token}`;
  184. }
  185. // 添加基础URL
  186. if (!url.startsWith('http')) {
  187. url = BASE_URL + url;
  188. }
  189. uni.uploadFile({
  190. url,
  191. filePath,
  192. name,
  193. formData,
  194. header,
  195. success: (res) => {
  196. try {
  197. // 上传接口可能返回的是字符串
  198. if (typeof res.data === 'string') {
  199. res.data = JSON.parse(res.data);
  200. }
  201. // 应用响应拦截器
  202. const data = responseInterceptor({
  203. statusCode: res.statusCode,
  204. data: res.data
  205. });
  206. resolve(data);
  207. } catch (error) {
  208. reject(error);
  209. }
  210. },
  211. fail: (err) => {
  212. showError('文件上传失败');
  213. reject(err);
  214. }
  215. });
  216. });
  217. }
  218. };
  219. export default http;