• 接口数据请求,解决跨域问题

#调用方法

  1. import { http } from '@/utils/index.js'
  2. http.post('/student_chat/Chat/getChatList', {
  3. chat_type: ''
  4. }).then(res => {
  5. console.log(res,'res');
  6. }).catch(err => {
  7. console.log(err,'err');
  8. })
  9. http.get('', {}).then(res => {
  10. console.log(res,'res');
  11. }).catch(err => {
  12. console.log(err,'err');
  13. })

#调用结果

  1. {
  2. "data": {
  3. "code": 1,
  4. "msg": "success",
  5. "data": {
  6. "list": [{
  7. "chat_no": "kf_group",
  8. "chat_type": "kf_group",
  9. "user_no": "",
  10. "chat_weight": 0,
  11. "msg_time": 0,
  12. "msg_unread_num": 0,
  13. "chat_target": {
  14. "target_title": "客服消息",
  15. "target_icon": "http://cdn.zsdx.cn/student-app/images/message/sys_kf_user.png"
  16. },
  17. "chat_user": [],
  18. "latest_msg": {
  19. "msg_id": 0,
  20. "msg_type": "",
  21. "msg_content": ""
  22. }
  23. }, {
  24. "chat_no": "chat_no_user_recommend",
  25. "chat_type": "user_recommend",
  26. "user_no": "user_no_user_recommend",
  27. "chat_weight": 0,
  28. "msg_time": 1583801141,
  29. "msg_unread_num": 0,
  30. "chat_target": {
  31. "target_title": "同校同学推荐",
  32. "target_icon": "http://cdn.zsdx.cn/student-app/images/message/user_recommend.png"
  33. },
  34. "chat_user": [],
  35. "latest_msg": {
  36. "msg_id": 0,
  37. "msg_type": "",
  38. "msg_content": ""
  39. }
  40. }, {
  41. "chat_no": "fc_c7061c8d390cf",
  42. "user_no": "st_c8142529456da",
  43. "chat_type": "social",
  44. "chat_weight": 0,
  45. "msg_time": 1585124777,
  46. "msg_unread_num": 0,
  47. "latest_msg": {
  48. "msg_id": 11371,
  49. "msg_type": "emoji",
  50. "msg_content": "{\"url\":\"http://pic.wxhand.com/dev/phaadmin_image/04d98272df70e4017eb981f9cbeaf054.png\",\"title\":\"奇怪脸\",\"preview_url\":\"\",\"w\":154,\"h\":154}"
  51. },
  52. "chat_user": {
  53. "student_no": "st_c8142529456da",
  54. "true_name": "你可愿与我再并肩",
  55. "head_img": "http://pic.wxhand.com/dev/student_image/9e3f6cea939ce916e18b449d9549e49f!Thumbwidth320",
  56. "sex": 1,
  57. "is_system": 0,
  58. "candle_step": 2,
  59. "candle_status": 0,
  60. "stuagent_level": 1
  61. },
  62. "chat_shop": [],
  63. "chat_company": []
  64. }, {
  65. "chat_no": "fc_a905236cd4dcf",
  66. "user_no": "st_87ca94738e53c",
  67. "chat_type": "social",
  68. "chat_weight": 0,
  69. "msg_time": 1583801141,
  70. "msg_unread_num": 0,
  71. "latest_msg": {
  72. "msg_id": 9865,
  73. "msg_type": "card_image",
  74. "msg_content": "{\"image_url\":\"http:\\/\\/cdn.zsdx.cn\\/student-app\\/images\\/message\\/msg_exam_result.png\",\"app_action\":{\"action\":\"app_page\",\"target\":\"forum_exam_result\",\"param\":{\"submit_id\":\"490\"}}}"
  75. },
  76. "chat_user": {
  77. "student_no": "st_87ca94738e53c",
  78. "true_name": "掌大客服",
  79. "head_img": "http://cdn.zsdx.cn/student-app/images/message/sys_kf.png",
  80. "sex": 1,
  81. "is_system": 1,
  82. "candle_step": 0,
  83. "candle_status": 0,
  84. "stuagent_level": 0
  85. },
  86. "chat_shop": [],
  87. "chat_company": []
  88. }]
  89. }
  90. },
  91. "statusCode": 200,
  92. "header": {
  93. "Access-Control-Allow-Origin": "*",
  94. "Content-Type": "application/json; charset=utf-8",
  95. "X-Powered-By": "PHP/7.1.4",
  96. "Access-Control-Allow-Methods": "POST, GET, OPTIONS",
  97. "Access-Control-Allow-Credentials": "true",
  98. "Access-Control-Max-Age": "86400",
  99. "Date": "Wed, 25 Mar 2020 12:17:47 GMT",
  100. "Access-Control-Allow-Headers": "zsdx-session-ticket, zsdx-app-type, zsdx-app-uuid, zsdx-version, zsdx-device-info, content-type",
  101. "Set-Cookie": "SERVERID=d3438ece8edc13d4d09e5c8a2df783d4|1585138667|1585138188;Path=/",
  102. "Transfer-Encoding": "Identity",
  103. "Connection": "keep-alive"
  104. },
  105. "errMsg": "request:ok",
  106. "config": {
  107. "url": "/student_chat/Chat/getChatList",
  108. "data": {
  109. "chat_type": "social"
  110. },
  111. "method": "POST",
  112. "baseUrl": "http://student-app-api.zsdx.cn/api",
  113. "dataType": "json",
  114. "params": {},
  115. "header": {
  116. "content-type": "application/json",
  117. "zsdx-app-type": 1,
  118. "zsdx-app-uuid": "",
  119. "zsdx-device-info": "...",
  120. "zsdx-session-ticket": "...",
  121. "zsdx-version": "..."
  122. },
  123. "custom": {},
  124. "sslVerify": true
  125. }
  126. }

#index.js

  1. import Request from './request'
  2. const http = new Request()
  3. http.setConfig((config) => { /* 设置全局配置 */
  4. config.baseUrl = 'http://student-app-api.zsdx.cn/api' /* 根域名配置 */
  5. config.header = {
  6. ...config.header,
  7. //请求头配置处理,配置接口需要的参数
  8. 'zsdx-app-type': '',
  9. 'zsdx-app-uuid': '',
  10. 'zsdx-device-info': '',
  11. 'zsdx-session-ticket': '',
  12. 'zsdx-version': ''
  13. //请求头配置处理,配置接口需要的参数
  14. }
  15. return config
  16. })
  17. /**
  18. * 自定义验证器,如果返回true 则进入响应拦截器的响应成功函数(resolve),否则进入响应拦截器的响应错误函数(reject)
  19. * @param { Number } statusCode - 请求响应体statusCode(只读)
  20. * @return { Boolean } 如果为true,则 resolve, 否则 reject
  21. */
  22. http.validateStatus = (statusCode) => {
  23. return statusCode === 200
  24. }
  25. http.interceptor.request((config, cancel) => { /* 请求之前拦截器 */
  26. config.header = {
  27. ...config.header
  28. }
  29. /*
  30. if (!token) { // 如果token不存在,调用cancel 会取消本次请求,但是该函数的catch() 仍会执行
  31. cancel('token 不存在') // 接收一个参数,会传给catch((err) => {}) err.errMsg === 'token 不存在'
  32. }
  33. */
  34. return config
  35. })
  36. http.interceptor.response((response) => { /* 请求之后拦截器 */
  37. // if (response.data.code !== 200) { // 服务端返回的状态码不等于200,则reject()
  38. // return Promise.reject(response)
  39. // }
  40. // if (response.config.custom.verification) { // 演示自定义参数的作用
  41. // return response.data
  42. // }
  43. if(response.data.code == '1001') { // 异常处理
  44. //...
  45. } else {
  46. return response
  47. }
  48. }, (response) => { // 请求错误做点什么
  49. return response
  50. })
  51. export {
  52. http
  53. }

#request.js

  1. export default class Request {
  2. config = {
  3. baseUrl: '',
  4. header: {
  5. 'content-type': 'application/json'
  6. },
  7. method: 'GET',
  8. dataType: 'json',
  9. // #ifndef MP-ALIPAY || APP-PLUS
  10. responseType: 'text',
  11. // #endif
  12. custom: {},
  13. // #ifdef MP-ALIPAY
  14. timeout: 30000,
  15. // #endif
  16. // #ifdef APP-PLUS
  17. sslVerify: true
  18. // #endif
  19. }
  20. static posUrl (url) { /* 判断url是否为绝对路径 */
  21. return /(http|https):\/\/([\w.]+\/?)\S*/.test(url)
  22. }
  23. static mergeUrl (url, baseUrl, params) {
  24. let mergeUrl = Request.posUrl(url) ? url : `${baseUrl}${url}`
  25. if (Object.keys(params).length !== 0) {
  26. const paramsH = Request.addQueryString(params)
  27. mergeUrl += mergeUrl.includes('?') ? `&${paramsH}` : `?${paramsH}`
  28. }
  29. return mergeUrl
  30. }
  31. static addQueryString (params) {
  32. let paramsData = ''
  33. Object.keys(params).forEach(function (key) {
  34. paramsData += key + '=' + encodeURIComponent(params[key]) + '&'
  35. })
  36. return paramsData.substring(0, paramsData.length - 1)
  37. }
  38. /**
  39. * @property {Function} request 请求拦截器
  40. * @property {Function} response 响应拦截器
  41. * @type {{request: Request.interceptor.request, response: Request.interceptor.response}}
  42. */
  43. interceptor = {
  44. /**
  45. * @param {Request~requestCallback} cb - 请求之前拦截,接收一个函数(config, cancel)=> {return config}。第一个参数为全局config,第二个参数为函数,调用则取消本次请求。
  46. */
  47. request: (cb) => {
  48. if (cb) {
  49. this.requestBeforeFun = cb
  50. }
  51. },
  52. /**
  53. * @param {Request~responseCallback} cb 响应拦截器,对响应数据做点什么
  54. * @param {Request~responseErrCallback} ecb 响应拦截器,对响应错误做点什么
  55. */
  56. response: (cb, ecb) => {
  57. if (cb) {
  58. this.requestComFun = cb
  59. }
  60. if (ecb) {
  61. this.requestComFail = ecb
  62. }
  63. }
  64. }
  65. requestBeforeFun (config) {
  66. return config
  67. }
  68. requestComFun (response) {
  69. return response
  70. }
  71. requestComFail (response) {
  72. return response
  73. }
  74. /**
  75. * 自定义验证器,如果返回true 则进入响应拦截器的响应成功函数(resolve),否则进入响应拦截器的响应错误函数(reject)
  76. * @param { Number } statusCode - 请求响应体statusCode(只读)
  77. * @return { Boolean } 如果为true,则 resolve, 否则 reject
  78. */
  79. validateStatus (statusCode) {
  80. return statusCode === 200
  81. }
  82. /**
  83. * @Function
  84. * @param {Request~setConfigCallback} f - 设置全局默认配置
  85. */
  86. setConfig (f) {
  87. this.config = f(this.config)
  88. }
  89. /**
  90. * @Function
  91. * @param {Object} options - 请求配置项
  92. * @prop {String} options.url - 请求路径
  93. * @prop {Object} options.data - 请求参数
  94. * @prop {Object} [options.responseType = config.responseType] [text|arraybuffer] - 响应的数据类型
  95. * @prop {Object} [options.dataType = config.dataType] - 如果设为 json,会尝试对返回的数据做一次 JSON.parse
  96. * @prop {Object} [options.header = config.header] - 请求header
  97. * @prop {Object} [options.method = config.method] - 请求方法
  98. * @returns {Promise<unknown>}
  99. */
  100. async request (options = {}) {
  101. options.baseUrl = this.config.baseUrl
  102. options.dataType = options.dataType || this.config.dataType
  103. // #ifndef MP-ALIPAY || APP-PLUS
  104. options.responseType = options.responseType || this.config.responseType
  105. // #endif
  106. // #ifdef MP-ALIPAY
  107. options.timeout = options.timeout || this.config.timeout
  108. // #endif
  109. options.url = options.url || ''
  110. options.data = options.data || {}
  111. options.params = options.params || {}
  112. options.header = options.header || this.config.header
  113. options.method = options.method || this.config.method
  114. options.custom = { ...this.config.custom, ...(options.custom || {}) }
  115. // #ifdef APP-PLUS
  116. options.sslVerify = options.sslVerify === undefined ? this.config.sslVerify : options.sslVerify
  117. // #endif
  118. options.getTask = options.getTask || this.config.getTask
  119. return new Promise((resolve, reject) => {
  120. let next = true
  121. const cancel = (t = 'handle cancel', config = options) => {
  122. const err = {
  123. errMsg: t,
  124. config: config
  125. }
  126. reject(err)
  127. next = false
  128. }
  129. const handleRe = { ...this.requestBeforeFun(options, cancel) }
  130. const _config = { ...handleRe }
  131. if (!next) return
  132. const requestTask = uni.request({
  133. url: Request.mergeUrl(_config.url, _config.baseUrl, _config.params),
  134. data: _config.data,
  135. header: _config.header,
  136. method: _config.method,
  137. // #ifdef MP-ALIPAY
  138. timeout: _config.timeout,
  139. // #endif
  140. dataType: _config.dataType,
  141. // #ifndef MP-ALIPAY || APP-PLUS
  142. responseType: _config.responseType,
  143. // #endif
  144. // #ifdef APP-PLUS
  145. sslVerify: _config.sslVerify,
  146. // #endif
  147. complete: (response) => {
  148. response.config = handleRe
  149. if (this.validateStatus(response.statusCode)) { // 成功
  150. response = this.requestComFun(response)
  151. resolve(response)
  152. } else {
  153. response = this.requestComFail(response)
  154. reject(response)
  155. }
  156. }
  157. })
  158. if (handleRe.getTask) {
  159. handleRe.getTask(requestTask, handleRe)
  160. }
  161. })
  162. }
  163. get (url, options = {}) {
  164. return this.request({
  165. url,
  166. method: 'GET',
  167. ...options
  168. })
  169. }
  170. post (url, data, options = {}) {
  171. return this.request({
  172. url,
  173. data,
  174. method: 'POST',
  175. ...options
  176. })
  177. }
  178. // #ifndef MP-ALIPAY
  179. put (url, data, options = {}) {
  180. return this.request({
  181. url,
  182. data,
  183. method: 'PUT',
  184. ...options
  185. })
  186. }
  187. // #endif
  188. // #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
  189. delete (url, data, options = {}) {
  190. return this.request({
  191. url,
  192. data,
  193. method: 'DELETE',
  194. ...options
  195. })
  196. }
  197. // #endif
  198. // #ifdef APP-PLUS || H5 || MP-WEIXIN
  199. connect (url, data, options = {}) {
  200. return this.request({
  201. url,
  202. data,
  203. method: 'CONNECT',
  204. ...options
  205. })
  206. }
  207. // #endif
  208. // #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
  209. head (url, data, options = {}) {
  210. return this.request({
  211. url,
  212. data,
  213. method: 'HEAD',
  214. ...options
  215. })
  216. }
  217. // #endif
  218. // #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
  219. options (url, data, options = {}) {
  220. return this.request({
  221. url,
  222. data,
  223. method: 'OPTIONS',
  224. ...options
  225. })
  226. }
  227. // #endif
  228. // #ifdef APP-PLUS || H5 || MP-WEIXIN
  229. trace (url, data, options = {}) {
  230. return this.request({
  231. url,
  232. data,
  233. method: 'TRACE',
  234. ...options
  235. })
  236. }
  237. // #endif
  238. upload (url, {
  239. // #ifdef APP-PLUS
  240. files,
  241. // #endif
  242. // #ifdef MP-ALIPAY
  243. fileType,
  244. // #endif
  245. filePath,
  246. name,
  247. header,
  248. formData = {},
  249. custom = {},
  250. params = {},
  251. getTask
  252. }) {
  253. return new Promise((resolve, reject) => {
  254. let next = true
  255. const globalHeader = { ...this.config.header }
  256. delete globalHeader['content-type']
  257. delete globalHeader['Content-Type']
  258. const pubConfig = {
  259. baseUrl: this.config.baseUrl,
  260. url,
  261. // #ifdef MP-ALIPAY
  262. fileType,
  263. // #endif
  264. filePath,
  265. method: 'UPLOAD',
  266. name,
  267. header: header || globalHeader,
  268. formData,
  269. params,
  270. custom: { ...this.config.custom, ...custom },
  271. getTask: getTask || this.config.getTask
  272. }
  273. // #ifdef APP-PLUS
  274. if (files) {
  275. pubConfig.files = files
  276. }
  277. // #endif
  278. const cancel = (t = 'handle cancel', config = pubConfig) => {
  279. const err = {
  280. errMsg: t,
  281. config: config
  282. }
  283. reject(err)
  284. next = false
  285. }
  286. const handleRe = { ...this.requestBeforeFun(pubConfig, cancel) }
  287. const _config = {
  288. url: Request.mergeUrl(handleRe.url, handleRe.baseUrl, handleRe.params),
  289. // #ifdef MP-ALIPAY
  290. fileType: handleRe.fileType,
  291. // #endif
  292. filePath: handleRe.filePath,
  293. name: handleRe.name,
  294. header: handleRe.header,
  295. formData: handleRe.formData,
  296. complete: (response) => {
  297. response.config = handleRe
  298. if (typeof response.data === 'string') {
  299. response.data = JSON.parse(response.data)
  300. }
  301. if (this.validateStatus(response.statusCode)) { // 成功
  302. response = this.requestComFun(response)
  303. resolve(response)
  304. } else {
  305. response = this.requestComFail(response)
  306. reject(response)
  307. }
  308. }
  309. }
  310. // #ifdef APP-PLUS
  311. if (handleRe.files) {
  312. _config.files = handleRe.files
  313. }
  314. // #endif
  315. if (!next) return
  316. const requestTask = uni.uploadFile(_config)
  317. if (handleRe.getTask) {
  318. handleRe.getTask(requestTask, handleRe)
  319. }
  320. })
  321. }
  322. download (url, options = {}) {
  323. return new Promise((resolve, reject) => {
  324. let next = true
  325. const pubConfig = {
  326. baseUrl: this.config.baseUrl,
  327. url,
  328. method: 'DOWNLOAD',
  329. header: options.header || this.config.header,
  330. params: options.params || {},
  331. custom: { ...this.config.custom, ...(options.custom || {}) },
  332. getTask: options.getTask || this.config.getTask
  333. }
  334. const cancel = (t = 'handle cancel', config = pubConfig) => {
  335. const err = {
  336. errMsg: t,
  337. config: config
  338. }
  339. reject(err)
  340. next = false
  341. }
  342. const handleRe = { ...this.requestBeforeFun(pubConfig, cancel) }
  343. if (!next) return
  344. const requestTask = uni.downloadFile({
  345. url: Request.mergeUrl(handleRe.url, handleRe.baseUrl, handleRe.params),
  346. header: handleRe.header,
  347. complete: (response) => {
  348. response.config = handleRe
  349. if (this.validateStatus(response.statusCode)) { // 成功
  350. response = this.requestComFun(response)
  351. resolve(response)
  352. } else {
  353. response = this.requestComFail(response)
  354. reject(response)
  355. }
  356. }
  357. })
  358. if (handleRe.getTask) {
  359. handleRe.getTask(requestTask, handleRe)
  360. }
  361. })
  362. }
  363. }
  364. /**
  365. * setConfig回调
  366. * @return {Object} - 返回操作后的config
  367. * @callback Request~setConfigCallback
  368. * @param {Object} config - 全局默认config
  369. */
  370. /**
  371. * 请求拦截器回调
  372. * @return {Object} - 返回操作后的config
  373. * @callback Request~requestCallback
  374. * @param {Object} config - 全局config
  375. * @param {Function} [cancel] - 取消请求钩子,调用会取消本次请求
  376. */
  377. /**
  378. * 响应拦截器回调
  379. * @return {Object} - 返回操作后的response
  380. * @callback Request~responseCallback
  381. * @param {Object} response - 请求结果 response
  382. */
  383. /**
  384. * 响应错误拦截器回调
  385. * @return {Object} - 返回操作后的response
  386. * @callback Request~responseErrCallback
  387. * @param {Object} response - 请求结果 response
  388. */

