1. 新建一个dva项目。使用antd 或者antd-mobile组件库。

  1. $ npm install dva-cli -g
  2. $ dva -v
  3. $ dva new dva-quickstart
  4. $ npm start
  1. $ npm install antd babel-plugin-import --save
  2. 或者是
  3. $ npm install antd-mobile babel-plugin-import --save

导入方式css

  1. {
  2. "entry": "src/index.js",
  3. "env": {
  4. "development": {
  5. "extraBabelPlugins": [
  6. "dva-hmr",
  7. "transform-runtime",
  8. ["import", { "libraryName": "antd-mobile", "style": "css" }]
  9. ]
  10. },
  11. "production": {
  12. "extraBabelPlugins": [
  13. "transform-runtime",
  14. ["import", { "libraryName": "antd-mobile", "style": "css" }]
  15. ]
  16. }
  17. }
  18. }

2. 在该项目的src中utils 创建名为request文件夹。

  1. $ cd dva-quickstart
  2. $ cd src
  3. $ cd utils

新建文件夹名为request,然后在request文件夹下面创建名为helpers的文件夹以及index.js 和 README.md , request.js 如图所示:

在helpers 下建三个js文件 combineURL.js , isAbsoluteURL.js , serialize.js
react中请求接口的封装 - 图1

combineURL.js中 :

  1. // Creates a new URL by combining the specified URLs
  2. const combineURL = (baseUrl, path) => {
  3. return `${baseUrl.replace(/\/+$/, '')}/${path.replace(/^\/+/, '')}`;
  4. };
  5. export default combineURL;

isAbsoluteURL.js中 :

  1. // A URL is considered absolute if it begins with "<scheme>://" or "//" (protocol-relative URL).
  2. // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed
  3. // by any combination of letters, digits, plus, period, or hyphen.
  4. // https://www.ietf.org/rfc/rfc3986.txt
  5. const isAbsoluteURL = (url) => /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url);
  6. export default isAbsoluteURL;

serialize.js中 :

  1. import { isPresent } from 'lib/lang';
  2. const encode = (value) => {
  3. return encodeURIComponent(value)
  4. .replace(/%40/gi, '@')
  5. .replace(/%3A/gi, ':')
  6. .replace(/%24/g, '$')
  7. .replace(/%2C/gi, ',')
  8. .replace(/%20/g, '+')
  9. .replace(/%5B/gi, '[')
  10. .replace(/%5D/gi, ']');
  11. };
  12. // Encode a set of form elements as a string for submission.
  13. const serialize = (params) => {
  14. const ret = [];
  15. Object.keys(params).forEach(key => {
  16. const value = params[key];
  17. if (isPresent(value)) {
  18. ret.push(`${encode(key)}=${encode(value)}`);
  19. }
  20. });
  21. return ret.join('&');
  22. };
  23. export default serialize;

3. 在utils下创建一个与request同级的lang.js

