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中 :
// Creates a new URL by combining the specified URLs const combineURL = (baseUrl, path) => { return `${baseUrl.replace(/\/+$/, '')}/${path.replace(/^\/+/, '')}`; }; export default combineURL;
isAbsoluteURL.js中 :
// A URL is considered absolute if it begins with "<scheme>://" or "//" (protocol-relative URL). // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed // by any combination of letters, digits, plus, period, or hyphen. // https://www.ietf.org/rfc/rfc3986.txt const isAbsoluteURL = (url) => /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url); export default isAbsoluteURL;
serialize.js中 :
import { isPresent } from 'lib/lang'; const encode = (value) => { return encodeURIComponent(value) .replace(/%40/gi, '@') .replace(/:/gi, ':') .replace(/%24/g, '$') .replace(/%2C/gi, ',') .replace(/%20/g, '+') .replace(/%5B/gi, '[') .replace(/%5D/gi, ']'); }; // Encode a set of form elements as a string for submission. const serialize = (params) => { const ret = []; Object.keys(params).forEach(key => { const value = params[key]; if (isPresent(value)) { ret.push(`${encode(key)}=${encode(value)}`); } }); return ret.join('&'); }; export default serialize;
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同级。
import fetch from 'dva/fetch'; function parseJSON(response) { return response.json(); } function checkStatus(response) { if (response.status >= 200 && response.status < 300) { return response; } const error = new Error(response.statusText); error.response = response; throw error; } function parseErrorMessage({ data }) { const { status, message } = data; if (status === 'error') { throw new Error(message); } return { data }; } export default function request(url, options) { return fetch(url, options) .then(checkStatus) .then(parseJSON) .then(parseErrorMessage) .then((data) => ({ data })) .catch((err) => ({ err })); }
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 }); }
原文地址:https://www.jianshu.com/p/007196101cf1