request.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. /**
  2. * 和服务端进行交互
  3. * 它封装了全局 request拦截器、respone拦截器、统一的错误处理、统一做了超时,baseURL设置等
  4. * <pre>
  5. * 作者:hugh zhuang
  6. * 邮箱:3378340995@qq.com
  7. * 日期:2015-11-02-下午3:29:34
  8. * 版权:广州流辰信息技术有限公司
  9. * </pre>
  10. */
  11. import axios from 'axios'
  12. import { Message, MessageBox } from 'element-ui'
  13. import store from '@/store'
  14. import router from '@/router'
  15. // Internationalization 国际化
  16. import I18n from '@/utils/i18n'
  17. import Utils from '@/utils/util'
  18. import Ids from 'ids'
  19. import setting from '@/setting.js'
  20. import { encryptByAes } from '@/utils/encrypt'
  21. // 验权
  22. import { getToken, updateToken, removeRefreshToken } from '@/utils/auth'
  23. import { refreshAccessToken } from '@/api/oauth2/user'
  24. import { showFullScreenLoading, tryHideFullScreenLoading } from './loading'
  25. import requestState from '@/constants/state'
  26. import { BASE_API, BASE_GATEWAY_API } from '@/api/baseUrl'
  27. import {
  28. HEADER_TOKEN_KEY,
  29. HEADER_SYSTEM_ID,
  30. HEADER_TENANT_ID,
  31. MULTIPLE_DOMAIN,
  32. API_DOMAIN_NAMES,
  33. ENCRYPT_GET_PARAMS
  34. } from '@/constant'
  35. // 请求超时(timeout)时间
  36. const TIMEOUT = setting.requestTimeout
  37. /**
  38. * 创建axios实例
  39. */
  40. const service = axios.create({
  41. // request timeout
  42. timeout: TIMEOUT,
  43. // 跨域安全策略
  44. withCredentials: true,
  45. headers: {
  46. 'Cache-Control': 'no-cache',
  47. 'Content-Type': 'application/json;charset=utf-8'
  48. }
  49. })
  50. // 是否正在刷新的标记
  51. let isRefreshing = false
  52. // 重试队列,每一项将是一个待执行的函数形式
  53. let requests = []
  54. // 取消请求
  55. let cancelRequest = false
  56. let requestCount = 0
  57. /**
  58. * 请求(request)拦截器
  59. *
  60. * get请求,统一参数放在params里面,后台对应只有@RequestParam
  61. * `params`是即将与请求一起发送的 URL 参数,必须是一个无格式对象(plain object)或 URLSearchParams 对象
  62. *
  63. * post请求,统一参数放在data里面——json格式,后台对应@RequestBody ,其他 后台对应@RequestParam
  64. * `data` 是作为请求主体被发送的数据
  65. * 只适用于这些请求方法 'PUT', 'POST', 和 'PATCH'
  66. * 在没有设置 `transformRequest` 时,必须是以下类型之一:
  67. * - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
  68. * - 浏览器专属:FormData, File, Blob
  69. * - Node 专属: Stream
  70. * post 请求 `params` 这个同get 但要注意 后台对应@RequestParam 请求的`Content-Type`是 application/x-www-form-urlencoded 用 qs.stringify 去构造数据
  71. */
  72. service.interceptors.request.use(
  73. async (config) => {
  74. console.log('config===>', config)
  75. if (!config.baseURL) {
  76. config.baseURL = BASE_API(parseInt(requestCount / 5, 10))
  77. if (MULTIPLE_DOMAIN) {
  78. requestCount >= (API_DOMAIN_NAMES.length - 1) * 5
  79. ? (requestCount = 0)
  80. : requestCount++
  81. }
  82. }
  83. if (config.gateway) {
  84. config.baseURL = BASE_GATEWAY_API()
  85. console.log(BASE_GATEWAY_API());
  86. }
  87. if (config.url == '/jyxt/SampleProcess/getSampleList') {
  88. console.log('aaaaa');
  89. config.baseURL = ''
  90. }
  91. config.isLoading =
  92. config.isLoading !== undefined && config.isLoading !== null
  93. ? config.isLoading
  94. : false
  95. if (config.isLoading) {
  96. showFullScreenLoading(config.loading)
  97. }
  98. // 如果超时不对还原默认超时时间
  99. if (config.timeout !== TIMEOUT) {
  100. config.timeout = TIMEOUT
  101. }
  102. // 只设置当前的时间设置以设置为准
  103. if (Utils.isNotEmpty(config.overtime)) {
  104. config.timeout = config.overtime
  105. }
  106. // 防止缓存,参数加密
  107. if (config.method.toUpperCase() === 'GET') {
  108. if (ENCRYPT_GET_PARAMS) {
  109. config.params = {
  110. _p: Utils.isNotEmpty(config.params)
  111. ? encryptByAes(JSON.stringify(config.params), 'get')
  112. : undefined,
  113. _t: new Ids([32, 36, 1]).next()
  114. }
  115. } else {
  116. config.params = {
  117. ...config.params,
  118. _t: new Ids([32, 36, 1]).next()
  119. }
  120. }
  121. }
  122. // 判断是否需要token
  123. if (setting.whiteRequestList.indexOf(config.url) !== -1) {
  124. return config
  125. }
  126. config.headers[HEADER_TOKEN_KEY] = getToken()
  127. // 系统ID
  128. if (store && store.getters.systemid) {
  129. config.headers[HEADER_SYSTEM_ID] = store.getters.systemid
  130. }
  131. // 租户ID
  132. if (store && store.getters.tenantid) {
  133. config.headers[HEADER_TENANT_ID] = store.getters.designTenantid
  134. ? store.getters.designTenantid
  135. : store.getters.tenantid
  136. }
  137. // 租户模式
  138. if (store && store.getters.isTenantOpen) {
  139. config.headers[HEADER_TENANT_ID] = store.getters.designTenantid
  140. ? store.getters.designTenantid
  141. : store.getters.tenantid
  142. }
  143. return config
  144. },
  145. (error) => {
  146. tryHideFullScreenLoading()
  147. // Do something with request error
  148. // for debug
  149. Utils.log.error('request' + error)
  150. Promise.reject(error)
  151. }
  152. )
  153. /**
  154. * 响应(respone)拦截器
  155. */
  156. service.interceptors.response.use(
  157. (response) => {
  158. // 临时关闭了保存等待的取消功能
  159. tryHideFullScreenLoading()
  160. const dataAxios = response.data
  161. const { state, message, cause } = dataAxios
  162. // 获取接口保留参数,用于部分接口的后续请求
  163. const { retainData } = response.config
  164. if (response.config.responseType === 'arraybuffer') {
  165. // 刷新tonken
  166. return response
  167. }
  168. // 如果没有 state 代表这不是项目后端开发的接口 比如可能是请求最新版本,或者是请求的数据,或者是
  169. if (state === undefined) {
  170. const msg = '接口异常,没有返回[state]参数</br>' + response.config.url
  171. Message.closeAll()
  172. Message({
  173. message: `${msg}`,
  174. type: 'error',
  175. showClose: true,
  176. dangerouslyUseHTMLString: true,
  177. duration: 10000
  178. })
  179. return
  180. }
  181. // state为200是正确的请求 或者 验证码问题,或者警告类型的错误 自行处理
  182. if (
  183. state === requestState.SUCCESS ||
  184. state === requestState.UNSUPORT ||
  185. state === requestState.WARNING ||
  186. state === requestState.WARN
  187. ) {
  188. return { ...dataAxios, retainData }
  189. }
  190. // 处理刷新tonken问题,说明token过期了,刷新token
  191. if (state === requestState.TOKEN_EXPIRED) {
  192. const config = response.config
  193. if (!isRefreshing) {
  194. isRefreshing = true
  195. return refreshAccessToken()
  196. .then((res) => {
  197. const data = res.data
  198. updateToken(data)
  199. const token = getToken()
  200. config.headers[HEADER_TOKEN_KEY] = token
  201. // 已经刷新了token,将所有队列中的请求进行重试
  202. requests.forEach((cb) => cb(token))
  203. requests = []
  204. return service(config)
  205. })
  206. .catch((res) => {
  207. console.error('refreshtoken error =>', res)
  208. removeRefreshToken()
  209. window.location.href = this.$baseUrl
  210. })
  211. .finally(() => {
  212. isRefreshing = false
  213. })
  214. } else {
  215. // 正在刷新token,将返回一个未执行resolve的promise
  216. return new Promise((resolve) => {
  217. // 将resolve放进队列,用一个函数形式来保存,等token刷新后直接执行
  218. requests.push((token) => {
  219. config.headers[HEADER_TOKEN_KEY] = token
  220. resolve(service(config))
  221. })
  222. })
  223. }
  224. // 6020201:非法的token;6020202:其他客户端登录了;6020301:Token 过期了;
  225. } else if (
  226. state === requestState.ILLEGAL_TOKEN ||
  227. state === requestState.OTHER_CLIENTS
  228. ) {
  229. // if (!cancelRequest) {
  230. cancelRequest = false
  231. MessageBox.confirm(
  232. I18n.t('error.logout.message'),
  233. I18n.t('error.logout.title'),
  234. {
  235. confirmButtonText: I18n.t('error.logout.confirmButtonText'),
  236. cancelButtonText: I18n.t('error.logout.cancelButtonText'),
  237. type: 'warning'
  238. }
  239. )
  240. .then(() => {
  241. store
  242. .dispatch('ibps/account/fedLogout')
  243. .then(() => {
  244. // 终止所有请求
  245. cancelRequest = true
  246. router.push({
  247. name: 'login'
  248. })
  249. })
  250. .catch(() => {
  251. cancelRequest = false
  252. })
  253. })
  254. .finally(() => {
  255. cancelRequest = false
  256. })
  257. // }
  258. return Promise.reject(new Error(message))
  259. } else {
  260. // 错误处理
  261. let errorMsg = ''
  262. if (Utils.isNotEmpty(message)) {
  263. // 有错误消息
  264. errorMsg = Utils.isNotEmpty(dataAxios.cause)
  265. ? I18n.t('error.messageCause', {
  266. message,
  267. cause: dataAxios.cause
  268. })
  269. : I18n.t('error.message', {
  270. message
  271. })
  272. } else if (Utils.isNotEmpty(cause)) {
  273. // 只有错误原因
  274. errorMsg = I18n.t('error.cause', {
  275. cause
  276. })
  277. } else if (I18n.te('error.status.' + state)) {
  278. // 有错误编码
  279. errorMsg = I18n.t('error.status.' + state)
  280. } else {
  281. // 未知
  282. errorMsg =
  283. message ||
  284. I18n.t('error.unknown', {
  285. state
  286. })
  287. }
  288. Message.closeAll()
  289. Message({
  290. message: `${errorMsg}`,
  291. type: 'error',
  292. showClose: true,
  293. dangerouslyUseHTMLString: true,
  294. duration: 5 * 1000
  295. })
  296. const err = new Error(errorMsg)
  297. err.state = state
  298. err.cause = cause
  299. return Promise.reject(err)
  300. }
  301. },
  302. (error) => {
  303. // 异常处理
  304. tryHideFullScreenLoading()
  305. // for debug
  306. console.error('request-error', error)
  307. if (error && error.response) {
  308. error.message = I18n.t('error.status.' + error.response.status, {
  309. url: error.response.config.url
  310. })
  311. } else {
  312. error.state = 500
  313. // '服务器君开小差了,请稍后再试'
  314. error.message = I18n.t('error.network')
  315. }
  316. Message.closeAll()
  317. Message({
  318. message: error.message || I18n.t('error.network'),
  319. type: 'error',
  320. showClose: true,
  321. duration: 5 * 1000
  322. })
  323. return Promise.reject(error)
  324. }
  325. )
  326. export default service