react中请求接口的封装
2018-01-24 本文已影响632人
sidney_c
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中 :
image.png
isAbsoluteURL.js中 :
image.png
serialize.js中 :
image.png
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同级。
image.png
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 });
}