天天看點

react中請求接口的封裝

1. 建立一個dva項目。使用antd 或者antd-mobile元件庫。
$ npm install dva-cli -g
$ dva -v
$ dva new dva-quickstart
$ npm start
           
$  npm  install antd babel-plugin-import --save
或者是 
$  npm  install antd-mobile babel-plugin-import --save
           

導入方式css

{
  "entry": "src/index.js",
  "env": {
    "development": {
      "extraBabelPlugins": [
        "dva-hmr",
        "transform-runtime",
        ["import", { "libraryName": "antd-mobile", "style": "css" }]
      ]
    },
    "production": {
      "extraBabelPlugins": [
        "transform-runtime",
        ["import", { "libraryName": "antd-mobile", "style": "css" }]
      ]
    }
  }
}
           
2. 在該項目的src中utils 建立名為request檔案夾。
$ cd  dva-quickstart
$ cd  src 
$ cd utils 
           

建立檔案夾名為request,然後在request檔案夾下面建立名為helpers的檔案夾以及index.js 和 README.md , request.js 如圖所示:

在helpers 下建三個js檔案 combineURL.js , isAbsoluteURL.js , serialize.js

image.png

combineURL.js中 :

isAbsoluteURL.js中 :

serialize.js中 :

3. 在utils下建立一個與request同級的lang.js

lang.js 如下:

export const isPresent = (obj) => {
  return typeof obj !== 'undefined' && obj !== null;
};

export const isBlank = (obj) => {
  return typeof obj === 'undefined' || obj === null;
};

export const isBoolean = (obj) => {
  return typeof obj === 'boolean';
};

export const isNumber = (obj) => {
  return typeof obj === 'number';
};

export const isString = (obj) => {
  return typeof obj === 'string';
};

export const isArray = (obj) => {
  return Array.isArray(obj) || Object.prototype.toString.call(obj) === '[object Array]';
};

export const isDate = (obj) => {
  return obj instanceof Date && !isNaN(obj.valueOf());
};

export const isFunction = (obj) => {
  return typeof obj === 'function';
};

export const isJsObject = (obj) => {
  return obj !== null && (isFunction(obj) || typeof obj === 'object');
};

export const isPromise = (obj) => {
  return isPresent(obj) && isFunction(obj.then);
};

export const isEmpty = (obj) => {
  if (isBlank(obj)) {
    return true;
  }

  if (obj.length === 0) {
    return true;
  }

  for (const key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      return false;
    }
  }

  return true;
};

export const normalizeBlank = (obj) => {
  return isBlank(obj) ? null : obj;
};

export const normalizeBool = (obj) => {
  return isBlank(obj) ? false : obj;
};

export const stringify = (token) => {
  if (isString(token)) {
    return token;
  }

  if (isBlank(token)) {
    return String(token);
  }

  const ret = token.toString();
  const newLineIndex = ret.indexOf('\n');
  return (newLineIndex === -1) ? ret : ret.substring(0, newLineIndex);
};

export class PromiseWrapper {
  // Excutes promises one by one, e.g.
  // const promise = () => new Promise(...)
  // const promise2 = () => new Promise(...)
  // sequentialize([ promise, promise2 ])
  static sequentialize = promiseFactories => {
    let chain = Promise.resolve();
    promiseFactories.forEach(factory => {
      chain = chain.then(factory);
    });
    return chain;
  }

  // Promise finally util similar to Q.finally
  // e.g. finally(promise.then(...))
  /* eslint-disable consistent-return */
  static finally = (promise, cb) => promise.then(res => {
    const otherPromise = cb();
    if (typeof otherPromise.then === 'function') {
      return otherPromise.then(() => res);
    }
  }, reason => {
    const otherPromise = cb();
    if (typeof otherPromise.then === 'function') {
      return otherPromise.then(() => {
        throw reason;
      });
    }
    throw reason;
  })
}
/* eslint-enable consistent-return */

export class StringWrapper {
  static equals = (s1, s2) => s1 === s2;

  static contains = (s, substr) => s.indexOf(substr) !== -1;

  static compare = (a, b) => {
    if (a < b) {
      return -1;
    } else if (a > b) {
      return 1;
    }

    return 0;
  }
}

/* eslint-disable max-params */
export class DateWrapper {
  static create(
    year,
    month = 1,
    day = 1,
    hour = 0,
    minutes = 0,
    seconds = 0,
    milliseconds = 0
  ) {
    return new Date(year, month - 1, day, hour, minutes, seconds, milliseconds);
  }

  static fromISOString(str) {
    return new Date(str);
  }

  static fromMillis(ms) {
    return new Date(ms);
  }

  static toMillis(date) {
    return date.getTime();
  }

  static now() {
    return Date.now() || new Date();
  }

  static toJson(date) {
    return date.toJSON();
  }
}
/* eslint-enable max-params */

           

這個是dva自動生成的request.js 把這個檔案換下名字requests.js,它與lang.js同級。

