开始

关于错误码,建议不要后端控制,逻辑问题由前端自己控制。

布码

image.png
类似这样的业务逻辑,应该先把所有的接口进行一次封装,有一个统一的调用、报错流程。
错误码的目的是为了把错误抛出来,不是做优化(发现错误跳转正常路径),所以只有少数的错误需要跳转,更改逻辑,大部分的业务错误码,不作处理,只是报错
axios.js

  1. import Axios from 'axios'
  2. import router from 'umi/router'
  3. import settings from '@@/settings'
  4. import Toast from '@@/components/Toast'
  5. import pathToRegexp from 'path-to-regexp'
  6. import UAParser from 'ua-parser-js'
  7. const { token, userId } = window.APP_ACCOUNT
  8. const parser = new UAParser()
  9. const version = parser.getOS().name + parser.getOS().version
  10. const manufacturer = parser.getDevice().vendor
  11. const sessionInfo = {
  12. 'x-request-token': token,
  13. 'x-user-id': userId,
  14. 'x-client-id': 'qh0bo06P',
  15. 'app-id': settings.wechatAppId,
  16. 'x-system-version': version,
  17. 'x-device-manufacturer': manufacturer,
  18. }
  19. const productUrlFormat = [
  20. {
  21. check: () => {
  22. const regexp = pathToRegexp('/course/resource/:uniqueId')
  23. const resource = regexp.exec(window.location.pathname)
  24. return resource && resource[1].split('-')[0]
  25. },
  26. },
  27. {
  28. check: () => {
  29. const regexp = pathToRegexp('/course/product/archive/:productId')
  30. const archive = regexp.exec(window.location.pathname)
  31. return archive && archive[1]
  32. },
  33. },
  34. ]
  35. const getPathname = () => {
  36. let productId = ''
  37. productUrlFormat.find(item => {
  38. const currentId = item.check()
  39. if (currentId) {
  40. productId = currentId
  41. }
  42. })
  43. return productId ? `/course/product/${productId}` : '/course'
  44. }
  45. const setInterceptors = instance => {
  46. instance.interceptors.request.use(config => {
  47. config.headers = Object.assign({}, config.headers, sessionInfo)
  48. config.headers['x-timestamp'] = new Date().valueOf()
  49. return config
  50. })
  51. instance.interceptors.response.use(
  52. response => {
  53. response.data.data = response.data.data || {}
  54. if (response.data.status === 200 && (response.data.code === 0 || response.data.code === 200)) {
  55. return Promise.resolve(response.data)
  56. }
  57. // 35103 => 用户未绑定手机号
  58. const checkPhone = [35103]
  59. if (checkPhone.includes(response.data.code)) {
  60. const pathname = window.location.pathname
  61. const search = window.location.search
  62. router.replace({ pathname: '/course/account/phone', state: { targetUrl: pathname + search } })
  63. return Promise.reject(response.data)
  64. }
  65. // 35101 => 用户没有购买训练营
  66. // 35102 => 用户未购买此商品
  67. const checkLesson = [35101, 35102]
  68. if (checkLesson.includes(response.data.code)) {
  69. const pathname = getPathname()
  70. router.replace({ pathname })
  71. return Promise.reject(response.data)
  72. }
  73. response.data.message = response.data.message || '数据请求失败'
  74. Toast.show(response.data.message)
  75. return Promise.reject(response.data)
  76. },
  77. error => {
  78. if (!error.response) {
  79. return Promise.reject(new Error('网络连接异常'))
  80. } else {
  81. error.response.code = error.response.code || error.response.status
  82. switch (error.response.status) {
  83. case 400:
  84. error.response.message = '错误请求'
  85. break
  86. case 401:
  87. error.response.message = '未授权'
  88. break
  89. case 403:
  90. error.response.message = '拒绝访问'
  91. break
  92. case 404:
  93. error.response.message = '未找到该资源'
  94. break
  95. case 405:
  96. error.response.message = '请求方法未允许'
  97. break
  98. case 408:
  99. error.response.message = '请求超时'
  100. break
  101. case 429:
  102. error.response.message = '请求频繁'
  103. break
  104. case 500:
  105. error.response.message = '服务器端出错'
  106. break
  107. case 501:
  108. error.response.message = '网络未实现'
  109. break
  110. case 502:
  111. error.response.message = '网络错误'
  112. break
  113. case 503:
  114. error.response.message = '服务不可用'
  115. break
  116. case 504:
  117. error.response.message = '网络超时'
  118. break
  119. case 505:
  120. error.response.message = '不支持该请求'
  121. break
  122. default:
  123. error.response.message = error.response.status
  124. }
  125. return Promise.reject(error.response)
  126. }
  127. }
  128. )
  129. return instance
  130. }
  131. const apiInstance = Axios.create({ baseURL: `${settings.apiServer}` })
  132. const courseInstance = Axios.create({ baseURL: `${settings.courseServer}` })
  133. const fundInstance = Axios.create({ baseURL: `${settings.fundServer}` })
  134. export const apiRequest = setInterceptors(apiInstance)
  135. export const courseRequest = setInterceptors(courseInstance)
  136. export const fundRequest = setInterceptors(fundInstance)

settings.js

  1. // 这里返回基础设置 settings
  2. const { flowTrackId, wechatAppId, apiServer, courseServer, fundServer, fundPageUrl } = window.APP_CONFIG
  3. const {
  4. globalConfig: { autoGroupingPopupLimit },
  5. } = window.APP_STATE
  6. const appEnv = window.APP_ENV
  7. const sentryRelease = process.env.SENTRY_RELEASE
  8. const settings = {
  9. appEnv,
  10. sentryRelease,
  11. flowTrackId,
  12. wechatAppId,
  13. apiServer,
  14. courseServer,
  15. fundServer,
  16. fundPageUrl,
  17. autoGroupingPopupLimit,
  18. }
  19. export default settings