uni-app自带uni.request用于网络请求,因为我们需要自定义拦截器等功能,也是为了和我们后台管理保持统一,这里我们使用比较流行且功能更强大的axios来实现网络请求。
Axios 是一个基于 promise 网络请求库,作用于node.js
和浏览器中。 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生 node.js http
模块, 而在客户端 (浏览端) 则使用 XMLHttpRequests。
HBuilderX有针对于uni-app的通用配置,很多通用常量可以直接配置在manifest.json文件中,且HBuilderX提供图形化配置界面。但我们有许多业务系统相关的常量配置,那么就需要一个自定义常量配置文件。
uni-app中定义全局常量有多种方式,在vue.js框架中也可以使用App.vue里面的globalData,根据业务需求,自定义常量可能会很多,不便于和官方配置融合在一起,所以这里使用新增配置project.config.js文件并挂载Vue.prototype的方式来实现常量配置。
module.exports = {
# 配置请求后台地址
APP_API_BASE_URL: 'http://127.0.0.1:8080',
# 多租户项目,这里是默认的租户id
APP_TENANT_ID: '0',
# OAuth2授权的用户名密码
APP_CLIENT_ID: 'gitegg-admin',
# client_id:client_secret加密后的值,直接传,不需要再进行BASE64加密
APP_CLIENT_SECRET: 'Z2l0ZWdnLWFkbWluOjEyMzQ1Ng=='
}
// 导入js文件
import ProjectConfig from './project.config'
// 挂载
Vue.prototype.$ProjectConfig = ProjectConfig
this.$ProjectConfig.APP_API_BASE_URL
import ProjectConfig from './project.config'
HBuilderX默认没有开启终端命令窗口,选中项目,有两种方式打开命令窗口:
yarn add axios
yarn add axios-auth-refresh
const VueAxios = {
vm: {},
// eslint-disable-next-line no-unused-vars
install (Vue, instance) {
if (this.installed) {
return
}
this.installed = true
if (!instance) {
// eslint-disable-next-line no-console
console.error('You have to install axios')
return
}
Vue.axios = instance
Object.defineProperties(Vue.prototype, {
axios: {
get: function get () {
return instance
}
},
$http: {
get: function get () {
return instance
}
}
})
}
}
export {
VueAxios
}
在这里定义拦截器主要用于:自动设置token、token过期刷新、统一异常提示、返回数据处理等功能。
import axios from 'axios'
import createAuthRefreshInterceptor from 'axios-auth-refresh'
import store from '@/store'
import { serialize } from '@/common/util'
import { VueAxios } from './axios'
import { ACCESS_TOKEN, REFRESH_ACCESS_TOKEN } from '@/store/mutation-types'
// 导入js文件
import ProjectConfig from '@/project.config.js'
// uni-app适配
axios.defaults.adapter = function(config) {
return new Promise((resolve, reject) => {
var settle = require('axios/lib/core/settle');
var buildURL = require('axios/lib/helpers/buildURL');
uni.request({
method: config.method.toUpperCase(),
url: config.baseURL + buildURL(config.url, config.params, config.paramsSerializer),
header: config.headers,
data: config.data,
dataType: config.dataType,
responseType: config.responseType,
sslVerify: config.sslVerify,
complete: function complete(response) {
response = {
data: response.data,
status: response.statusCode,
errMsg: response.errMsg,
header: response.header,
config: config
};
settle(resolve, reject, response);
}
})
})
}
// 创建 axios 实例
const request = axios.create({
// API 请求的默认前缀
baseURL: ProjectConfig.APP_API_BASE_URL,
timeout: 30000 // 请求超时时间
})
// 当token失效时,需要调用的刷新token的方法
const refreshAuthLogic = failedRequest =>
axios.post(ProjectConfig.APP_API_BASE_URL + '/oauth/token',
serialize({
grant_type: 'refresh_token',
refresh_token: uni.getStorageSync(REFRESH_ACCESS_TOKEN)
}),
{
headers: { 'TenantId': ProjectConfig.APP_TENANT_ID, 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': 'Basic ' + ProjectConfig.APP_CLIENT_SECRET },
skipAuthRefresh: true // 刷新token请求过期,不再进行刷新
}
).then(tokenRefreshResponse => {
if (tokenRefreshResponse.status === 200 && tokenRefreshResponse.data && tokenRefreshResponse.data.success) {
const result = tokenRefreshResponse.data.data
uni.setStorageSync(ACCESS_TOKEN, result.tokenHead + result.token, result.expiresIn * 1000)
uni.setStorageSync(REFRESH_ACCESS_TOKEN, result.refreshToken, result.refreshExpiresIn * 1000)
failedRequest.response.config.headers['Authorization'] = result.tokenHead + result.token
} else if (tokenRefreshResponse.status === 200 && tokenRefreshResponse.data &&
!tokenRefreshResponse.data.success && tokenRefreshResponse.data.code === 401) {
store.dispatch('Timeout').then(async () => {
uni.navigateTo({
url: '/pages/login/login'
})
})
}
return Promise.resolve()
})
// 初始化刷新token拦截器
createAuthRefreshInterceptor(request, refreshAuthLogic, {
pauseInstanceWhileRefreshing: true // 当刷新token执行时,暂停其他请求
})
// 异常拦截处理器
const errorHandler = (error) => {
if (error.response) {
const data = error.response.data
if (error.response.status === 403) {
uni.showToast({
title: '您没有权限访问此接口',
icon:'error',
duration: 2000
});
} else if (error.response.status === 401 && !(data.result && data.result.isLogin)) {
// 当刷新token超时,则调到登录页面
uni.showModal({
title: '登录超时',
content: '由于您长时间未操作, 为确保安全, 请重新登录系统进行后续操作 !',
confirmText: '重新登录',
showCancel: false,
success: (res) => {
if(res.confirm) {
store.dispatch('Timeout').then(() => {
uni.navigateTo({
url: '/pages/login/login'
})
})
}
}
})
}
}
return Promise.reject(error)
}
// request interceptor
request.interceptors.request.use(config => {
const token = uni.getStorageSync(ACCESS_TOKEN)
// 如果 token 存在
// 让每个请求携带自定义 token 请根据实际情况自行修改
if (token && config.authenticationScheme !== 'Basic') {
config.headers['Authorization'] = token
}
config.headers['TenantId'] = ProjectConfig.APP_TENANT_ID
return config
}, errorHandler)
// response interceptor
request.interceptors.response.use((response) => {
const res = response.data
if (res && res.code) {
if (res.code !== 200) {
uni.showToast({
title: '操作失败: ' + res.msg,
icon:'error',
duration: 2000
});
return Promise.reject(res || 'Error')
} else {
return response.data
}
} else {
return response
}
}, errorHandler)
const installer = {
vm: {},
install (Vue) {
Vue.use(VueAxios, request)
}
}
export default request
export {
installer as VueAxios,
request as axios
}
import request from '@/common/utils/request'
import ProjectConfig from '@/project.config.js'
const loginApi = {
// 登录
Login: '/oauth/token',
// 退出登录
Logout: '/oauth/logout',
// 获取系统配置的验证码类型
CaptchaType: '/oauth/captcha/type',
// 获取图片验证码
ImageCaptcha: '/oauth/captcha/image',
// 发送短信验证码
SendSms: '/oauth/sms/captcha/send',
// 获取用户信息
UserInfo: '/system/account/user/info',
// 第三方登录
SocialLoginUrl: '/oauth/social/login/',
// 第三方登录回调
SocialLoginCallback: '/oauth/social/',
// 第三方用户绑定---通过手机号验证码绑定
SocialBindMobile: '/oauth/social/bind/mobile',
// 第三方用户绑定---通过账号密码绑定
SocialBindAccount: '/oauth/social/bind/account',
// 发送短信验证码
SmsSend: '/extension/sms/code/send',
// 校验短信验证码
SmsCheckPre: '/extension/sms/check/code/pre',
// 校验短信验证码
SmsCheck: '/extension/sms/check/code',
// 发送注册短信
SmsRegisterSend: '/system/account/register/sms/send',
// 账户注册
Register: '/system/account/register',
// 校验用户是否存在
CheckUserExist: '/system/account/register/check'
}
export default loginApi
/**
* OAuth2登录
* @param parameter
* @returns {*}
*/
export function login (parameter) {
return request({
url: loginApi.Login,
authenticationScheme: 'Basic',
method: 'post',
headers: { 'Authorization': 'Basic ' + ProjectConfig.APP_CLIENT_SECRET },
skipAuthRefresh: true,
data: parameter
})
}
/**
* OAuth2退出登录
* @param parameter
* @returns {*}
*/
export function logout (parameter) {
return request({
url: loginApi.Logout,
method: 'post',
skipAuthRefresh: true,
data: parameter
})
}
/**
* 获取验证码类型
* @param parameter
* @returns {*}
*/
export function getCaptchaType () {
return request({
url: loginApi.CaptchaType,
method: 'get'
})
}
/**
* 获取图片验证码
* @param parameter
* @returns {*}
*/
export function getImageCaptcha () {
return request({
url: loginApi.ImageCaptcha,
method: 'get'
})
}
/**
* 获取短信验证码
* @param parameter
* @returns {*}
*/
export function getSmsCaptcha (parameter) {
return request({
url: loginApi.SendSms,
method: 'post',
data: parameter
})
}
/**
* 获取用户信息
* @param parameter
* @returns {*}
*/
export function getInfo () {
return request({
url: loginApi.UserInfo,
method: 'get'
})
}
/**
* 获取第三方登录的URL
* @param {Object} socialType
*/
export function getSocialLoginUrl (socialType) {
return request({
url: loginApi.SocialLoginUrl + socialType,
method: 'get'
})
}
/**
* 第三方登录回调地址
* @param {Object} socialType
* @param {Object} parameter
*/
export function socialLoginCallback (socialType, parameter) {
return request({
url: loginApi.SocialLoginCallback + socialType + '/callback',
method: 'get',
params: parameter
})
}
/**
* 发送短信验证码
* @param {Object} parameter
*/
export function sendSmsCode (parameter) {
return request({
url: loginApi.SmsSend,
method: 'post',
data: parameter
})
}
/**
* 校验短信验证码
* @param {Object} parameter
*/
export function checkSmsCode (parameter) {
return request({
url: loginApi.SmsCheckPre,
method: 'get',
params: parameter
})
}
/**
* 发送注册短信验证码
* @param {Object} parameter
*/
export function smsRegisterSend (parameter) {
return request({
url: loginApi.SmsRegisterSend,
method: 'post',
data: parameter
})
}
/**
* 校验用户是否存在
* @param {Object} parameter
*/
export function checkUserExist (parameter) {
return request({
url: loginApi.CheckUserExist,
method: 'post',
data: parameter
})
}
/**
* 用户注册
* @param {Object} parameter
*/
export function userRegister (parameter) {
return request({
url: loginApi.Register,
method: 'post',
data: parameter
})
}
/**
* 第三方用户绑定---通过手机号验证码绑定
* @param {Object} parameter
*/
export function userBindMobile (parameter) {
return request({
url: loginApi.SocialBindMobile,
method: 'post',
data: parameter
})
}
/**
* 第三方用户绑定---通过账号密码绑定
* @param {Object} parameter
*/
export function userBindAccount (parameter) {
return request({
url: loginApi.SocialBindAccount,
method: 'post',
data: parameter
})
}
3、在/pages目录下创建login目录,新增login.vue登录页面,用于登录。
—————— 其他登录方式 ——————
const token = uni.getStorageSync(ACCESS_TOKEN)
if(!token || token === ''){
uni.navigateTo({
url: '/pages/login/login'
})
} else {
console.log('已登录');
}
上文中介绍了如果配置HBuilderX连接手机模拟器,预览并调试uni-app项目,这里我们通过以上配置和编写,实现了登录界面,现在我们可以在手机模拟器中查看刚刚写的登录页面了。
Gitee: https://gitee.com/wmz1930/GitEgg
GitHub: https://github.com/wmz1930/GitEgg
欢迎感兴趣的小伙伴Star支持一下。