快捷搜索:  汽车  科技

springcloud微服务可行性分析(7K字详解SpringCloud微服务实战)

springcloud微服务可行性分析(7K字详解SpringCloud微服务实战)yarn add axios yarn add axios-auth-refresh 4、在目录/common/utils新建axios.js,创建Axios 实例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 }

uni-app自带uni.request用于网络请求,因为我们需要自定义拦截器等功能,也是为了和我们后台管理保持统一,这里我们使用比较流行且功能更强大的axios来实现网络请求。

  Axios 是一个基于 promise 网络请求库,作用于node.js 和浏览器中。 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生 node.js http 模块 而在客户端 (浏览端) 则使用 XMLHttpRequests。

Axios特性:
  • 从浏览器创建 XMLHttpRequests
  • 从 node.js 创建 http 请求
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求和响应数据
  • 取消请求
  • 自动转换JSON数据
  • 客户端支持防御XSRF
一、安装axios和axios-auth-refresh组件1、新增uni-app自定义常量配置文件

  HBuilderX有针对于uni-app的通用配置,很多通用常量可以直接配置在manifest.json文件中,且HBuilderX提供图形化配置界面。但我们有许多业务系统相关的常量配置,那么就需要一个自定义常量配置文件。
  uni-app中定义全局常量有多种方式,在vue.js框架中也可以使用App.vue里面的globalData,根据业务需求,自定义常量可能会很多,不便于和官方配置融合在一起,所以这里使用新增配置project.config.js文件并挂载Vue.prototype的方式来实现常量配置。

  • 在工程的根目录下新增project.config.js

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==' }

  • 在main.js中导入、挂载project.config.js

// 导入js文件 import ProjectConfig from './project.config' // 挂载 Vue.prototype.$ProjectConfig = ProjectConfig

  • 在项目中引用

this.$ProjectConfig.APP_API_BASE_URL

  • 如果是在APP挂在前引用,那么使用以下方法引用

import ProjectConfig from './project.config' 2、打开HBuilderX终端命令窗口,用于执行yarn安装命令

HBuilderX默认没有开启终端命令窗口,选中项目,有两种方式打开命令窗口:

  • 按快捷键Ctrl Alt T打开终端窗口
  • 菜单栏中,选择 视图 > 显示终端(C)
3、执行安装axios(http请求拦截)和axios-auth-refresh(强大的token刷新)组件命令

yarn add axios yarn add axios-auth-refresh 4、在目录/common/utils新建axios.js,创建Axios 实例

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 } 5、在目录/common/utils新建request.js,自定义Axios拦截器

在这里定义拦截器主要用于:自动设置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 } 二、请求后台接口并实现登录功能1、新建api目录,用于存放所有后台请求的接口,在api目录下新建login目录,存放用于登录的相关接口2、在/api/login目录下新增login.js

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登录页面,用于登录。

