request

request({
url: 'path',
query: { ... },    //  url 请求参数
params: { ... },   //  url 宏参值替换
body: { ... },     //  请求体
headers: { ... },  //  请求头
interceptors: {    //  请求和返回体加工函数的数组对象
request: [func1, func2, ...],
response: [func1, func2, ...]
}
})
request('path')
request('path', { ... })

@param  {string} [url]       URL
@param  {object} [options]   Options
@return {promise}            Promise
async function request (url, options) {
  if (!options) {
    if (isPlainObject(url)) {
      options = url
      url = null
    } else {
      options = {
        url
      }
    }
  }

  if (!isPlainObject(options)) {
    throw new ParameterException('Options must be an object!')
  }

  if (isString(url)) {
    options.url = url
  }

  if (!isString(options.url)) {
    throw new ParameterException('URL is required!')
  }

  let { interceptors, ...req } = options
  interceptors = intercept(interceptors)

  req = await parseReq(req)
  req = await iterateInterceptors(req, interceptors.request)

  const res = await fetch(req.url, req)

  let body = await getBody(res)
  body = await iterateInterceptors(body, interceptors.response)

  if (res.status >= 200 && res.status < 400) {
    return body
  }

  throw body
}