axios.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. import Vue from "vue";
  2. import axios from "axios";
  3. import router from "../router";
  4. import { loadProgressBar } from "axios-progress-bar";
  5. import networkInformationHint from "./networkInformationHint.js";
  6. import { initSyncTime, fetchTime } from "./syncServerTime";
  7. import { getAuthorization } from "./crypto";
  8. function setAuth(config) {
  9. let userSession = sessionStorage.getItem("user");
  10. // console.log("userSession", userSession);
  11. if (userSession && !config.noAuth) {
  12. let user = JSON.parse(userSession);
  13. const timestamp = fetchTime();
  14. const authorization = getAuthorization(
  15. {
  16. method: config.method,
  17. uri: config.url.split("?")[0].trim(),
  18. timestamp,
  19. sessionId: user.sessionId,
  20. token: user.accessToken,
  21. },
  22. "token"
  23. );
  24. config.headers["Authorization"] = authorization;
  25. config.headers["time"] = timestamp;
  26. }
  27. }
  28. const ERROR_MSG_CONFIG = require("./errorMsgConfig").default;
  29. // Full config: https://github.com/axios/axios#request-config
  30. // axios.defaults.baseURL = process.env.baseURL || process.env.apiUrl || '';
  31. // axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
  32. // axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
  33. let config = {
  34. // baseURL: process.env.baseURL || process.env.apiUrl || ""
  35. timeout: 60 * 1000, // Timeout
  36. withCredentials: true, // Check cross-site Access-Control
  37. };
  38. const _$httpWith500Msg = axios.create(config);
  39. const _$http = axios.create(config); // no auto 500 error UI
  40. const _$httpWithoutBar = axios.create(config);
  41. const noauthUrls = [
  42. "/auth/login",
  43. "/auth/version",
  44. "/auth/thirdPartyAccess",
  45. "/sso/login",
  46. ];
  47. /**
  48. * A. token lifecycle
  49. * 1. /login UI => localStorage.removeItem('token') && localStorage.setItem('token')
  50. * 2. non /login UI => axios if(!wk_token) wk_token = window.sessionStorage.getItem("token"), send request
  51. * 3. if axios request fail with 401/403, wk_token = null, redirect to /login removeItem('token')
  52. * 4. logout to /login, before send request, invalidate wk_token
  53. * */
  54. let wk_token;
  55. let wk_orgId;
  56. function getRootOrgId() {
  57. if (location.hostname.includes("qmth.com.cn")) {
  58. return "";
  59. } else {
  60. return "?orgId=" + (wk_orgId === undefined ? "" : wk_orgId);
  61. }
  62. }
  63. function returnLogin() {
  64. const returnUrl = window.sessionStorage.getItem("returnUrl");
  65. if (returnUrl) {
  66. window.location.href = returnUrl;
  67. return;
  68. }
  69. router.push("/login/" + getRootOrgId());
  70. }
  71. _$httpWith500Msg.interceptors.request.use(
  72. function (config) {
  73. setAuth(config);
  74. networkInformationHint();
  75. // Do something before request is sent
  76. if (!noauthUrls.some((url) => config.url.includes(url))) {
  77. if (!wk_token) {
  78. const user = JSON.parse(window.sessionStorage.getItem("user"));
  79. if (!user) {
  80. if (
  81. window.___lastInvalidDate === undefined ||
  82. window.___lastInvalidDate < Date.now() - 300
  83. ) {
  84. Vue.prototype.$alert("登录失效,请重新登录!", "提示", {
  85. confirmButtonText: "确定",
  86. callback: () => {
  87. // if (process.env.NODE_ENV !== "production") {
  88. // router.push("/login/" + "?orgId=" + wk_orgId);
  89. // } else {
  90. // router.push("/login");
  91. // }
  92. returnLogin();
  93. },
  94. });
  95. window.___lastInvalidDate = Date.now();
  96. }
  97. return;
  98. }
  99. // wk_token = user.token;
  100. // wk_key = user.key;
  101. wk_orgId = user.rootOrgId;
  102. }
  103. if (wk_token && config.headers.common["token"] == null) {
  104. // config.headers.common["token"] = wk_token;
  105. // config.headers.common["key"] = wk_key;
  106. }
  107. } else {
  108. wk_token = null;
  109. }
  110. return config;
  111. },
  112. function (error) {
  113. // Do something with request error
  114. Vue.prototype.$notify({
  115. showClose: true,
  116. message: error,
  117. type: "error",
  118. });
  119. return Promise.reject(error);
  120. }
  121. );
  122. _$http.interceptors.request.use(
  123. // no auto 500 error UI
  124. function (config) {
  125. setAuth(config);
  126. networkInformationHint();
  127. // Do something before request is sent
  128. if (!noauthUrls.some((url) => config.url.includes(url))) {
  129. if (!wk_token) {
  130. const user = JSON.parse(window.sessionStorage.getItem("user"));
  131. if (!user) {
  132. if (
  133. window.___lastInvalidDate === undefined ||
  134. window.___lastInvalidDate < Date.now() - 300
  135. ) {
  136. Vue.prototype.$alert("登录失效,请重新登录!", "提示", {
  137. confirmButtonText: "确定",
  138. callback: () => {
  139. returnLogin();
  140. },
  141. });
  142. window.___lastInvalidDate = Date.now();
  143. }
  144. return;
  145. }
  146. // wk_token = user.token;
  147. // wk_key = user.key;
  148. wk_orgId = user.rootOrgId;
  149. }
  150. if (wk_token && config.headers.common["token"] == null) {
  151. // config.headers.common["token"] = wk_token;
  152. // config.headers.common["key"] = wk_key;
  153. }
  154. } else {
  155. wk_token = null;
  156. }
  157. return config;
  158. },
  159. function (error) {
  160. // Do something with request error
  161. Vue.prototype.$notify({
  162. showClose: true,
  163. message: error,
  164. type: "error",
  165. });
  166. return Promise.reject(error);
  167. }
  168. );
  169. const recordRequest = () => {
  170. let matchedRoutePath;
  171. try {
  172. const matched = router.resolve(location).route.matched;
  173. const exactMatched = matched[matched.length - 1];
  174. matchedRoutePath = exactMatched.path;
  175. } catch (error) {
  176. console.log(error);
  177. window._hmt.push([
  178. "_trackEvent",
  179. `页面-${location.pathname}`,
  180. "网络请求-响应",
  181. "解析出错",
  182. ]);
  183. }
  184. window._hmt.push([
  185. "_trackEvent",
  186. `页面-${matchedRoutePath || location.pathname}`,
  187. "网络请求-响应",
  188. ]);
  189. };
  190. // Add a response interceptor
  191. _$httpWith500Msg.interceptors.response.use(
  192. (response) => {
  193. initSyncTime(new Date(response.headers.date).getTime());
  194. recordRequest(response);
  195. return response;
  196. },
  197. (error) => {
  198. console.dir(error);
  199. if (!error.response) {
  200. // "Network Error" 网络不通,直接返回
  201. Vue.prototype.$notify({
  202. showClose: true,
  203. message: "网络连接异常,请检查网络设置。",
  204. type: "error",
  205. });
  206. return Promise.reject(error);
  207. } else {
  208. initSyncTime(new Date(error.response.headers.date).getTime());
  209. }
  210. if (error.config.noToast) {
  211. return Promise.reject(error);
  212. }
  213. // 这里是返回状态码不为200时候的错误处理
  214. let status = error.response.status;
  215. // 登录失效 跳转登录页面
  216. if (status == 403 || status == 401) {
  217. if (
  218. window.___lastInvalidDate === undefined ||
  219. window.___lastInvalidDate < Date.now() - 300
  220. ) {
  221. Vue.prototype.$alert("登录失效,请重新登录!", "提示", {
  222. confirmButtonText: "确定",
  223. callback: () => {
  224. returnLogin();
  225. },
  226. });
  227. window.___lastInvalidDate = Date.now();
  228. }
  229. return Promise.reject(error);
  230. } else if (status == 405) {
  231. Vue.prototype.$alert("没有权限!", "提示", {
  232. confirmButtonText: "确定",
  233. callback: () => {
  234. returnLogin();
  235. },
  236. });
  237. return Promise.reject(error);
  238. } else if (status == 502) {
  239. Vue.prototype.$alert("服务器异常!", "提示", {
  240. confirmButtonText: "确定",
  241. });
  242. return Promise.reject(error);
  243. }
  244. // 下载数据类型为blob时,错误提示直接抛给通用下载逻辑
  245. if (error.response.config.responseType === "blob") {
  246. return Promise.reject(error);
  247. }
  248. if (status != 200) {
  249. const data = error.response.data;
  250. if (ERROR_MSG_CONFIG.map((v) => v.code).includes(data.code)) {
  251. const MSG = ERROR_MSG_CONFIG.find((v) => v.code === data.code);
  252. if (MSG.display) {
  253. Vue.prototype.$notify({
  254. showClose: true,
  255. message: MSG.message,
  256. type: "error",
  257. });
  258. }
  259. } else {
  260. if (data && data.desc) {
  261. Vue.prototype.$notify({
  262. showClose: true,
  263. message: data.desc,
  264. type: "error",
  265. });
  266. } else {
  267. Vue.prototype.$notify({
  268. showClose: true,
  269. message: "未定义异常: " + JSON.stringify(data, 2),
  270. type: "error",
  271. });
  272. }
  273. }
  274. return Promise.reject(error);
  275. }
  276. }
  277. );
  278. // Add a response interceptor
  279. _$http.interceptors.response.use(
  280. // no auto 500 error UI
  281. (response) => {
  282. initSyncTime(new Date(response.headers.date).getTime());
  283. recordRequest(response);
  284. return response;
  285. },
  286. (error) => {
  287. if (!error.response) {
  288. Vue.prototype.$notify({
  289. showClose: true,
  290. message: "网络连接异常,请检查网络设置。",
  291. type: "error",
  292. });
  293. return Promise.reject(error);
  294. } else {
  295. initSyncTime(new Date(error.response.headers.date).getTime());
  296. }
  297. if (error.config.noToast) {
  298. return Promise.reject(error);
  299. }
  300. // 这里是返回状态码不为200时候的错误处理
  301. let status = error.response.status;
  302. // 登录失效 跳转登录页面
  303. if (status == 403 || status == 401) {
  304. if (
  305. window.___lastInvalidDate === undefined ||
  306. window.___lastInvalidDate < Date.now() - 300
  307. ) {
  308. Vue.prototype.$alert("登录失效,请重新登录!", "提示", {
  309. confirmButtonText: "确定",
  310. callback: () => {
  311. returnLogin();
  312. },
  313. });
  314. window.___lastInvalidDate = Date.now();
  315. }
  316. return Promise.reject(error);
  317. } else if (status == 405) {
  318. Vue.prototype.$alert("没有权限!", "提示", {
  319. confirmButtonText: "确定",
  320. callback: () => {
  321. returnLogin();
  322. },
  323. });
  324. return Promise.reject(error);
  325. } else if (status == 502) {
  326. Vue.prototype.$alert("服务器异常!", "提示", {
  327. confirmButtonText: "确定",
  328. });
  329. return Promise.reject(error);
  330. } else if (status == 503) {
  331. Vue.prototype.$alert("服务器繁忙,请稍后重试!", "提示", {
  332. confirmButtonText: "确定",
  333. });
  334. return Promise.reject(error);
  335. }
  336. if (status != 200) {
  337. return Promise.reject(error);
  338. }
  339. }
  340. );
  341. _$httpWithoutBar.interceptors.request.use(
  342. // no auto 500 error UI
  343. function (config) {
  344. setAuth(config);
  345. networkInformationHint();
  346. // Do something before request is sent
  347. if (!noauthUrls.some((url) => config.url.includes(url))) {
  348. if (!wk_token) {
  349. const user = JSON.parse(window.sessionStorage.getItem("user"));
  350. if (!user) {
  351. if (
  352. window.___lastInvalidDate === undefined ||
  353. window.___lastInvalidDate < Date.now() - 300
  354. ) {
  355. Vue.prototype.$alert("登录失效,请重新登录!", "提示", {
  356. confirmButtonText: "确定",
  357. callback: () => {
  358. returnLogin();
  359. },
  360. });
  361. window.___lastInvalidDate = Date.now();
  362. }
  363. return;
  364. }
  365. // wk_token = user.token;
  366. // wk_key = user.key;
  367. wk_orgId = user.rootOrgId;
  368. }
  369. if (wk_token && config.headers.common["token"] == null) {
  370. // config.headers.common["token"] = wk_token;
  371. // config.headers.common["key"] = wk_key;
  372. }
  373. } else {
  374. wk_token = null;
  375. }
  376. return config;
  377. },
  378. function (error) {
  379. // Do something with request error
  380. Vue.prototype.$notify({
  381. showClose: true,
  382. message: error,
  383. type: "error",
  384. });
  385. return Promise.reject(error);
  386. }
  387. );
  388. _$httpWithoutBar.interceptors.response.use(
  389. // no auto 500 error UI
  390. (response) => {
  391. initSyncTime(new Date(response.headers.date).getTime());
  392. recordRequest(response);
  393. return response;
  394. },
  395. (error) => {
  396. if (!error.response) {
  397. Vue.prototype.$notify({
  398. showClose: true,
  399. message: "网络连接异常,请检查网络设置。",
  400. type: "error",
  401. });
  402. return Promise.reject(error);
  403. } else {
  404. initSyncTime(new Date(error.response.headers.date).getTime());
  405. }
  406. if (error.config.noToast) {
  407. return Promise.reject(error);
  408. }
  409. // 这里是返回状态码不为200时候的错误处理
  410. let status = error.response.status;
  411. // 登录失效 跳转登录页面
  412. if (status == 403 || status == 401) {
  413. if (
  414. window.___lastInvalidDate === undefined ||
  415. window.___lastInvalidDate < Date.now() - 300
  416. ) {
  417. window.___lastInvalidDate = Date.now();
  418. }
  419. return Promise.reject(error);
  420. } else if (status == 405) {
  421. Vue.prototype.$alert("没有权限!", "提示", {
  422. confirmButtonText: "确定",
  423. callback: () => {
  424. returnLogin();
  425. },
  426. });
  427. return Promise.reject(error);
  428. } else if (status == 502) {
  429. Vue.prototype.$alert("服务器异常!", "提示", {
  430. confirmButtonText: "确定",
  431. });
  432. return Promise.reject(error);
  433. }
  434. if (status != 200) {
  435. return Promise.reject(error);
  436. }
  437. }
  438. );
  439. Plugin.install = function (Vue) {
  440. Vue.$http = _$http; // no auto 500 error UI
  441. Object.defineProperties(Vue.prototype, {
  442. $http: {
  443. get() {
  444. return _$http; // no auto 500 error UI
  445. },
  446. },
  447. });
  448. Vue.$httpWithMsg = _$httpWith500Msg;
  449. Object.defineProperties(Vue.prototype, {
  450. $httpWithMsg: {
  451. get() {
  452. return _$httpWith500Msg;
  453. },
  454. },
  455. });
  456. // for below request
  457. // config.url.includes("/api/ecs_ques/paper/") === false &&
  458. // config.url.includes("/api/ecs_ques/questionAudio") === false
  459. const _a = axios.create(config);
  460. Vue.$httpWithoutAuth = _a;
  461. Object.defineProperties(Vue.prototype, {
  462. $httpWithoutAuth: {
  463. get() {
  464. return _a;
  465. },
  466. },
  467. });
  468. Vue.$httpWithoutBar = _$httpWithoutBar;
  469. Object.defineProperties(Vue.prototype, {
  470. $httpWithoutBar: {
  471. get() {
  472. return _$httpWithoutBar;
  473. },
  474. },
  475. });
  476. };
  477. Vue.use(Plugin);
  478. loadProgressBar({}, Vue.$http);
  479. loadProgressBar({}, Vue.$httpWithMsg);
  480. loadProgressBar({}, Vue.$httpWithoutAuth);
  481. // const update = (type, e) => {
  482. // // debugger;
  483. // console.log(type);
  484. // console.log(
  485. // "e.target.url: ",
  486. // e.target.responseURL,
  487. // " timeStamp: ",
  488. // e.timeStamp.toFixed(2),
  489. // " loaded:",
  490. // e.loaded,
  491. // " total: ",
  492. // e.total
  493. // );
  494. // console.log(e);
  495. // };
  496. // Vue.$httpWithMsg.defaults.onDownloadProgress = e => {
  497. // update("下载", e);
  498. // };
  499. // Vue.$httpWithMsg.defaults.onUploadProgress = e => {
  500. // update("上传", e);
  501. // };
  502. import "axios-progress-bar/dist/nprogress.css";
  503. export default Plugin;
  504. export const $httpWithMsg = _$httpWith500Msg;