uni-APP:实现前后端数据交互(登录案例)

uni-APP:实现前后端数据交互(登录案例)

上一个项目是用vue写的,然后这次采用了一样的写法(错误写法)

methods: {
			login(){
				let uid = this.uid;
				let password = this.password;
				this.$http.post("/userLogin",{uid,password}).then(res=>{
					console.log(res);
					if(res.code==1){
						console.log("登录成功")
					}
				})
			}
		}

在登录时出现chunk-vendors.js:3962 [Vue warn]: Error in v-on handler: “TypeError: Cannot read property ‘post’ of undefined”

found in
在这里插入图片描述

  • 原因在于我的 h t t p 方 法 没 有 注 入 , 也 就 是 说 , http方法没有注入,也就是说, httphttp在我这个项目里不存在,原先vue是没有内置网络请求的,需要引入axios,uni-APP不用。
  • 解决方案:
    建立common/request文件夹,目录如下:
    在这里插入图片描述
    api:.js (集中管理所有接口,比如此处的login,所有的请求都要在api.js注册后才能使用)
/**
 * 接口列表文件
 */
export default {
	user:{
		//获取sessionkey
		login:{
			url: 'userLogin',
			auth: false,
			method: 'POST'
		},
		
	}


};

index.js: (是主运行文件,用来处理请求拦截的),这些内容打印就是index.js去做的,把你发送了什么,服务器返回了什么打印出来)

在这里插入图片描述
在这里插入图片描述

import Request from './request'
import apiList from './api'
import store from '@/store/index.js'

export default function api(url, data = {}) {
	const request = new Request();
	let api = getApiObj(url);
	request.interceptor.request((config, cancel) => { /* 请求之前拦截器 */
		if (api.auth) {
			let token = uni.getStorageSync('token');
			if (!token) { // 如果token不存在,调用cancel 会取消本次请求,但是该函数的catch() 仍会执行
				cancel('token 不存在'); // 接收一个参数,会传给catch((err) => {}) err.errMsg === 'token 不存在'
				store.commit('LOGIN_TIP', true)
				// store.commit('OUT_LOGIN');
			}
		}
		if (uni.getStorageSync('token')) {
			config.header.token = uni.getStorageSync('token');
		}
		// if(store.state.envVersion!='release'){
		console.log('客户端发送:',config.url, config.data,new Date().valueOf())
		// }
		return config
	});

	request.interceptor.response((response) => { /* 请求之后拦截器 */
		// if(store.state.envVersion!='release'){
			console.log('客户端收到', response.config.url, response,new Date().valueOf());
		// }
		if (response.errMsg == "request:fail timeout") {
			uni.showToast({
				title: '连接超时,请检查网络',
				icon: 'none'
			})
		}else if (response.statusCode != 200) {
			uni.showToast({
				title: '服务器繁忙',
				icon: 'none'
			})
		}else if (response.data.code === 0) { 
			
			uni.showToast({
				title: response.data.data || '请求出错,稍后重试',
				icon: 'none',
				duration: 1000,
				mask: true
			});
		}else if (response.data.code === 401 || response.data.code === 402) { // 服务端返回的状态码不等于200,则reject()
			uni.removeStorageSync('token');
			uni.removeStorageSync('user_id');
			store.commit('LOGIN_TIP', true)
		}
		// if (response.config.custom.verification) { // 演示自定义参数的作用
		//   return response.data
		// }
		return response
	}, (response) => { // 预留可以日志上报
		return response
	})

	return request.request({
		url: api.url,
		data,
		method: api.method
	},api.isBase)

}

function getApiObj(url) {
	let apiArray = url.split(".");
	let api = apiList;
	apiArray.forEach(v => {
		api = api[v];
	});
	return api;
}

request.js (request.js是对基层网络请求的封装)


import store from '@/store';

export default class Request {
	config = {
		baseUrl: store.state.serverUrl,
		header: {
			'content-type': 'application/json'
		},
		loadingText: '请求中...',
		loadingTime: 1000, // 在此时间内,请求还没回来的话,就显示加载中动画,单位ms
		timer: null, // 定时器
		timerState: false, //loading开启了没有
		method: 'GET',
		dataType: 'json',
		// #ifndef MP-ALIPAY || APP-PLUS
		responseType: 'text',
		// #endif
		custom: {},
		// #ifdef MP-ALIPAY
		timeout: 30000,
		// #endif
		// #ifdef APP-PLUS
		sslVerify: true
		// #endif
	}

	static posUrl(url) { /* 判断url是否为绝对路径 */
		return /(http|https):\/\/([\w.]+\/?)\S*/.test(url)
	}