lang.js 如下:

  1. export const isPresent = (obj) => {
  2. return typeof obj !== 'undefined' && obj !== null;
  3. };
  4. export const isBlank = (obj) => {
  5. return typeof obj === 'undefined' || obj === null;
  6. };
  7. export const isBoolean = (obj) => {
  8. return typeof obj === 'boolean';
  9. };
  10. export const isNumber = (obj) => {
  11. return typeof obj === 'number';
  12. };
  13. export const isString = (obj) => {
  14. return typeof obj === 'string';
  15. };
  16. export const isArray = (obj) => {
  17. return Array.isArray(obj) || Object.prototype.toString.call(obj) === '[object Array]';
  18. };
  19. export const isDate = (obj) => {
  20. return obj instanceof Date && !isNaN(obj.valueOf());
  21. };
  22. export const isFunction = (obj) => {
  23. return typeof obj === 'function';
  24. };
  25. export const isJsObject = (obj) => {
  26. return obj !== null && (isFunction(obj) || typeof obj === 'object');
  27. };
  28. export const isPromise = (obj) => {
  29. return isPresent(obj) && isFunction(obj.then);
  30. };
  31. export const isEmpty = (obj) => {
  32. if (isBlank(obj)) {
  33. return true;
  34. }
  35. if (obj.length === 0) {
  36. return true;
  37. }
  38. for (const key in obj) {
  39. if (Object.prototype.hasOwnProperty.call(obj, key)) {
  40. return false;
  41. }
  42. }
  43. return true;
  44. };
  45. export const normalizeBlank = (obj) => {
  46. return isBlank(obj) ? null : obj;
  47. };
  48. export const normalizeBool = (obj) => {
  49. return isBlank(obj) ? false : obj;
  50. };
  51. export const stringify = (token) => {
  52. if (isString(token)) {
  53. return token;
  54. }
  55. if (isBlank(token)) {
  56. return String(token);
  57. }
  58. const ret = token.toString();
  59. const newLineIndex = ret.indexOf('\n');
  60. return (newLineIndex === -1) ? ret : ret.substring(0, newLineIndex);
  61. };
  62. export class PromiseWrapper {
  63. // Excutes promises one by one, e.g.
  64. // const promise = () => new Promise(...)
  65. // const promise2 = () => new Promise(...)
  66. // sequentialize([ promise, promise2 ])
  67. static sequentialize = promiseFactories => {
  68. let chain = Promise.resolve();
  69. promiseFactories.forEach(factory => {
  70. chain = chain.then(factory);
  71. });
  72. return chain;
  73. }
  74. // Promise finally util similar to Q.finally
  75. // e.g. finally(promise.then(...))
  76. /* eslint-disable consistent-return */
  77. static finally = (promise, cb) => promise.then(res => {
  78. const otherPromise = cb();
  79. if (typeof otherPromise.then === 'function') {
  80. return otherPromise.then(() => res);
  81. }
  82. }, reason => {
  83. const otherPromise = cb();
  84. if (typeof otherPromise.then === 'function') {
  85. return otherPromise.then(() => {
  86. throw reason;
  87. });
  88. }
  89. throw reason;
  90. })
  91. }
  92. /* eslint-enable consistent-return */
  93. export class StringWrapper {
  94. static equals = (s1, s2) => s1 === s2;
  95. static contains = (s, substr) => s.indexOf(substr) !== -1;
  96. static compare = (a, b) => {
  97. if (a < b) {
  98. return -1;
  99. } else if (a > b) {
  100. return 1;
  101. }
  102. return 0;
  103. }
  104. }
  105. /* eslint-disable max-params */
  106. export class DateWrapper {
  107. static create(
  108. year,
  109. month = 1,
  110. day = 1,
  111. hour = 0,
  112. minutes = 0,
  113. seconds = 0,
  114. milliseconds = 0
  115. ) {
  116. return new Date(year, month - 1, day, hour, minutes, seconds, milliseconds);
  117. }
  118. static fromISOString(str) {
  119. return new Date(str);
  120. }
  121. static fromMillis(ms) {
  122. return new Date(ms);
  123. }
  124. static toMillis(date) {
  125. return date.getTime();
  126. }
  127. static now() {
  128. return Date.now() || new Date();
  129. }
  130. static toJson(date) {
  131. return date.toJSON();
  132. }
  133. }
  134. /* eslint-enable max-params */

4.这个是dva自动生成的request.js 把这个文件换下名字requests.js,它与lang.js同级。

  1. import fetch from 'dva/fetch';
  2. function parseJSON(response) {
  3. return response.json();
  4. }
  5. function checkStatus(response) {
  6. if (response.status >= 200 && response.status < 300) {
  7. return response;
  8. }
  9. const error = new Error(response.statusText);
  10. error.response = response;
  11. throw error;
  12. }
  13. function parseErrorMessage({ data }) {
  14. const { status, message } = data;
  15. if (status === 'error') {
  16. throw new Error(message);
  17. }
  18. return { data };
  19. }
  20. export default function request(url, options) {
  21. return fetch(url, options)
  22. .then(checkStatus)
  23. .then(parseJSON)
  24. .then(parseErrorMessage)
  25. .then((data) => ({ data }))
  26. .catch((err) => ({ err }));
  27. }

