# 网络请求

# 简单使用

this.$reqNetworkRequest 的一个实例

# this.$req.get(url)

  • 描述: get 请求
  • 参数
    • url
      • 类型: string
      • 是否必须: 是
      • 描述: 请求的地址
  • 返回值: Promise
<script>
export default {
  name: 'ajax-get-demo',
  methods: {
    getDataA() {
      this.$req.get('url').then(rs => {
        // TODO
      }).catch(err => {
        // Error handle
        this.$toast({ 'msg': err.message })
      })
    },
    async getDataB() {
      const rs = await this.$req.get('url/?param1=A&param2=B')
      // TODO
    },
  }
}
</script>

# this.$req.post(url, data, headers)

  • 描述: post 请求
  • 参数
    • url
      • 类型: string
      • 是否必须: 是
      • 描述: 请求的地址
    • data
      • 类型: object
      • 是否必须: 否
      • 描述: 发送的数据
    • headers
      • 类型: object
      • 是否必须: 否
      • 描述: 发送的请求头
  • 返回值: Promise
<script>
import { Base64 } from 'js-base64'

export default {
  name: 'ajax-post-demo',
  data() {
    return {
      username: '',
      password: '',
      someInfo: {}
    }
  },
  methods: {
    postDataA() {
      this.$req.post('url', {
        username: this.username,
        password: this.password
      }).then(rs => {
        // TODO
      }).catch(err => {
        // Error handle
        this.$toast({ 'msg': err.message })
      })
    },
    async postDataB() {
      const token = Base64.encode(`${Math.floor(new Date().getTime())}-${this.username}`)
      const rs = await this.$req.post('url', this.someInfo, { Authorization: token })
      // TODO
    },
  }
}
</script>

# this.$req.request(options)

  • 描述: 发送请求
  • 返回值: Promise
  • 参数
    • options
      • 类型: object
      • 是否必须: 是
      • 注意data, files, body, stream 之间,除了 datafiles 可以同时使用,其它参数都不能同时使用。
      • 描述: 请求的可选项,包括以下几项