	static addQueryString(params) {
		let paramsData = ''
		Object.keys(params).forEach(function(key) {
			paramsData += key + '=' + encodeURIComponent(params[key]) + '&'
		})
		return paramsData.substring(0, paramsData.length - 1)
	}

	/**
	 * @property {Function} request 请求拦截器
	 * @property {Function} response 响应拦截器
	 * @type {{request: Request.interceptor.request, response: Request.interceptor.response}}
	 */
	interceptor = {
		/**
		 * @param {Request~requestCallback} cb - 请求之前拦截,接收一个函数(config, cancel)=> {return config}。第一个参数为全局config,第二个参数为函数,调用则取消本次请求。
		 */
		request: (cb) => {
			if (cb) {
				this.requestBeforeFun = cb
			}
		},
		/**
		 * @param {Request~responseCallback} cb 响应拦截器,对响应数据做点什么
		 * @param {Request~responseErrCallback} ecb 响应拦截器,对响应错误做点什么
		 */
		response: (cb, ecb) => {
			if (cb && ecb) {
				this.requestComFun = cb
				this.requestComFail = ecb
			}
		}
	}

	requestBeforeFun(config) {
		return config
	}

	requestComFun(response) {
		return response
	}

	requestComFail(response) {
		return response
	}

	/**
	 * 自定义验证器,如果返回true 则进入响应拦截器的响应成功函数(resolve),否则进入响应拦截器的响应错误函数(reject)
	 * @param { Number } statusCode - 请求响应体statusCode(只读)
	 * @return { Boolean } 如果为true,则 resolve, 否则 reject
	 */
	validateStatus(statusCode) {
		return statusCode === 200
	}

	/**
	 * @Function
	 * @param {Request~setConfigCallback} f - 设置全局默认配置
	 */
	setConfig(f) {
		this.config = f(this.config)
	}

	/**
	 * @Function
	 * @param {Object} options - 请求配置项
	 * @prop {String} options.url - 请求路径
	 * @prop {Object} options.data - 请求参数
	 * @prop {Object} [options.responseType = config.responseType] [text|arraybuffer] - 响应的数据类型
	 * @prop {Object} [options.dataType = config.dataType] - 如果设为 json,会尝试对返回的数据做一次 JSON.parse
	 * @prop {Object} [options.header = config.header] - 请求header
	 * @prop {Object} [options.method = config.method] - 请求方法
	 * @returns {Promise<unknown>}
	 */
	async request(options = {}) {
		options.baseUrl = store.state.serverUrl
		options.dataType = options.dataType || this.config.dataType
		// #ifndef MP-ALIPAY || APP-PLUS
		options.responseType = options.responseType || this.config.responseType
		// #endif
		// #ifdef MP-ALIPAY
		options.timeout = options.timeout || this.config.timeout
		// #endif
		options.url = options.url || ''
		options.data = options.data || {}
		options.params = options.params || {}
		options.header = options.header || this.config.header
		options.method = options.method || this.config.method
		options.custom = { ...this.config.custom,
			...(options.custom || {})
		}
		// #ifdef APP-PLUS
		options.sslVerify = options.sslVerify === undefined ? this.config.sslVerify : options.sslVerify
		// #endif
		// uni.showToast({
		// 	icon: "loading",
		// 	image: "/static/imgs//logo/logo.gif"
		// })
		return new Promise((resolve, reject) => {
			let next = true
			let handleRe = {}
			options.complete = (response) => {
				
				// 请求返回后,隐藏loading(如果请求返回快的话,可能会没有loading)
				if (this.config.timerState) {
					uni.hideLoading();
				}
				// 清除定时器,如果请求回来了,就无需loading
				clearTimeout(this.config.timer);
				this.config.timer = null;
				response.config = handleRe
				if (this.validateStatus(response.statusCode)) { // 成功
					response = this.requestComFun(response)
					resolve(response.data)
				} else if (401 === response.statusCode) {
					response = this.requestComFun(response)
					resolve(response.data)
				} else if (500 === response.statusCode) {
					response = this.requestComFun(response)
					resolve(response.data)
				} else {
					response = this.requestComFail(response)
					reject(response)
				}
			}
			const cancel = (t = 'handle cancel', config = options) => {
				const err = {
					errMsg: t,
					config: config
				}
				reject(err)
				next = false
			}

			handleRe = { ...this.requestBeforeFun(options, cancel)
			}
			const _config = { ...handleRe
			}
			if (!next) return
			delete _config.custom
			let mergeUrl = Request.posUrl(_config.url) ? _config.url : (_config.baseUrl + _config.url)
			if (JSON.stringify(_config.params) !== '{}') {
				const paramsH = Request.addQueryString(_config.params);
				mergeUrl += mergeUrl.indexOf('?') === -1 ? `?${paramsH}` : `&${paramsH}`
			}
			_config.url = mergeUrl
			
			// 是否显示loading
			// 加一个是否已有timer定时器的判断,否则有两个同时请求的时候,后者会清除前者的定时器id
			// 而没有清除前者的定时器,导致前者超时,一直显示loading
			
			if( !this.config.timer) {
				
				this.config.timer = setTimeout(() => {
					uni.showLoading({
						title: this.config.loadingText,
						mask: true
					})
					this.config.timerState = true;
					this.config.timer = null;
				}, this.config.loadingTime);
			}
			uni.request(_config)
		})
	}

