# 前端业务错误码

# 开始


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

# 布码

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

import Axios from 'axios'
import router from 'umi/router'
import settings from '@@/settings'
import Toast from '@@/components/Toast'
import pathToRegexp from 'path-to-regexp'
import UAParser from 'ua-parser-js'

const { token, userId } = window.APP_ACCOUNT
const parser = new UAParser()
const version = parser.getOS().name + parser.getOS().version
const manufacturer = parser.getDevice().vendor

const sessionInfo = {
  'x-request-token': token,
  'x-user-id': userId,
  'x-client-id': 'qh0bo06P',
  'app-id': settings.wechatAppId,
  'x-system-version': version,
  'x-device-manufacturer': manufacturer,
}

const productUrlFormat = [
  {
    check: () => {
      const regexp = pathToRegexp('/course/resource/:uniqueId')
      const resource = regexp.exec(window.location.pathname)
      return resource && resource[1].split('-')[0]
    },
  },
  {
    check: () => {
      const regexp = pathToRegexp('/course/product/archive/:productId')
      const archive = regexp.exec(window.location.pathname)
      return archive && archive[1]
    },
  },
]

const getPathname = () => {
  let productId = ''
  productUrlFormat.find(item => {
    const currentId = item.check()
    if (currentId) {
      productId = currentId
    }
  })
  return productId ? `/course/product/${productId}` : '/course'
}

const setInterceptors = instance => {
  instance.interceptors.request.use(config => {
    config.headers = Object.assign({}, config.headers, sessionInfo)
    config.headers['x-timestamp'] = new Date().valueOf()

    return config
  })

  instance.interceptors.response.use(
    response => {
      response.data.data = response.data.data || {}

      if (response.data.status === 200 && (response.data.code === 0 || response.data.code === 200)) {
        return Promise.resolve(response.data)
      }

      // 35103 => 用户未绑定手机号
      const checkPhone = [35103]
      if (checkPhone.includes(response.data.code)) {
        const pathname = window.location.pathname
        const search = window.location.search
        router.replace({ pathname: '/course/account/phone', state: { targetUrl: pathname + search } })
        return Promise.reject(response.data)
      }

      // 35101 => 用户没有购买训练营
      // 35102 => 用户未购买此商品
      const checkLesson = [35101, 35102]
      if (checkLesson.includes(response.data.code)) {
        const pathname = getPathname()
        router.replace({ pathname })
        return Promise.reject(response.data)
      }

      response.data.message = response.data.message || '数据请求失败'
      Toast.show(response.data.message)
      return Promise.reject(response.data)
    },
    error => {
      if (!error.response) {
        return Promise.reject(new Error('网络连接异常'))
      } else {
        error.response.code = error.response.code || error.response.status
        switch (error.response.status) {
          case 400:
            error.response.message = '错误请求'
            break
          case 401:
            error.response.message = '未授权'
            break
          case 403:
            error.response.message = '拒绝访问'
            break
          case 404:
            error.response.message = '未找到该资源'
            break
          case 405:
            error.response.message = '请求方法未允许'
            break
          case 408:
            error.response.message = '请求超时'
            break
          case 429:
            error.response.message = '请求频繁'
            break
          case 500:
            error.response.message = '服务器端出错'
            break
          case 501:
            error.response.message = '网络未实现'
            break
          case 502:
            error.response.message = '网络错误'
            break
          case 503:
            error.response.message = '服务不可用'
            break
          case 504:
            error.response.message = '网络超时'
            break
          case 505:
            error.response.message = '不支持该请求'
            break
          default:
            error.response.message = error.response.status
        }

        return Promise.reject(error.response)
      }
    }
  )

  return instance
}

const apiInstance = Axios.create({ baseURL: `${settings.apiServer}` })
const courseInstance = Axios.create({ baseURL: `${settings.courseServer}` })
const fundInstance = Axios.create({ baseURL: `${settings.fundServer}` })

export const apiRequest = setInterceptors(apiInstance)
export const courseRequest = setInterceptors(courseInstance)
export const fundRequest = setInterceptors(fundInstance)


settings.js

// 这里返回基础设置 settings
const { flowTrackId, wechatAppId, apiServer, courseServer, fundServer, fundPageUrl } = window.APP_CONFIG
const {
  globalConfig: { autoGroupingPopupLimit },
} = window.APP_STATE

const appEnv = window.APP_ENV
const sentryRelease = process.env.SENTRY_RELEASE

const settings = {
  appEnv,
  sentryRelease,
  flowTrackId,
  wechatAppId,
  apiServer,
  courseServer,
  fundServer,
  fundPageUrl,
  autoGroupingPopupLimit,
}

export default settings