<!-- 蓝色简洁登录页面 --> <template> <view class="login-bg"> <br /><br /><br /><br /><br /><br /><br /> <view class="t-login"> <form class="cl"> <view class="t-a"> <image src="@/static/login/user.png"></image> <input type="text" name="username" placeholder="请输入手机号码" maxlength="11" v-model="username" /> </view> <view class="t-a"> <image src="@/static/login/pwd.png"></image> <input type="password" name="password" maxlength="100" placeholder="请输入密码" v-model="password" /> </view> <button @tap="login()">登 录</button> <view class="t-c"> <text class="t-c-txt" @tap="reg()">注册账号</text> <text @tap="forgotPwd()">忘记密码</text> </view> </form> <view class="t-f"><text>—————— 其他登录方式 ——————</text></view> <view class="t-e cl"> <view class="t-g" @tap="wxLogin()"><image src="@/static/login/wx2.png"></image></view> <view class="t-g" @tap="zfbLogin()"><image src="@/static/login/qq2.png"></image></view> <view class="t-g" @tap="zfbLogin()"><image src="@/static/login/wb.png"></image></view> </view> </view> <image class="img-a" src="@/static/login/bg1.png"></image> </view> </template> <script> import md5 from '@/common/md5.min.js'; import { mapActions } from 'vuex' import { ACCESS_TOKEN REFRESH_ACCESS_TOKEN } from '@/store/mutation-types' export default { data() { return { username: '' password: '' grant_type: 'password' }; } onLoad() {} methods: { ...mapActions(['Login' 'Logout']) login() { var that = this; if (!that.username) { uni.showToast({ title: '请输入手机号' icon: 'none' }); return; } if (!/^[1][3 4 5 7 8 9][0-9]{9}$/.test(that.username)) { uni.showToast({ title: '请输入正确手机号' icon: 'none' }); return; } if (!that.password) { uni.showToast({ title: '请输入密码' icon: 'none' }); return; } const loginParams = {} loginParams.username = that.username loginParams.grant_type = 'password' loginParams.password = md5(that.password) that.Login(loginParams) .then((res) => this.loginSuccess(res)) .catch(err => this.requestFailed(err)) .finally(() => { }) } loginSuccess (res) { // 判断是否记住密码 uni.showToast({ title: '登录成功!' icon: 'none' }); uni.switchTab({ url: '/pages/tabBar/component/component' fail(err) { console.log(err) } }) } requestFailed (res) { // 判断是否记住密码 uni.showToast({ title: '登录失败:' res.msg icon: 'none' }); } //忘记密码 forgotPwd() { uni.showToast({ title: '忘记密码' icon: 'none' }); } //立刻注册 reg() { uni.showToast({ title: '注册账号' icon: 'none' }); } } }; </script> <style> .img-a { width: 100%; position: absolute; bottom: 0; } .login-bg { height: 100vh; background-image: url(/static/login/bg3.png); } .t-login { width: 580rpx; padding: 55rpx; margin: 0 auto; font-size: 28rpx; background-color: #ffffff; border-radius: 20rpx; box-shadow: 0 5px 7px 0 rgba(0 0 0 0.15); z-index: 9; } .t-login button { font-size: 28rpx; background: linear-gradient(to right #ff8f77 #fe519f); color: #fff; height: 90rpx; line-height: 90rpx; border-radius: 50rpx; } .t-login input { padding: 0 20rpx 0 120rpx; height: 90rpx; line-height: 90rpx; margin-bottom: 50rpx; background: #f6f6f6; border: 1px solid #f6f6f6; font-size: 28rpx; border-radius: 50rpx; } .t-login .t-a { position: relative; } .t-login .t-a image { width: 40rpx; height: 40rpx; position: absolute; left: 40rpx; top: 28rpx; padding-right: 20rpx; } .t-login .t-b { text-align: left; font-size: 46rpx; color: #000; padding: 300rpx 0 120rpx 0; font-weight: bold; } .t-login .t-d { text-align: center; color: #999; margin: 80rpx 0; } .t-login .t-c { text-align: right; color: #666666; margin: 30rpx 30rpx 40rpx 0; } .t-login .t-c .t-c-txt { margin-right: 300rpx; } .t-login .t-e { text-align: center; width: 600rpx; margin: 40rpx auto 0; } .t-login .t-g { float: left; width: 33.33%; } .t-login .t-e image { width: 70rpx; height: 70rpx; } .t-login .t-f { text-align: center; margin: 80rpx 0 0 0; color: #999; } .t-login .t-f text { margin-left: 20rpx; color: #b9b9b9; font-size: 27rpx; } .t-login .uni-input-placeholder { color: #aeaeae; } .cl { zoom: 1; } .cl:after { clear: both; display: block; visibility: hidden; height: 0; content: '\20'; } </style> 4、将页面中用到的图片,复制到/static/login目录下5、配置pages.json文件,将新增的login.vue文件目录加入到配置中。pages.json类似于vue.js工程下的路由页面配置6、在App.vue文件的onLaunch方法中新增判断,当token为空时,跳转到我们刚刚新建的登录界面。

const token = uni.getStorageSync(ACCESS_TOKEN) if(!token || token === ''){ uni.navigateTo({ url: '/pages/login/login' }) } else { console.log('已登录'); } 三、在手机模拟器中运行并预览登录界面

  上文中介绍了如果配置HBuilderX连接手机模拟器,预览并调试uni-app项目,这里我们通过以上配置和编写,实现了登录界面,现在我们可以在手机模拟器中查看刚刚写的登录页面了。

1、启动手机模拟器 > 双击桌面的nox_adb快捷方式2、在HBuilder X中依次点击 运行 -> 运行到手机或模拟器 -> 运行到Android App基座

springcloud微服务可行性分析(7K字详解SpringCloud微服务实战)(1)

3、弹出框会显示我们已连接的模拟器,点击运行,HBuilderX就可以自动打包app发布到模拟器中运行,并可以在HBuilderX控制台查看运行日志。

springcloud微服务可行性分析(7K字详解SpringCloud微服务实战)(2)

4、在手机模拟器展示的登录界面中,输入我们系统用户的手机号码 密码,登录成功后即可跳转到登录后的界面。

springcloud微服务可行性分析(7K字详解SpringCloud微服务实战)(3)

springcloud微服务可行性分析(7K字详解SpringCloud微服务实战)(4)

猜您喜欢: