wodax-umi-request

A request tool based on fetch. Cloned from umi-request

Usage no npm install needed!

<script type="module">
  import wodaxUmiRequest from 'https://cdn.skypack.dev/wodax-umi-request';
</script>

README

English | 简体中文

wodax-umi-request

Base on umi-request

The network request library, based on fetch encapsulation, combines the features of fetch and axios to provide developers with a unified api call method, simplifying usage, and providing common functions such as caching, timeout, character encoding processing, and error handling.

NPM version NPM downloads


Supported features

  • url parameter is automatically serialized
  • post data submission method is simplified
  • response return processing simplification
  • api timeout support
  • api request cache support
  • support for processing gbk
  • request and response interceptor support like axios
  • unified error handling
  • middleware support
  • cancel request support like axios
  • make http request from node.js

wodax-umi-request vs fetch vs axios

Features wodax-umi-request fetch axios
implementation Browser native support Browser native support XMLHttpRequest
size 9k 4k (polyfill) 14k
query simplification
post simplification
timeout
cache
error Check
error Handling
interceptor
prefix
suffix
processing gbk
middleware
cancel request

For more discussion, refer to Traditional Ajax is dead, Fetch eternal life If you have good suggestions and needs, please mention issue

TODO Welcome pr

  • Test case coverage 85%+
  • write a document
  • CI integration
  • release configuration
  • typescript

Installation

npm install --save wodax-umi-request

Example

Performing a GET request

import request from 'wodax-umi-request';

request.get('/api/v1/xxx?id=1')
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

