axios.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. import Axios from 'axios'
  2. import defu from 'defu'
  3. // Axios.prototype cannot be modified
  4. const axiosExtra = {
  5. setBaseURL (baseURL) {
  6. this.defaults.baseURL = baseURL
  7. },
  8. setHeader (name, value, scopes = 'common') {
  9. for (let scope of Array.isArray(scopes) ? scopes : [ scopes ]) {
  10. if (!value) {
  11. delete this.defaults.headers[scope][name];
  12. return
  13. }
  14. this.defaults.headers[scope][name] = value
  15. }
  16. },
  17. setToken (token, type, scopes = 'common') {
  18. const value = !token ? null : (type ? type + ' ' : '') + token
  19. this.setHeader('Authorization', value, scopes)
  20. },
  21. onRequest(fn) {
  22. this.interceptors.request.use(config => fn(config) || config)
  23. },
  24. onResponse(fn) {
  25. this.interceptors.response.use(response => fn(response) || response)
  26. },
  27. onRequestError(fn) {
  28. this.interceptors.request.use(undefined, error => fn(error) || Promise.reject(error))
  29. },
  30. onResponseError(fn) {
  31. this.interceptors.response.use(undefined, error => fn(error) || Promise.reject(error))
  32. },
  33. onError(fn) {
  34. this.onRequestError(fn)
  35. this.onResponseError(fn)
  36. },
  37. create(options) {
  38. return createAxiosInstance(defu(options, this.defaults))
  39. }
  40. }
  41. // Request helpers ($get, $post, ...)
  42. for (let method of ['request', 'delete', 'get', 'head', 'options', 'post', 'put', 'patch']) {
  43. axiosExtra['$' + method] = function () { return this[method].apply(this, arguments).then(res => res && res.data) }
  44. }
  45. const extendAxiosInstance = axios => {
  46. for (let key in axiosExtra) {
  47. axios[key] = axiosExtra[key].bind(axios)
  48. }
  49. }
  50. const createAxiosInstance = axiosOptions => {
  51. // Create new axios instance
  52. const axios = Axios.create(axiosOptions)
  53. axios.CancelToken = Axios.CancelToken
  54. axios.isCancel = Axios.isCancel
  55. // Extend axios proto
  56. extendAxiosInstance(axios)
  57. // Setup interceptors
  58. setupCredentialsInterceptor(axios)
  59. setupProgress(axios)
  60. return axios
  61. }
  62. const setupCredentialsInterceptor = axios => {
  63. // Send credentials only to relative and API Backend requests
  64. axios.onRequest(config => {
  65. if (config.withCredentials === undefined) {
  66. if (!/^https?:\/\//i.test(config.url) || config.url.indexOf(config.baseURL) === 0) {
  67. config.withCredentials = true
  68. }
  69. }
  70. })
  71. }
  72. const setupProgress = (axios) => {
  73. if (process.server) {
  74. return
  75. }
  76. // A noop loading inteterface for when $nuxt is not yet ready
  77. const noopLoading = {
  78. finish: () => { },
  79. start: () => { },
  80. fail: () => { },
  81. set: () => { }
  82. }
  83. const $loading = () => (window.$nuxt && window.$nuxt.$loading && window.$nuxt.$loading.set) ? window.$nuxt.$loading : noopLoading
  84. let currentRequests = 0
  85. axios.onRequest(config => {
  86. if (config && config.progress === false) {
  87. return
  88. }
  89. currentRequests++
  90. })
  91. axios.onResponse(response => {
  92. if (response && response.config && response.config.progress === false) {
  93. return
  94. }
  95. currentRequests--
  96. if (currentRequests <= 0) {
  97. currentRequests = 0
  98. $loading().finish()
  99. }
  100. })
  101. axios.onError(error => {
  102. if (error && error.config && error.config.progress === false) {
  103. return
  104. }
  105. currentRequests--
  106. if (Axios.isCancel(error)) {
  107. return
  108. }
  109. $loading().fail()
  110. $loading().finish()
  111. })
  112. const onProgress = e => {
  113. if (!currentRequests) {
  114. return
  115. }
  116. const progress = ((e.loaded * 100) / (e.total * currentRequests))
  117. $loading().set(Math.min(100, progress))
  118. }
  119. axios.defaults.onUploadProgress = onProgress
  120. axios.defaults.onDownloadProgress = onProgress
  121. }
  122. export default (ctx, inject) => {
  123. // baseURL
  124. const baseURL = process.browser
  125. ? 'http://localhost:3000/'
  126. : (process.env._AXIOS_BASE_URL_ || 'http://localhost:3000/')
  127. // Create fresh objects for all default header scopes
  128. // Axios creates only one which is shared across SSR requests!
  129. // https://github.com/mzabriskie/axios/blob/master/lib/defaults.js
  130. const headers = {
  131. "common": {
  132. "Accept": "application/json, text/plain, */*"
  133. },
  134. "delete": {},
  135. "get": {},
  136. "head": {},
  137. "post": {},
  138. "put": {},
  139. "patch": {}
  140. }
  141. const axiosOptions = {
  142. baseURL,
  143. headers
  144. }
  145. // Proxy SSR request headers headers
  146. axiosOptions.headers.common = (ctx.req && ctx.req.headers) ? Object.assign({}, ctx.req.headers) : {}
  147. delete axiosOptions.headers.common['accept']
  148. delete axiosOptions.headers.common['host']
  149. delete axiosOptions.headers.common['cf-ray']
  150. delete axiosOptions.headers.common['cf-connecting-ip']
  151. delete axiosOptions.headers.common['content-length']
  152. delete axiosOptions.headers.common['content-md5']
  153. delete axiosOptions.headers.common['content-type']
  154. if (process.server) {
  155. // Don't accept brotli encoding because Node can't parse it
  156. axiosOptions.headers.common['accept-encoding'] = 'gzip, deflate'
  157. }
  158. const axios = createAxiosInstance(axiosOptions)
  159. // Inject axios to the context as $axios
  160. ctx.$axios = axios
  161. inject('axios', axios)
  162. }