#文档说明

  1. - 基于 Promise 对象实现更简单的 request 使用方式,支持请求和响应拦截
  2. - 支持全局挂载
  3. - 支持多个全局配置实例
  4. - 支持自定义验证器
  5. - 支持文件上传/下载(如不使用可以删除classuploaddownload 方法)
  6. - http-request 文件夹放到项目 utils/ 目录下
  7. **Example**
  8. ---
  9. 创建实例
  10. ``` javascript
  11. const http = new Request();

执行GET请求

  1. http.get('/user/login', {params: {userName: 'name', password: '123456'}}).then(res => {
  2. }).catch(err => {
  3. })
  4. // 局部修改配置,局部配置优先级高于全局配置
  5. http.get('/user/login', {
  6. params: {userName: 'name', password: '123456'}, /* 会加在url上 */
  7. header: {}, /* 会覆盖全局header */
  8. dataType: 'json',
  9. // 注:如果局部custom与全局custom有同名属性,则后面的属性会覆盖前面的属性,相当于Object.assign(全局,局部)
  10. custom: {auth: true}, // 可以加一些自定义参数,在拦截器等地方使用。比如这里我加了一个auth,可在拦截器里拿到,如果true就传token
  11. // #ifndef MP-ALIPAY || APP-PLUS
  12. responseType: 'text',
  13. // #endif
  14. // #ifdef MP-ALIPAY
  15. timeout: 30000, // 仅支付宝小程序支持
  16. // #endif
  17. // #ifdef APP-PLUS
  18. sslVerify: true, // 验证 ssl 证书 仅5+App安卓端支持(HBuilderX 2.3.3+)
  19. // #endif
  20. // 返回当前请求的task, options。请勿在此处修改options。非必填
  21. getTask: (task, options) => {
  22. // setTimeout(() => {
  23. // task.abort()
  24. // }, 500)
  25. }
  26. }).then(res => {
  27. }).catch(err => {
  28. })

执行POST请求

  1. http.post('/user/login', {userName: 'name', password: '123456'} ).then(res => {
  2. }).catch(err => {
  3. })
  4. // 局部修改配置,局部配置优先级高于全局配置
  5. http.post('/user/login', {userName: 'name', password: '123456'}, {
  6. params: {}, /* 会加在url上 */
  7. header: {}, /* 会覆盖全局header */
  8. dataType: 'json',
  9. // 注:如果局部custom与全局custom有同名属性,则后面的属性会覆盖前面的属性,相当于Object.assign(全局,局部)
  10. custom: {auth: true}, // 可以加一些自定义参数,在拦截器等地方使用。比如这里我加了一个auth,可在拦截器里拿到,如果true就传token
  11. // #ifndef MP-ALIPAY || APP-PLUS
  12. responseType: 'text',
  13. // #endif
  14. // #ifdef MP-ALIPAY
  15. timeout: 30000, // 仅支付宝小程序支持
  16. // #endif
  17. // #ifdef APP-PLUS
  18. sslVerify: true, // 验证 ssl 证书 仅5+App安卓端支持(HBuilderX 2.3.3+)
  19. // #endif
  20. // 返回当前请求的task, options。请勿在此处修改options。非必填
  21. getTask: (task, options) => {
  22. // setTimeout(() => {
  23. // task.abort()
  24. // }, 500)
  25. }
  26. }).then(res => {
  27. }).catch(err => {
  28. })

执行upload请求

  1. http.upload('api/upload/img', {
  2. params: {}, /* 会加在url上 */
  3. files: [], // 仅5+App支持
  4. fileType: 'image/video/audio', // 仅支付宝小程序,且必填。
  5. filePath: '', // 要上传文件资源的路径。
  6. // 注:如果局部custom与全局custom有同名属性,则后面的属性会覆盖前面的属性,相当于Object.assign(全局,局部)
  7. custom: {auth: true}, // 可以加一些自定义参数,在拦截器等地方使用。比如这里我加了一个auth,可在拦截器里拿到,如果true就传token
  8. name: 'file', // 文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容
  9. header: {},
  10. formData: {}, // HTTP 请求中其他额外的 form data
  11. // 返回当前请求的task, options。请勿在此处修改options。非必填
  12. getTask: (task, options) => {
  13. // task.onProgressUpdate((res) => {
  14. // console.log('上传进度' + res.progress);
  15. // console.log('已经上传的数据长度' + res.totalBytesSent);
  16. // console.log('预期需要上传的数据总长度' + res.totalBytesExpectedToSend);
  17. //
  18. // // 测试条件,取消上传任务。
  19. // if (res.progress > 50) {
  20. // uploadTask.abort();
  21. // }
  22. // });
  23. }
  24. }).then(res => {
  25. // 返回的res.data 已经进行JSON.parse
  26. }).catch(err => {
  27. })

luch-request API

  1. http.request({
  2. method: 'POST', // 请求方法必须大写
  3. url: '/user/12345',
  4. data: {
  5. firstName: 'Fred',
  6. lastName: 'Flintstone'
  7. },
  8. params: { // 会拼接到url上
  9. token: '1111'
  10. },
  11. // 注:如果局部custom与全局custom有同名属性,则后面的属性会覆盖前面的属性,相当于Object.assign(全局,局部)
  12. custom: {}, // 自定义参数
  13. // 返回当前请求的task, options。请勿在此处修改options。非必填
  14. getTask: (task, options) => {
  15. // setTimeout(() => {
  16. // task.abort()
  17. // }, 500)
  18. }
  19. })
  20. // 具体参数说明:[uni.uploadFile](https://uniapp.dcloud.io/api/request/network-file)
  21. http.upload('api/upload/img', {
  22. params: {}, /* 会加在url上 */
  23. files: [], // 仅5+App支持
  24. fileType: 'image/video/audio', // 仅支付宝小程序,且必填。
  25. filePath: '', // 要上传文件资源的路径。
  26. name: 'file', // 文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容
  27. header: {}, // 如填写,会覆盖全局header,
  28. custom: {}, // 自定义参数
  29. formData: {}, // HTTP 请求中其他额外的 form data
  30. // 返回当前请求的task, options。请勿在此处修改options。非必填
  31. getTask: (task, options) => {
  32. // setTimeout(() => {
  33. // task.abort()
  34. // }, 500)
  35. }
  36. }).then(res => {
  37. // 返回的res.data 已经进行JSON.parse
  38. }).catch(err => {
  39. })
  40. // 具体参数说明:[uni.downloadFile](https://uniapp.dcloud.io/api/request/network-file?id=downloadfile)
  41. http.download('api/download', {
  42. params: {}, /* 会加在url上 */
  43. header: {}, // 如填写,会覆盖全局header,
  44. custom: {}, // 自定义参数
  45. // 返回当前请求的task, options。非必填
  46. getTask: (task, options) => {
  47. // setTimeout(() => {
  48. // task.abort()
  49. // }, 500)
  50. }
  51. }).then(res => {
  52. }).catch(err => {
  53. })

请求方法别名 / 实例方法

  1. http.request(config)
  2. http.get(url[, config])
  3. http.upload(url[, config])
  4. http.delete(url[, data[, config]])
  5. http.head(url[, data[, config]])
  6. http.post(url[, data[, config]])
  7. http.put(url[, data[, config]])
  8. http.connect(url[, data[, config]])
  9. http.options(url[, data[, config]])
  10. http.trace(url[, data[, config]])

全局请求配置

  1. {
  2. baseUrl: '',
  3. header: {
  4. 'content-type': 'application/json'
  5. },
  6. method: 'GET',
  7. dataType: 'json',
  8. // #ifndef MP-ALIPAY || APP-PLUS
  9. responseType: 'text',
  10. // #endif
  11. // 注:如果局部custom与全局custom有同名属性,则后面的属性会覆盖前面的属性,相当于Object.assign(全局,局部)
  12. custom: {}, // 全局自定义参数默认值
  13. // #ifdef MP-ALIPAY
  14. timeout: 30000,
  15. // #endif
  16. // #ifdef APP-PLUS
  17. sslVerify: true,
  18. // #endif
  19. // 局部优先级高于全局,返回当前请求的task,options。请勿在此处修改options。非必填
  20. // getTask: (task, options) => {
  21. // 相当于设置了请求超时时间500ms
  22. // setTimeout(() => {
  23. // task.abort()
  24. // }, 500)
  25. // }
  26. }

全局配置修改setConfig

  1. /**
  2. * @description 修改全局默认配置
  3. * @param {Function}
  4. */
  5. http.setConfig((config) => { /* config 为默认全局配置*/
  6. config.baseUrl = 'http://www.bbb.cn'; /* 根域名 */
  7. config.header = {
  8. a: 1,
  9. b: 2
  10. }
  11. return config
  12. })

自定义验证器validateStatus

  1. /**
  2. * 自定义验证器,如果返回true 则进入响应拦截器的响应成功函数(resolve),否则进入响应拦截器的响应错误函数(reject)
  3. * @param { Number } statusCode - 请求响应体statusCode(只读)
  4. * @return { Boolean } 如果为true,则 resolve, 否则 reject
  5. */
  6. http.validateStatus = (statusCode) => { // 默认
  7. return statusCode === 200
  8. }
  9. // 举个栗子
  10. http.validateStatus = (statusCode) => {
  11. return statusCode && statusCode >= 200 && statusCode < 300
  12. }

拦截器

在请求之前拦截

  1. /**
  2. * @param { Function} cancel - 取消请求,调用cancel 会取消本次请求,但是该函数的catch() 仍会执行; 不会进入响应拦截器
  3. *
  4. * @param {String} text ['handle cancel'| any] - catch((err) => {}) err.errMsg === 'handle cancel'。非必传,默认'handle cancel'
  5. * @cancel {Object} config - catch((err) => {}) err.config === config; 非必传,默认为request拦截器修改之前的config
  6. * function cancel(text, config) {}
  7. */
  8. http.interceptor.request((config, cancel) => { /* cancel 为函数,如果调用会取消本次请求。需要注意:调用cancel,本次请求的catch仍会执行。必须return config */
  9. config.header = {
  10. ...config.header,
  11. a: 1
  12. }
  13. // if (config.custom.auth) {
  14. // config.header.token = 'token'
  15. // }
  16. /**
  17. if (!token) { // 如果token不存在,调用cancel 会取消本次请求,不会进入响应拦截器,但是该函数的catch() 仍会执行
  18. cancel('token 不存在', config) // 把修改后的config传入,之后响应就可以拿到修改后的config。 如果调用了cancel但是不传修改后的config,则catch((err) => {}) err.config 为request拦截器修改之前的config
  19. }
  20. **/
  21. return config
  22. })

在请求之后拦截

  1. http.interceptor.response((response) => { /* 对响应成功做点什么 (statusCode === 200),必须return response*/
  2. // if (response.data.code !== 200) { // 服务端返回的状态码不等于200,则reject()
  3. // return Promise.reject(response)
  4. // }
  5. // if (response.config.custom.verification) { // 演示自定义参数的作用
  6. // return response.data
  7. // }
  8. console.log(response)
  9. return response
  10. }, (response) => { /* 对响应错误做点什么 (statusCode !== 200),必须return response*/
  11. console.log(response)
  12. return response
  13. })

常见问题

  1. 为什么会请求两次?
    • 总有些小白问这些很那啥的问题,有两种可能,一种是‘post三次握手’,还有一种可能是本地访问接口时跨域请求,所以浏览器会先发一个option 去预测能否成功,然后再发一个真正的请求(自己观察请求头,Request Method,百度简单请求)。
  2. 如何跨域?
    • 问的人不少,可以先百度了解一下。如何跨域
  3. post 怎么传不了数组的参数啊?
    • uni-request
      可以点击看一下uni-request 的api 文档,data支持的文件类型只有Object/String/ArrayBuffer这个真跟我没啥关系 0.0
  4. ‘Content-Type’ 为什么要小写?
    • hbuilderX 更新至‘2.3.0.20190919’ 后,uni.request post请求,如果 ‘Content-Type’ 大写,就会在后面自动拼接‘ application/json’,请求头变成 Content-Type: application/json;charset=UTF-8 application/json,导致后端无法解析类型,Status Code 415,post 请求失败。但是小写就不会出现这个问题。至于为什么我也没有深究,我现在也不清楚这是他们的bug,还是以后就这样规范了。我能做的只有立马兼容,至于后边uni官方会不会继续变动也不清楚。

```