request.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import fetch from 'dva/fetch';
  2. import { stringify } from 'qs';
  3. import { message, notification } from 'antd';
  4. // HTTP响应状态码
  5. const httpCodeMessage = {
  6. 200: '服务器成功返回请求的数据',
  7. 201: '新建或修改数据成功。',
  8. 202: '一个请求已经进入后台排队(异步任务)',
  9. 204: '删除数据成功。',
  10. 400: '发出的请求有错误,服务器没有进行新建或修改数据,的操作。',
  11. 401: '用户没有权限(令牌、用户名、密码错误)。',
  12. 403: '用户得到授权,但是访问是被禁止的。',
  13. 404: '发出的请求针对的是不存在的记录,服务器没有进行操作',
  14. 406: '请求的格式不可得。',
  15. 410: '请求的资源被永久删除,且不会再得到的。',
  16. 422: '当创建一个对象时,发生一个验证错误。',
  17. 500: '服务器发生错误,请检查服务器',
  18. 502: '网关错误',
  19. 503: '服务不可用,服务器暂时过载或维护',
  20. 504: '网关超时',
  21. };
  22. // 自定义响应状态码
  23. const customCodeMessage = {
  24. 10004: 'Token认证失败',
  25. 800 : '数据不存在',
  26. };
  27. /**
  28. * 检查HTTP响应状态码,>=200 && < 300正常
  29. */
  30. function httpErrorHandler(response) {
  31. if (response.status >= 200 && response.status < 300) {
  32. return response;
  33. }
  34. const errortext = httpCodeMessage[response.status] || response.statusText;
  35. notification.error({
  36. message: `HTTP错误 ${response.status}: ${response.url}`,
  37. description: errortext,
  38. });
  39. const error = new Error(errortext);
  40. error.response = response;
  41. throw error;
  42. return { error };
  43. }
  44. /**
  45. * @desc 拦截接口返回的数据状态,提示错误内容
  46. * @return {[object]} { res: data }
  47. */
  48. function apiErrorHandler(data) {
  49. if (!data.success) {
  50. const errortext = customCodeMessage[data.code] || data.message;
  51. // Token认证失败,错误继续外抛,让全局app对象捕获,跳转到登录界面
  52. if (data.code === 10004) {
  53. const error = new Error(errortext);
  54. error.response = data;
  55. throw error;
  56. } else {
  57. message.error(`请求错误 错误代码:${data.code} 错误信息:${errortext}`);
  58. }
  59. }
  60. return data;
  61. }
  62. /**
  63. * @desc 处理未预知到的网络错误
  64. * @return {[object]} {error}
  65. */
  66. function unpredictableErrorHandler(error) {
  67. if (!error.response) {
  68. console.error(error);
  69. message.error('出现未知的网络问题,请检查日志或联系管理员');
  70. }
  71. if (error.response && error.response.code === 10004) {
  72. throw error;
  73. }
  74. return { error };
  75. };
  76. /**
  77. * @desc 处理请求超时错误
  78. * @return {[object]} {error}
  79. */
  80. function timeoutErrorHandler(error) {
  81. // 这里只会捕获两种错误,一是超时错误,一是认证失效错误,认证失效错误继续外抛
  82. if (!error.response) {
  83. message.error('请求超时,请检查网络状态是否可用!');
  84. }
  85. else if (error && error.response.code === 10004) {
  86. throw error;
  87. }
  88. return { error };
  89. }
  90. /**
  91. * response为promise对象,转换为json
  92. */
  93. function promise2Json(response) {
  94. return response.json();
  95. }
  96. /**
  97. * fetch api不支持超时设置,进行浅度加工,变相实现超时设定
  98. */
  99. function _fetch(requestPromise, timeout=8000) {
  100. let timeoutAction = null;
  101. const timerPromise = new Promise((resolve, reject) => {
  102. timeoutAction = () => { reject() }
  103. });
  104. setTimeout(() => { timeoutAction() }, timeout);
  105. return Promise.race([requestPromise,timerPromise]);
  106. };
  107. /**
  108. * Requests a URL, returning an object or none.
  109. *
  110. * @param {string} url The URL we want to request
  111. * @param {object} [options] The options we want to pass to "fetch"
  112. * @return {object} An object containing either "data" or "err"
  113. */
  114. export default function request(url, options) {
  115. const defaultOptions = {
  116. credentials: 'include', // with cookies(Post CORS requests)
  117. };
  118. const newOptions = { ...defaultOptions, ...options };
  119. newOptions.method = (newOptions.method || 'GET').toUpperCase();
  120. if (newOptions.method === 'POST' || newOptions.method === 'PUT' ||
  121. newOptions.method === 'PATCH' || newOptions.method === 'DELETE') {
  122. newOptions.headers = {
  123. 'Accept': 'application/json',
  124. 'Content-Type': 'application/json; charset=utf-8',
  125. ...newOptions.headers,
  126. };
  127. }
  128. else {
  129. newOptions.headers = {
  130. 'Accept': 'application/json',
  131. ...newOptions.headers,
  132. }
  133. }
  134. const originRequest = fetch(url, newOptions)
  135. .then(httpErrorHandler)
  136. .then(promise2Json)
  137. .then(apiErrorHandler)
  138. .catch(unpredictableErrorHandler)
  139. return _fetch(originRequest).catch(timeoutErrorHandler);
  140. }