	get(url, options = {}) {
		return this.request({
			url,
			method: 'GET',
			...options
		})
	}

	post(url, data, options = {}) {
		return this.request({
			url,
			data,
			method: 'POST',
			...options
		})
	}

	upload(url, {
		// #ifdef APP-PLUS
		files,
		// #endif
		// #ifdef MP-ALIPAY
		fileType,
		// #endif
		filePath,
		name,
		header,
		formData,
		custom
	}) {
		return new Promise((resolve, reject) => {
			let next = true
			let handleRe = {}
			const globalHeader = { ...this.config.header
			}
			delete globalHeader['content-type']
			const pubConfig = {
				baseUrl: this.config.baseUrl,
				url,
				// #ifdef APP-PLUS
				files,
				// #endif
				// #ifdef MP-ALIPAY
				fileType,
				// #endif
				filePath,
				method: 'UPLOAD',
				name,
				header: header || globalHeader,
				formData,
				custom: { ...this.config.custom,
					...(custom || {})
				},
				complete: (response) => {
					response.config = handleRe
					if (response.statusCode === 200) { // 成功
						response = this.requestComFun(response)
						resolve(response)
					} else {
						response = this.requestComFail(response)
						reject(response)
					}
				}
			}
			const cancel = (t = 'handle cancel', config = pubConfig) => {
				const err = {
					errMsg: t,
					config: config
				}
				reject(err)
				next = false
			}

			handleRe = { ...this.requestBeforeFun(pubConfig, cancel)
			}
			const _config = { ...handleRe
			}
			if (!next) return
			delete _config.custom
			_config.url = Request.posUrl(_config.url) ? _config.url : (_config.baseUrl + _config.url)
			uni.uploadFile(_config)
		})
	}
}

/**
 * setConfig回调
 * @return {Object} - 返回操作后的config
 * @callback Request~setConfigCallback
 * @param {Object} config - 全局默认config
 */
/**
 * 请求拦截器回调
 * @return {Object} - 返回操作后的config
 * @callback Request~requestCallback
 * @param {Object} config - 全局config
 * @param {Function} [cancel] - 取消请求钩子,调用会取消本次请求
 */
/**
 * 响应拦截器回调
 * @return {Object} - 返回操作后的response
 * @callback Request~responseCallback
 * @param {Object} response - 请求结果 response
 */
/**
 * 响应错误拦截器回调
 * @return {Object} - 返回操作后的response
 * @callback Request~responseErrCallback
 * @param {Object} response - 请求结果 response
 */

建立store文件夹,位置与common同级。目录如下:在这里插入图片描述
store是全局内容文件,store里面的东西是用来管理全局参数和全局方法的,store目前放了一个服务器地址,其他的参数都是预留的
index.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

const store = new Vuex.Store({
	state: {
		
		serverUrl: 'http://localhost:3000/', //服务器地址
		systemInfo:null,
		authorizeState: true, //是否授权
		config:null,
		token:null,
		userInfo:null,
		showLoginTip: false,
	},
	mutations: {
		LOGIN_TIP(state, data) {
			state.showLoginTip = data
		},
	},
	actions: {
		
	}
})

export default store
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      

main.js中:

在这里插入图片描述

最后是页面中:

methods: {
			login(){
				let uid = this.uid;
				let password = this.password;
				this.$api("user.login",{uid,password}).then(res=>{
					console.log(res);
					if(res.code==1){
						console.log("登录成功")
					}
				})
			}
		}

这样也实现了模块化管理

  • uni-APP配置的,在外部浏览器会跨域
    在这里插入图片描述

所以运行到h5项目要用内置浏览器在这里插入图片描述

  • 8
    点赞
  • 134
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值