可选项 类型 是否必须 默认值 描述
url string true undefined 请求地址
data object false undefined 发送的数据
files object false undefined 以表单方式提交文件,支持多文件上传(JSON对象),如 {"file": "path"},也支持同一字段对应多文件:{"file":["path1","path2"]}。文件路径,支持绝对路径,以及fs://、cache://、box://等文件路径协议。可直接使用其他端 API 返回的结果,如 api.getPicture 回调的 ret.data 等.
body object/string false undefined 以纯文本的方式提交数据,body 支持字符串及 JSON 对象(若要校验数据完整性,需将 JSON 对象转换成字符串再传入)。提交 JSON 对象时,需设置application/json 类型的 Content-Type
stream string false undefined 以二进制流的方式提交文件。stream为文件路径(字符串类型),支持绝对路径,以及fs://cache://box:// 等文件路径协议。可直接使用其他端API返回的结果,如 api.getPicture 回调的 ret.data
method string false 'get' 请求方式,取值范围 'get'、'post'、'put'、'delete'、 'head'、 'options'、 'patch'
encode boolean false true 是否对url进行编码。默认或传 true 时,Android 将始终对 url 编码,而 iOS 只有在 url 不合法(如存在中文字符)的时候才进行编码。如果url中有特殊字符需要编码的,建议先在 js 层进行编码,然后此参数传 false。
tag string false ajax-${new Date().getTime()} 该字段用于传给 cancelAjax 方法来取消请求,如果传入该字段,请保证各个 ajax 的 tag 字段唯一
timeout number false 30 超时时间,单位:秒
headers object false undefined 请求头
dataType string false 'json' 返回数据类型。若该字段传 json,接收到服务器返回的数据后会尝试将其转换成 JSON 对象,如果无法转成 JSON 对象,将返回数据类型错误,取值范围 'json','text';
returnAll boolean false false 是否需要返回所有 response 信息(包括响应头、消息体、状态码),为 true 时,返回的头信息获取方法(ret.headers),消息体信息获取方法(ret.body),状态码获取方法(ret.statusCode)
charset string false utf-8 当响应头里面没有返回字符集编码时,使用此编码来解析数据
report boolean false false 是否实时返回上传文件进度
cache boolean false false 是否缓存,若缓存,下次没网络时请求则会使用缓存,仅在 get 请求有效
certificate object false undefined 用于https请求开启双向认证的情况下,客户端配置p12安全证书设置。{ path: 'p12证书路径,支持fs://、widget://、cache://等文件路径协议,字符串类型' ,password:'证书密码,字符串类型' }
safeMode string false 'none' 设置请求安全模式。设置后,若检测到数据有安全风险时将返回 “不安全的数据” 错误 ,取值范围 'none', 'both','request', 'response';
proxy object false undefined 设置代理请求服务器。{ host: // 服务器地址,字符串类型 port: // 服务器端口,数字类型 }

# this.$req.setBaseUrl(baseUrl)

  • 描述: 设置请求的基础 url,之后请求即可使用相对地址
  • 参数
    • baseUrl
      • 类型: string
      • 是否必须: 是
      • 描述: 请求的基础网络地址
this.$req.setBaseUrl('https://api.example.com')

# this.$req.interceptor

  • 描述: 请求拦截器,在发送请求之前执行,可对请求参数进行预处理,通过 this.requestOptions 访问到请求的 options,这里 this 指向 NetworkRequest 实例,这里不可用箭头函数代替,否则 this 将指向 Vue 实例
  • 类型: Function
  • 返回值: boolean, 返回 true 将继续发送请求,返回 false 将取消请求发送

# this.$req.handleError

  • 描述: 错误处理,在发送请求出错时执行
  • 类型: Function
  • 参数: Error

# this.$req.afterRequest

  • 描述: 响应拦截器
  • 类型: Function
  • 参数: 服务端返回的数据
  • 返回值: 返回的数据将作为请求的返回数据
<script>
import { Base64 } from 'js-base64'

export default {
  name: 'ajax-interceptor-handleError-afterRequest-demo',
  data() {
    return {
      username: '',
      password: '',
      someInfo: {}
    }
  },
  methods: {
    initRequest () {
      const _this = this
      this.$req.setBaseUrl('https://api.example.com')
      this.$req.interceptor = function () {
        // 此处不可用箭头函数, 否则无法获取 NetworkRequest  实例的参数 requestOptions
        // 请求拦截器
        // 在发起请求前执行
        this.requestOptions.headers = {
          ...this.requestOptions.headers,
          'Authorization': Base64.encode(`${Math.floor(new Date().getTime())}-${_this.username}`)
        }
        return true
        // 返回值决定是否发起请求
        // 返回 true 继续发送请求
        // 返回 false 拦截请求,取消发送
      }
      this.$req.afterRequest = rs => {
        alert(`发送请求结束,返回的数据为 \n ${JSON.stringify(rs.data, null, 2)}`)
        return rs.data
      }
      this.$req.handleError = err => {
        // Error Handle
        this.$toast({ 'msg': err.message })
      }
    }
  },
  onReady() {
    this.initRequest()
  }
}
</script>

# this.$req.setTag(tag)

  • 描述: 设置当前请求的 tag, 用于传给 cancelAjax 方法来取消请求,如果传入该字段,请保证各个 ajax 的 tag 字段唯一
  • 参数
    • tag
      • 类型: string
      • 是否必须: 是
      • 描述: 每个请求的唯一标志

# this.$req.getTag()

  • 描述: 获取当前请求的 tag
  • 返回值
    • tag
      • 类型: string | CancelTokenSource
      • 描述: 若在 PC 端调试,则返回 axiosCancelTokenSource,若在 移动端 Loader 调试,则返回 设置的 tag 字符串或者默认的 tag 字符串

# this.$req.cancelAjax(tag)

  • 描述: 取消传入 tag 的请求
  • 参数
    • tag
      • 类型: string
      • 是否必须: 是
      • 描述: 要取消请求的标志

# 全局使用

新建 src/utils/HttpRequest.js

import { NetworkRequest } from 'vue-apicloud-quickstart'
import { Base64 } from 'js-base64'

export default class HttpRequest {
  constructor (baseUrl) {
    // 设置基础地址
    this.baseUrl = baseUrl
    // 存储请求队列
    this.queue = {}
  }

  // 销毁请求实例
  destroy (url) {
    delete this.queue[url]
    const queue = Object.keys(this.queue)
    return queue.length
  }

  // 请求拦截
  interceptors (instance, url) {
    // 添加请求拦截器
    instance.interceptor = function () {
      // 请求拦截器,在发送请求之前做点什么
      if (!url.includes('/login')) {
        this.requestOptions.header = {
          // TODO 如, 修改请求头内容
          ...this.requestOptions.header,
          'Authorization': Base64.encode(`${Math.floor(new Date().getTime())}-${window.$api.getStorage('token')}`)
        }
      }
      return true
    }

    // 添加响应拦截器
    instance.afterRequest = rs => {
      // TODO 请求结束之后做点什么
      this.destroy(url)
      return rs
    }

    instance.handleError = (/* err */) => {
      // TODO 统一的错误处理
    }
  }

  // 创建实例
  create () {
    const request = new NetworkRequest()
    request.setBaseUrl(this.baseUrl)
    request.requestOptions.header = {
      'Content-Type': 'application/json; charset=utf-8',
      'X-URL-PATH': location.pathname
    }
    return request
  }

  // 请求实例
  request (options) {
    const instance = this.create()
    this.interceptors(instance, options.url)
    options = Object.assign({}, options)
    this.queue[options.url] = instance
    return instance.request(options)
  }
}

新建 src/utils/request.js

import HttpRequest from './HttpRequest'

const Req = new HttpRequest('https://api.example.com')

export const login = (username, password) => {
  const data = { username, password }
  return Req.request({
    url: '/login',
    method: 'post',
    data
  })
}

export const getUserinfo = usertoken => {
  return Req.request({ url: `/users/${usertoken}` })
}

// 在这里定义业务的请求

在页面中使用, src/pages/login/index.vue:

<script>
import { login, getUserinfo } from '@/utils/request'

export default {
  name: 'ajax-demo',
  data() {
    return {
      username: '',
      password: '',
      usertoken: '',
      userinfo: {}
    }
  },
  methods: {
    loginApp() {
      login(this.username, this.password).then(rs => {
        this.usertoken = rs.usertoken
        this.getUserInfo()
      })
    },
    async getUserInfo() {
      const rs = await getUserinfo(this.usertoken)
      this.$api.setStorage('userinfo', rs.userinfo)
      this.userinfo = rs.userinfo
    }
  }
}
</script>