// use options.params
request.get('/api/v1/xxx', {
    params: {
      id: 1
    }
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

Performing a POST request

request.post('/api/v1/user', {
    data: {
      name: 'Mike'
    }
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

wodax-umi-request API

Requests can be made by passing relevant options to wodax-umi-request

wodax-umi-request(url[, options])

import request from 'wodax-umi-request';

request('/api/v1/xxx', {
    method: 'get',
    params: { id: 1 }
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

request('/api/v1/user', {
    method: 'post',
    data: {
      name: 'Mike'
    }
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

Request method aliases

For convenience wodax-umi-request have been provided for all supported methods.

request.get(url[, options])

request.post(url[, options])

request.delete(url[, options])

request.put(url[, options])

request.patch(url[, options])

request.head(url[, options])

request.options(url[, options])

Creating an instance

You can use extend({[options]}) to create a new instance of wodax-umi-request.

extend([options])

import { extend } from 'wodax-umi-request';

const request = extend({
  prefix: '/api/v1',
  timeout: 1000,
  headers: {
    'Content-Type': 'multipart/form-data'
  }
});

request.get('/user')
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

Create an instance of wodax-umi-request in NodeJS enviroment

const umi = require('wodax-umi-request');
const extendRequest = umi.extend({ timeout: 10000 })

extendRequest('/api/user')
  .then(res => {
    console.log(res);
  })
  .catch(err => {
    console.log(err);
  });

The available instance methods are list below. The specified options will be merge with the instance options.

request.get(url[, options])

request.post(url[, options])

request.delete(url[, options])

request.put(url[, options])

request.patch(url[, options])

request.head(url[, options])

request.options(url[, options])

More wodax-umi-request cases can see antd-pro

request options

Parameter Description Type Optional Value Default
method request method string get , post , put ... get
params url request parameters object or URLSearchParams -- --
data Submitted data any -- --
headers fetch original parameters object -- {}
timeout timeout, default millisecond, write with caution number -- --
prefix prefix, generally used to override the uniform settings prefix string -- --
suffix suffix, such as some scenes api need to be unified .json string --
credentials fetch request with cookies string -- credentials: 'same-origin'
useCache Whether to use caching (only support browser environment) boolean -- false
ttl Cache duration, 0 is not expired number -- 60000
maxCache Maximum number of caches number -- 0(Infinity)
requestType post request data type string json , form json
parseResponse response processing simplification boolean -- true
charset character set string utf8 , gbk utf8
responseType How to parse the returned data string json , text , blob , formData ... json , text
throwErrIfParseFail throw error when JSON parse fail and responseType is 'json' boolean -- false
getResponse Whether to get the source response, the result will wrap a layer boolean -- fasle
errorHandler exception handling, or override unified exception handling function(error) --
cancelToken Token to cancel request CancelToken.token -- --

The other parameters of fetch are valid. See fetch documentation

extend options Initialize default parameters, support all of the above

Parameter Description Type Optional Value Default
method request method string get , post , put ... get
params url request parameters object -- --
data Submitted data any -- --
...
{
  // 'method' is the request method to be used when making the request
  method: 'get', // default

  // 'params' are the URL parameters to be sent with request
  // Must be a plain object or a URLSearchParams object
  params: { id: 1 },

  // 'paramSerializer' is a function in charge of serializing 'params'. ( be aware of 'params' was merged by extends's 'params' and request's 'params' and URLSearchParams will be transform to plain object. )
  paramsSerializer: function (params) {
    return Qs.stringify(params, { arrayFormat: 'brackets' })
  },

  // 'data' 作为请求主体被发送的数据
  // 适用于这些请求方法 'PUT', 'POST', 和 'PATCH'
  // 必须是以下类型之一:
  // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
  // - 浏览器专属:FormData, File, Blob
  // - Node 专属: Stream

  // 'data' is the data to be sent as the request body
  // Only applicable for request methods 'PUT', 'POST', and 'PATCH'
  // Must be of one of the following types:
  // 1. string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
  // 2. Browser only: FormData, File, Blob
  // 3. Node only: Stream
  data: { name: 'Mike' },

  // 'headers' are custom headers to be sent
  headers: { 'Content-Type': 'multipart/form-data' },

  // 'timeout' specifies the number of milliseconds before the request times out.
  // If the request takes longer than 'timeout', request will be aborted and throw RequestError.
  timeout: 1000,

  // ’prefix‘ used to set URL's prefix
  // ( e.g. request('/user/save', { prefix: '/api/v1' }) => request('/api/v1/user/save') )
  prefix: '',

  // ’suffix‘ used to set URL's suffix
  // ( e.g. request('/api/v1/user/save', { suffix: '.json'}) => request('/api/v1/user/save.json') )
  suffix: '',

  // 'credentials' indicates whether the user agent should send cookies from the other domain in the case of cross-origin requests.
  // omit: Never send or receive cookies.
  // same-origin: Send user credentials (cookies, basic http auth, etc..) if the URL is on the same origin as the calling script. This is the default value.
  // include: Always send user credentials (cookies, basic http auth, etc..), even for cross-origin calls.
  credentials: 'same-origin', // default

  // ’useCache‘ The GET request would be cache in ttl milliseconds when 'useCache' is true.
  // The cache key would be 'url + params'.
  useCache: false, // default

  // 'ttl' cache duration(milliseconds),0 is infinity
  ttl: 60000,

  // 'maxCache' are the max number of requests to be cached, 0 means infinity.
  maxCache: 0,

  // 'requestType' wodax-umi-request will add headers and body according to the 'requestType' when the type of data is object or array.
  // 1. requestType === 'json' :(default )
  // options.headers = {
  //   Accept: 'application/json',
  //   'Content-Type': 'application/json;charset=UTF-8',
  //   ...options.headers,
  // }
  // options.body = JSON.stringify(data)
  // 
  // 2. requestType === 'form':
  // options.headers = {
  //   Accept: 'application/json',
  //   'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
  //   ...options.headers,
  // };
  // options.body = query-string.stringify(data);
  // 
  // 3. other requestType
  // options.headers = {
  //   Accept: 'application/json',
  //   ...options.headers,
  // };
  // options.body = data;
  requestType: 'json', // default

  // 'parseResponse' whether processing response 
  parseResponse: true, // default

  // 'charset' This parameter can be used when the server returns gbk to avoid garbled characters.(parseResponse should set to true)
  charset: 'gbk',

  // 'responseType': how to processing response.(parseResponse should be true)
  // The default value is 'json', would processing response by Response.text().then( d => JSON.parse(d) )
  // Other responseType (text, blob, arrayBuffer, formData), would processing response by Response[responseType]()
  responseType: 'json', // default

  // 'throwErrIfParseFail': whether throw error or not when JSON parse data fail and responseType is 'json'.
  throwErrIfParseFail: false, // default

  // 'getResponse': if you need the origin Response, set true and will return { data, response }.
  getResponse: false,// default

  // 'errorHandler' error handle entry.
  errorHandler: function(error) { /* 异常处理 */ },

  // 'cancelToken' the token of cancel request.
  cancelToken: null,
  }

Response Schema

The response for a request contains the following information.

{
  // 'data' is the response that was provided by the server
  data: {},

  // 'status' is the HTTP status code from the server response
  status: 200,

  // 'statusText' is the HTTP status message from the server response
  statusText: 'OK',

  // 'headers' the headers that the server responded with
  // All header names are lower cased
  headers: {},
}

When options.getResponse === false, the response schema would be 'data'

request.get('/api/v1/xxx', { getResponse: false })
  .then(function(data) {
    console.log(data);
  })

When options.getResponse === true ,the response schema would be { data, response }

request.get('/api/v1/xxx', { getResponse: true })
  .then(function({ data, response }) {
    console.log(data);
    console.log(response.status);
    console.log(response.statusText);
    console.log(response.headers);
  })

You can get Response from error object in errorHandler or request.catch.

Error handling

import request, { extend } from 'wodax-umi-request';

const errorHandler = function (error) {
const codeMap = {
    '021': 'An error has occurred',
    '022': 'It’s a big mistake,',
    // ....
};
  if (error.response) {
    // The request was made and the server responded with a status code
    // that falls out of the range of 2xx
    console.log(error.response.status);
    console.log(error.response.headers);
    console.log(error.data);
    console.log(error.request);
    console.log(codeMap[error.data.status])

  } else {
    // The request was made but no response was received or error occurs when setting up the request.
    console.log(error.message);
}

  throw error; // If throw. The error will continue to be thrown.

  // return {some: 'data'}; If return, return the value as a return. If you don't write it is equivalent to return undefined, you can judge whether the response has a value when processing the result.
  // return {some: 'data'}; 
}

// 1. Unified processing
const extendRequest = extend({ errorHandler });

// 2. Separate special treatment
// If unified processing is configured, but an api needs special handling. When requested, the errorHandler is passed as a parameter.
request('/api/v1/xxx', { errorHandler });


// 3. not configure errorHandler, the response will be directly treated as promise, and it will be caught.
request('/api/v1/xxx')
.then(function (response) {
  console.log(response);
})
.catch(function (error) {
  return errorHandler(error);
})

Middleware

Expressive HTTP middleware framework for node.js. For development to enhance before and after request. Support create instance, global, core middlewares.

Instance Middleware (default) request.use(fn) Different instances's instance middleware are independence. Global Middleware request.use(fn, { global: true }) Different instances share global middlewares. Core Middleware request.use(fn, { core: true }) Used to expand request core.

request.use(fn[, options])

params

fn params

  • ctx(Object):context, content request and response
  • next(Function):function to call the next middleware

options params

  • global(boolean): whether global, higher priority than core
  • core(boolean): whether core

example

  1. same type of middlewares
import request, { extend } from 'wodax-umi-request';
request.use(async (ctx, next) => {
  console.log('a1');
  await next();
  console.log('a2');
})
request.use(async (ctx, next) => {
  console.log('b1');
  await next();
  console.log('b2');
})

const data = await request('/api/v1/a');

order of middlewares be called:

a1 -> b1 -> response -> b2 -> a2
  1. Defferent type of middlewares
request.use( async (ctx, next) => {
  console.log('instanceA1');
  await next();
  console.log('instanceA2');
})
request.use( async (ctx, next) => {
  console.log('instanceB1');
  await next();
  console.log('instanceB2');
})
request.use( async (ctx, next) => {
  console.log('globalA1');
  await next();
  console.log('globalA2');
}, { global: true })
request.use( async (ctx, next) => {
  console.log('coreA1');
  await next();
  console.log('coreA2');
}, { core: true })

order of middlewares be called:

instanceA1 -> instanceB1 -> globalA1 -> coreA1 -> coreA2 -> globalA2 -> instanceB2 -> instanceA2
  1. Enhance request
request.use(async (ctx, next) => {
  const { req } = ctx;
  const { url, options } = req;

  if ( url.indexOf('/api') !== 0 ) {
    ctx.req.url = `/api/v1/${url}`;
  }
  ctx.req.options = {
    ...options,
    foo: 'foo'
  };

  await next();

  const { res } = ctx;
  const { success = false } = res; 
  if (!success) {
    // ...
  }
})

  1. Use core middleware to expand request core.

request.use(async (ctx, next) => {
  const { req } = ctx;
  const { url, options } = req;
  const { __umiRequestCoreType__ = 'normal' } = options;
  
  // __umiRequestCoreType__ use to identificat request core
  // when value is 'normal' , use wodax-umi-request 's fetch request core
  if ( __umiRequestCoreType__ === 'normal') {
    await next();
    return;
  }

  // when value is not normal, use your request func. 
  const response = getResponseByOtherWay();

  ctx.res = response;

  await next();
  return;
}, { core: true });


request('/api/v1/rpc', {
  __umiRequestCoreType__: 'rpc',
  parseResponse: false,
})
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  })

Interceptor

You can intercept requests or responses before they are handled by then or catch.

// request interceptor, change url or options.
request.interceptors.request.use((url, options) => {
  return (
    {
      url: `${url}&interceptors=yes`,
      options: { ...options, interceptors: true },
    }
  );
});

// response interceptor, chagne response
request.interceptors.response.use((response, options) => {
  response.headers.append('interceptors', 'yes yo');
  return response;
});

// handling error in response interceptor
request.interceptors.response.use((response) => {
  const codeMaps = {
    502: '网关错误。',
    503: '服务不可用,服务器暂时过载或维护。',
    504: '网关超时。',
  };
  message.error(codeMaps[response.status]);
  return response;
});

// clone response in response interceptor
request.interceptors.response.use(async (response) => {
  const data = await response.clone().json();
  if(data && data.NOT_LOGIN) {
    location.href = '登录url';
  }
  return response;
})

Cancel request

  1. You can cancel a request using a cancel token.
import Request from 'wodax-umi-request';

const CancelToken = Request.CancelToken;
const { token, cancel } = CancelToken.source();
 
Request.get('/api/cancel', {
  cancelToken: token
}).catch(function(thrown) {
  if (Request.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
    // handle error
  }
});

Request.post('/api/cancel', {
  name: 'hello world'
}, {
  cancelToken: token
})
 
// cancel request (the message parameter is optional)
cancel('Operation canceled by the user.');
  1. You can also create a cancel token by passing an executor function to the CancelToken constructor:
import Request from 'wodax-umi-request';

const CancelToken = Request.CancelToken;
let cancel;
 
Request.get('/api/cancel', {
  cancelToken: new CancelToken(function executor(c) {
    cancel = c;
  })
});
 
// cancel request
cancel();

FAQ

How to get Response Headers

use Headers.get() to get information from Response Headers. ( more detail see MDN doc)

request('/api/v1/some/api', { getResponse: true })
.then(({ data, response}) => {
  response.headers.get('Content-Type');
})

If want to get a custem header, you need to set Access-Control-Expose-Headers on server.

Cases

How to get Response Headers

Use Headers.get() (more detail see MDN 文档)

request('/api/v1/some/api', { getResponse: true })
.then(({ data, response}) => {
  response.headers.get('Content-Type');
})

File upload

Use FormData() contructor,the browser will add rerequest header "Content-Type: multipart/form-data" automatically, developer don't need to add request header Content-Type

const formData = new FormData();
formData.append('file', file);
request('/api/v1/some/api', { method:'post', data: formData });

The Access-Control-Expose-Headers response header indicates which headers can be exposed as part of the response by listing their names.Access-Control-Expose-Headers

Development and debugging

  • npm install
  • npm run dev
  • npm link
  • Then go to the project you are testing to execute npm link wodax-umi-request
  • Introduced and used

Questions & Suggestions

Please open an issue here.

LICENSE

MIT