request.js

  1. import fetch from 'dva/fetch';
  2. import { isEmpty } from '../lang';
  3. import serialize from './helpers/serialize';
  4. import combineURL from './helpers/combineURL';
  5. import isAbsoluteURL from './helpers/isAbsoluteURL';
  6. import { apiBaseUrl } from '../../config';
  7. import { Toast } from 'antd-mobile';
  8. const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
  9. const timeout = (p, ms = 30 * 1000) =>
  10. Promise.race([
  11. p,
  12. wait(ms).then(() => {
  13. const error = new Error(`Connection timed out after ${ms} ms`);
  14. error.statusCode = 408;
  15. throw error;
  16. }),
  17. ]);
  18. // Request factory
  19. function request(url, options, method) {
  20. const { endpoint, ...rest } = interceptRequest(url, options, method);
  21. const xhr = fetch(endpoint, rest).then(interceptResponse);
  22. return timeout(xhr, request.defaults.timeout).catch((error) => {
  23. // return Promise.reject(error);
  24. });
  25. }
  26. request.defaults = {
  27. baseURL: apiBaseUrl,
  28. timeout: 10 * 5000,
  29. headers: {
  30. Accept: 'application/json',
  31. },
  32. };
  33. // Headers factory
  34. const createHeaders = () => {
  35. const headers = {
  36. ...request.defaults.headers,
  37. };
  38. // const auth = JSON.parse(localStorage.getItem('auth'+sessionStorage.getItem("hid")));
  39. // const token = sessionStorage.getItem('token'); // <Michael> 登录location获取到的token存放l
  40. // if (auth) {
  41. // // Toast.info(`请稍等: ${token}`, 2);
  42. // // Toast.loading('');
  43. // headers.Authorization = auth.Token;
  44. // } else if (token) {
  45. // // <Michael>;
  46. // // Toast.info(`请稍等: ${token}`, 2);
  47. // // Toast.loading('');
  48. // headers.Authorization = token;
  49. // }
  50. headers.Authorization = "app";
  51. return headers;
  52. };
  53. // Request interceptor
  54. function interceptRequest(url, options, method) {
  55. let endpoint;
  56. if (isAbsoluteURL(url)) {
  57. endpoint = url;
  58. } else {
  59. endpoint = combineURL(request.defaults.baseURL, url);
  60. }
  61. let data = {
  62. method,
  63. endpoint,
  64. headers: createHeaders(),
  65. };
  66. if (!isEmpty(options)) {
  67. data = {
  68. ...data,
  69. ...options,
  70. };
  71. if (options.json) {
  72. data.headers['Content-Type'] = 'application/json;charset=utf-8';
  73. data.body = JSON.stringify(options.json);
  74. }
  75. if (options.form) {
  76. data.headers['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
  77. data.body = serialize(options.form);
  78. }
  79. if (options.body) {
  80. data.body = options.body;
  81. const auth = JSON.parse(localStorage.getItem('auth'+sessionStorage.getItem("hid")));
  82. if (auth) {
  83. if (auth && options.body instanceof FormData && !options.body.hasPatientid) {
  84. // options.body.append('patientid', auth.Patientid);
  85. }
  86. }
  87. }
  88. if (options.params) {
  89. endpoint += `?${serialize(options.params)}`;
  90. data.endpoint = endpoint;
  91. }
  92. }
  93. return data;
  94. }
  95. // Response interceptor
  96. /* eslint-disable consistent-return */
  97. function interceptResponse(response) {
  98. return new Promise((resolve, reject) => {
  99. const emptyCodes = [204, 205];
  100. // Don't attempt to parse 204 & 205
  101. if (emptyCodes.indexOf(response.status) !== -1) {
  102. return resolve(response.ok);
  103. }
  104. if (response.ok) {
  105. const contentType = response.headers.get('Content-Type');
  106. if (contentType.includes('application/json')) {
  107. resolve(response.json());
  108. }
  109. resolve(response);
  110. }
  111. if (response.status === 401) {
  112. // return Toast.fail('认证信息已过期,请重新登录', 2, () => {
  113. // return Toast.fail('请重新登录', 2, () => {
  114. localStorage.removeItem('auth'+sessionStorage.getItem("hid"));
  115. // sessionStorage.removeItem('token');
  116. location.reload();
  117. // TODO:跳转登录路由
  118. // });
  119. }
  120. const error = new Error(response.statusText);
  121. try {
  122. response.clone().json().then((result) => {
  123. error.body = result;
  124. error.response = response;
  125. reject(error);
  126. });
  127. } catch (e) {
  128. error.response = response;
  129. reject(error);
  130. }
  131. });
  132. }
  133. /* eslint-enable consistent-return */
  134. // suger
  135. request.get = (url, options) => request(url, options, 'GET');
  136. request.head = (url, options) => request(url, options, 'HEAD');
  137. request.options = (url, options) => request(url, options, 'OPTIONS');
  138. request.post = (url, options) => request(url, options, 'POST');
  139. request.put = (url, options) => request(url, options, 'PUT');
  140. request.delete = (url, options) => request(url, options, 'DELETE');
  141. request.del = request.delete;
  142. export default request;

5. 这样你就可以在今后的项目正常使用按照以下步骤

  1. module.exports = {
  2. apiBaseUrl: "http://172.118.100.50/api/",
  3. };

之后再services文件下就可以这样去下啦:

  1. import request from '../utils/request/request';
  2. export function queryScaleMenu(start, limit) {
  3. const body = new FormData();
  4. body.append('start',start);
  5. body.append('limit', limit);
  6. return request.post('news/menu/query', { body });
  7. }

简书地址: https://www.jianshu.com/p/007196101cf1
更新日期: 2018.01.24 18:49