4. 打開在request檔案下request.js,進行編輯:

request.js

import fetch from 'dva/fetch';
import { isEmpty } from '../lang';
import serialize from './helpers/serialize';
import combineURL from './helpers/combineURL';
import isAbsoluteURL from './helpers/isAbsoluteURL';
import { apiBaseUrl } from '../../config';
import { Toast } from 'antd-mobile';

const wait = ms => new Promise(resolve => setTimeout(resolve, ms));

const timeout = (p, ms = 30 * 1000) =>
  Promise.race([
    p,
    wait(ms).then(() => {
      const error = new Error(`Connection timed out after ${ms} ms`);
      error.statusCode = 408;
      throw error;
    }),
  ]);

// Request factory
function request(url, options, method) {
  const { endpoint, ...rest } = interceptRequest(url, options, method);
  const xhr = fetch(endpoint, rest).then(interceptResponse);
  return timeout(xhr, request.defaults.timeout).catch((error) => {
    // return Promise.reject(error);
    
  });
}

request.defaults = {
  baseURL: apiBaseUrl,
  timeout: 10 * 5000,
  headers: {
    Accept: 'application/json',
  },
};

// Headers factory
const createHeaders = () => {
  const headers = {
    ...request.defaults.headers,
  };

  // const auth = JSON.parse(localStorage.getItem('auth'+sessionStorage.getItem("hid")));
  
  // const token = sessionStorage.getItem('token'); // <Michael> 登入location擷取到的token存放l
  
  
  // if (auth) {
  //   // Toast.info(`請稍等: ${token}`, 2);
  //   // Toast.loading('');
    
  //   headers.Authorization = auth.Token;
  // } else if (token) {
  //   // <Michael>;
  //   // Toast.info(`請稍等: ${token}`, 2);
  //   // Toast.loading('');
  //   headers.Authorization = token;
    
  // }
  headers.Authorization = "app";
  return headers;
};

// Request interceptor
function interceptRequest(url, options, method) {
  let endpoint;
  if (isAbsoluteURL(url)) {
    endpoint = url;
  } else {
    endpoint = combineURL(request.defaults.baseURL, url);
  }

  let data = {
    method,
    endpoint,
    headers: createHeaders(),
  };

  if (!isEmpty(options)) {
    data = {
      ...data,
      ...options,
    };

    if (options.json) {
      data.headers['Content-Type'] = 'application/json;charset=utf-8';
      data.body = JSON.stringify(options.json);
    }

    if (options.form) {
      data.headers['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
      data.body = serialize(options.form);
    }

    if (options.body) {
      data.body = options.body;

      const auth = JSON.parse(localStorage.getItem('auth'+sessionStorage.getItem("hid")));

      if (auth) {
        if (auth && options.body instanceof FormData && !options.body.hasPatientid) {
          // options.body.append('patientid', auth.Patientid);
        }
      }
    }

    if (options.params) {
      endpoint += `?${serialize(options.params)}`;
      data.endpoint = endpoint;
    }
  }

  return data;
}

// Response interceptor
/* eslint-disable consistent-return */
function interceptResponse(response) {
  return new Promise((resolve, reject) => {
    const emptyCodes = [204, 205];

    // Don't attempt to parse 204 & 205
    if (emptyCodes.indexOf(response.status) !== -1) {
      return resolve(response.ok);
    }

    if (response.ok) {
      const contentType = response.headers.get('Content-Type');
      if (contentType.includes('application/json')) {
        resolve(response.json());
      }

      resolve(response);
    }

    if (response.status === 401) {
      // return Toast.fail('認證資訊已過期,請重新登入', 2, () => {
      // return Toast.fail('請重新登入', 2, () => { 
        localStorage.removeItem('auth'+sessionStorage.getItem("hid"));
        // sessionStorage.removeItem('token');
        location.reload();
        // TODO:跳轉登入路由
      // });
    }

    const error = new Error(response.statusText);
    try {
      response.clone().json().then((result) => {
        error.body = result;
        error.response = response;
        reject(error);
      });
    } catch (e) {
      error.response = response;
      reject(error);
    }
  });
}
/* eslint-enable consistent-return */

// suger
request.get = (url, options) => request(url, options, 'GET');

request.head = (url, options) => request(url, options, 'HEAD');

request.options = (url, options) => request(url, options, 'OPTIONS');

request.post = (url, options) => request(url, options, 'POST');

request.put = (url, options) => request(url, options, 'PUT');

request.delete = (url, options) => request(url, options, 'DELETE');

request.del = request.delete;

export default request;

           
5. 這樣你就可以在今後的項目正常使用按照以下步驟
module.exports = {
    apiBaseUrl: "http://172.118.100.50/api/",
};
           

之後再services檔案下就可以這樣去下啦:

import request from '../utils/request/request';

export function queryScaleMenu(start, limit) {
   const body = new FormData();
    body.append('start',start);
    body.append('limit', limit);
    return request.post('news/menu/query', { body });
} 
           

繼續閱讀