深入解析:ArkUI与Axios构建安全高效的登录注册流程 原创

在敲键盘的小鱼干很饥饿
发布于 2024-12-24 16:28
浏览
3收藏

引言

在现代的移动应用中,登录和注册必不可少,而鸿蒙又是现在的发展趋势,然而在网上却很难找到,一个完整的ArkUI实现的登录注册案例,并且是实现了功能的。本文将介绍一个使用ArkUI框架实现的简单而高效的登录和注册界面案例,并详细解析其代码绘制界面的过程以及功能实现的细节,通过过这个案例,读者可以了解到如何在ArkUI中使用HTTP请求和Axios库与后端服务器进行交互,以及如何管理组件状态以实现动态界面更新。该案例不仅实现了基本的登录和注册功能,还解决了验证码倒计时动态错误提示等常见痛点。

目标读者

本文适合所有对ArkUI框架感兴趣,希望了解如何实现登录和注册功能的开发者。无论你是初学者还是有经验的开发者,都可以通过本文获得对ArkUI框架的更深入理解,并学习如何处理HTTP请求和状态管理。

文章结构

文章将按照以下结构进行:

  1. 技术栈介绍:简述ArkUI、HTTP请求和Axios库的基本概念和技术优势。
  2. 代码结构与界面设计:详细说明代码的整体结构和每个部分的界面设计思路。
  3. 功能实现:深入解析发送验证码和登录/注册功能的具体实现细节。
  4. Axios与请求拦截器:了解到如何在项目中进行基础的HTTP请求配置,并确保在用户未登录的情况下,阻止对需要认证的API的访问,并引导用户到登录注册页面。

在接下来的内容中,我们将逐一探讨这些部分,帮助你更好地理解和实现这个登录和注册案例。现在,让我们从技术栈介绍开始吧。


深入解析:ArkUI与Axios构建安全高效的登录注册流程-鸿蒙开发者社区


技术栈介绍

ArkUI

ArkUI 是一个由华为开发的高性能、高可靠性的跨平台UI开发框架,专为全场景生态设计。它旨在提供统一的开发体验,支持多种设备,包括智能手机、平板、智能穿戴设备等。ArkUI采用声明式编程模型,使得开发者可以通过简洁的代码来描述UI界面,从而提高开发效率和代码可维护性。
主要特点

  • 声明式编程模型:通过描述UI的状态和行为来实现界面更新,简化代码逻辑。
  • 高性能:优化的渲染引擎和高效的组件复用机制,确保应用的流畅运行。
  • 跨平台支持:支持多种设备和操作系统,减少重复开发工作。
  • 丰富的组件库:提供大量的内置组件,满足各种复杂的UI需求。

HTTP请求

HTTP请求 是一种通过网络协议(如HTTP或HTTPS)与服务器进行通信的方式。在移动应用开发中,HTTP请求常用于获取和发送数据,实现用户登录、注册等功能。ArkUI框架提供了@ohos.net.http模块来处理HTTP请求。
主要特点

  • 简单易用:提供简洁的API接口,方便开发者进行HTTP请求。
  • 支持多种方法:包括GET、POST、PUT、DELETE等HTTP方法。
  • 支持自定义请求头和数据:可以灵活地设置请求头和请求数据,满足不同的业务需求。
  • 错误处理:提供详细的错误信息,帮助开发者调试和处理请求失败的情况。

Axios

Axios 是一个基于Promise的HTTP客户端,可以用于浏览器和Node.js中。它在移动端应用开发中常用于与后端服务器进行数据交互。Axios提供了丰富的功能和灵活的配置选项,使得HTTP请求更加简单和高效。
主要特点

  • 基于Promise:支持异步操作,便于处理请求的成功和失败情况。
  • 拦截器:可以拦截请求和响应,方便进行预处理和后处理。
  • 转换请求和响应数据:支持对请求和响应数据进行自动转换,简化数据处理过程。
  • 取消请求:允许取消正在进行的请求,避免无用的数据传输。
  • 自动转换JSON数据:默认情况下,自动将请求和响应的数据格式化为JSON,方便使用。

好了,由于笔者了解也有限,可能也并不太深入,大家有兴趣的可以自行去学习,技术就介绍到这里。
接下来,我们将详细解析代码的整体结构和界面设计思路。


代码结构与界面设计

在本部分中,我们将详细说明代码的整体结构和每个部分的界面设计思路。通过这些说明,你可以更好地理解如何在ArkUI中构建复杂的UI界面,并实现与后端服务器的交互。

整体结构

首先,我们来看一下整个代码的结构:

@Entry
@Component
struct LoginRegister {
  @State phone: string = ''
  @State code: string = ''
  @State errorMessage: string = ''
  @State isAgree: boolean = false

  @State countdown: number = 60
  @State isCounting: boolean = false
  @State loginData: LoginData | null = null;
  private interval: number | null = null; // 存储 interval 的引用

  // 发送验证码的API调用
  sendVerificationCode(phone: string) { ... }

  // 登录或注册的API调用
  loginOrRegister(phone: string, code: string) { ... }

  startCountdown() : void { ... }

  build() { ... }

  @Builder
  logoComponent() { ... }
}

主要部分

  • 状态定义:使用@State装饰器定义组件的状态变量,如手机号、验证码、错误信息、协议同意状态等。
  • 方法定义:定义了几个关键方法,包括发送验证码、登录或注册以及启动倒计时。
  • 界面构建:通过build方法构建整个登录和注册界面。
  • 自定义组件:使用@Builder装饰器定义了一个可复用的logoComponent。

界面设计

我们挑一些主要的实现,来给大家介绍一下:

手机或验证码的输入框

Row() {
  IconColumnView({imagePath: "app.media.ic_phone"})

  Column() {
    TextInput({
      placeholder: '请输入手机号',
      text: this.phone,
    })
      .placeholderColor('rgba(0, 0, 0, 0.6)')
      .fontColor(Color.Black)
      .width('100%')
      .borderRadius(15)
      .backgroundColor('#EDEDED')
      .caretColor('#000000')
      .height(39)
      .onChange((value: string) => {
        this.phone = value;
        if (this.phone === '' || this.phone.length !== 11 || !/^\d+$/.test(this.phone)) {
          // 检查手机号是否为空,或者长度是否不等于11位,并且是否只包含数字
          if (this.phone === '') {
            this.errorMessage = ''; // 如果输入为空,清空错误信息
          } else {
            this.errorMessage = '请输入有效的手机号'; // 如果输入不符合条件,设置错误信息
          }
        } else {
          this.errorMessage = ''; // 如果符合条件,清空错误信息
        }
      })
  }
  .width('85.8%')
  .backgroundColor('#EDEDED')
  .borderRadius({
    topLeft: 0,
    topRight: 15,
    bottomLeft: 0,
    bottomRight: 15
  })
}
.width('91%')
.borderRadius(15)
.backgroundColor('#EDEDED')
.margin({ top: 45 })

感觉笔者实现的可能有点复杂了,需求要在输入框的前面显示一个图标,用来表示这是手机或验证码的输入框,而我的实现方式是将两列叠在一起,有什么组件或者好的实现方式可以告诉我。
深入解析:ArkUI与Axios构建安全高效的登录注册流程-鸿蒙开发者社区
这里也没什么可讲的,在实现功能时我们还是要使用onChange事件:当用户输入手机号时,检查输入格式是否正确。如果不正确,则显示错误信息。
下面就是手机号和验证码的实现完成的图示
深入解析:ArkUI与Axios构建安全高效的登录注册流程-鸿蒙开发者社区

登录按钮

登录按钮允许用户使用手机号和验证码进行登录或注册。

Row() {
  Button('登录', { type: ButtonType.Normal })
    .width('67.7%')
    .height(39)
    .backgroundColor($r('sys.color.black'))
    .fontColor('#BEFF33')
    .fontSize(20)
    .borderRadius(15)
    .onClick(() => {
      if (this.phone.length === 11 && this.code.length > 0) {
        this.loginOrRegister(this.phone, this.code);
      }
    })
}
.margin({ top: 28 })
.justifyContent(FlexAlign.Center)
.width('100%')

主要也没什么可讲的主要就是调整样式和onClick事件,当用户点击按钮时,检查手机号和验证码是否正确。如果正确,则调用loginOrRegister方法。
深入解析:ArkUI与Axios构建安全高效的登录注册流程-鸿蒙开发者社区

错误提示信息

一个完整的登录注册不能只有输入框,登录按钮就行了,还需要错误提示信息用于显示用户输入错误或请求失败的信息。

Row() {
  if (this.errorMessage === '') {
    Text('')
  }
  else {
    Text(this.errorMessage)
      .fontSize(14)
      .fontColor('#F50000')
  }
}
.width('91%')

当然这些错误提示信息需要你在其他的代码逻辑中给出,实现。后面我的介绍中也会提到。

协议和隐私政策

一个好的应用,这一点是必不可少的,我在这部分将这些封装到一个小view中以方便后面其他界面的复用,嗯感觉也有点复杂,怕登录页面代码太多就把这些放在view中了,有没有只用一个Text就可以放完这些文字的组件。求指导。深入解析:ArkUI与Axios构建安全高效的登录注册流程-鸿蒙开发者社区

import { router } from '@kit.ArkUI'
@Component
export struct agreementView {
  @Link isAgree: boolean
  build() {
    Row() {
      Checkbox()
        .width(17)
        .height(15)
        .shape(CheckBoxShape.ROUNDED_SQUARE)
        .select(this.isAgree)
        .mark({ strokeColor: '#BEFF33' })
        .selectedColor($r('sys.color.black'))
        .borderRadius(6)
        .onClick(() => {
          this.isAgree = !this.isAgree
        })

      Text('我已阅读并同意')
        .fontColor($r('app.color.menu_font_color'))
        .fontSize(14)
        .lineHeight(20.27)
      Text('用户协议')
        .fontSize(14)
        .lineHeight(20.27)
        .onClick(() => {
          router.pushUrl({
            url: 'pages/UserAgreementPage' // 目标页面的 URL
          });
        })
      Text('和')
        .fontSize(14)
        .lineHeight(20.27)
        .fontColor($r('app.color.menu_font_color'))
      Text('隐私政策')
        .fontSize(14)
        .lineHeight(20.27)
        .onClick(() => {
          router.pushUrl({
            url: 'pages/PrivacyPolicyPage' // 目标页面的 URL
          });
        })
    }
    .width('100%')
    .height(21)
    .alignItems(VerticalAlign.Center)
    .justifyContent(FlexAlign.Center)
    .margin({ top: 26 })
  }
}

使用

          //协议和隐私政策
          agreementView({isAgree :this.isAgree})

样式调整

为了让整个登录注册更美观一点,当然这是我们产品的功劳。下面给出我案例的登录注册部分。我讲一下这个底层绿色的怎么实现,界面设计就讲到这,其他都是一些简单的布局和组件的使用。
深入解析:ArkUI与Axios构建安全高效的登录注册流程-鸿蒙开发者社区
代码实现

            //底层遮罩层(绿色)
            Column() {
            }
            .width('74.4%')
            .height(276)
            .backgroundColor('rgba(173, 212, 91, 0.8)')
            .borderRadius(6)
            .position({
              x: 66,
              y: 28
            })
            .zIndex(0)

我采用了绝对定位+zindex设置层级的方式实现的,当然也可以使用,stack层叠布局来实现。Position类型基于父组件左上角确定位置,zindex(0)设置层级最低。即可实现。我感觉加了一个这个真的挺好看的,产品的眼光真好。


通过以上对代码结构和界面设计的详细说明,你可以了解到如何在ArkUI中构建简单的登录注册UI界面,接下来,我们将深入解析发送验证码和登录/注册功能的具体实现细节。


功能实现

接下来就进入主要的部分,功能实现环节,可能在本部分中,我们将深入解析发送验证码和登录/注册功能的具体实现细节。通过这些解析,你可以了解到如何在ArkUI中使用HTTP请求和Axios库与后端服务器进行交互,并如何管理组件状态以实现动态界面更新。

发送验证码功能

发送验证码功能通过sendVerificationCode方法实现。该方法首先检查用户是否同意了协议和隐私政策,然后通过HTTP请求发送验证码到指定手机号。

//发送验证码的api调用
sendVerificationCode(phone: string) {
  if (this.isAgree === false) {
    this.errorMessage = '请先同意协议和隐私政策';
    return;
  } else {
    this.errorMessage = '';
  }
  let httpRequest = http.createHttp();
  httpRequest.request(
    '你的api地址',
    {
      method: http.RequestMethod.POST,
      header: { 'Content-Type': 'application/json' },
      extraData: {
        phone: phone,
      }
    }, (err, data) => {
    if (!err) {
      console.log('验证码发送成功');
      this.startCountdown();
    } else {
      console.error('验证码发送失败', err);
      this.errorMessage = '验证码发送失败,请重试';
    }
  });
}

详细步骤

  1. 检查协议同意状态
    使用this.isAgree检查用户是否同意了协议和隐私政策。并更新错误信息
  2. 创建创建HTTP请求:
    使用http.createHttp()创建一个HTTP请求实例。
  3. 发送POST请求
    使用httpRequest.request方法发送POST请求到指定的URL,设置请求头为{‘Content-Type’: ‘application/json’},设置请求数据为{ phone: phone }。
  4. 处理响应
    如果请求成功 (!err),则在控制台输出“验证码发送成功”,并调用this.startCountdown()启动倒计时,如果请求失败 (err),则在控制台输出错误信息,并设置this.errorMessage为“验证码发送失败,请重试”。

登录注册功能

登录或注册功能通过loginOrRegister方法实现。该方法使用Axios库发送POST请求到指定的URL,并处理请求的成功和失败情况。

//登录注册的api调用
loginOrRegister(phone: string, code: string) {
  console.log('尝试登录或注册的手机号和验证码:', phone, code); // 添加测试日志

  axios.post<LoginData>('你的api地址', {
    phone: phone,
    code: code,
  })
    .then((response: AxiosResponse<LoginData>) => {
      console.log('响应数据:', response.data); // 添加测试日志
      const parsedData: LoginData = response.data; // 使用 LoginData 接口明确类型
      console.log('解析后的数据:', parsedData); // 添加测试日志
      if (parsedData && parsedData.flag) {
        console.log('登录或注册成功,设置登录数据并跳转'); // 添加测试日志
        this.loginData = parsedData;
        dataPreferences?.putSync("token", parsedData.data);
        dataPreferences?.flush();
        console.log('准备跳转到 /pages/Tabs'); // 添加测试日志
        router.pushUrl({
          url: 'pages/Tabs' // 目标页面的 URI
        });
        console.log('跳转完成后'); // 添加测试日志
      } else {
        console.error('登录或注册失败', parsedData.msg); // 添加测试日志
        if (parsedData.msg && parsedData.msg.includes('验证码错误')) {
          this.errorMessage = '验证码错误,请重试';
        } else {
          this.errorMessage = '登录或注册失败,请重试';
        }
      }
    })
    .catch((error: AxiosError) => { // 使用 AxiosError 明确类型
      console.error('登录或注册失败', error); // 添加测试日志
      console.error('错误信息:', error.message); // 添加测试日志
      console.error('错误响应:', error.response); // 添加测试日志
      console.error('错误配置:', error.config); // 添加测试日志
      console.error('错误状态码:', error.response?.status); // 添加测试日志
      console.error('错误状态文本:', error.response?.statusText); // 添加测试日志
      this.errorMessage = `登录或注册失败,请重试: ${error.message}`;
    });
}

详细步骤

  1. 发送POST请求
    使用axios.post方法发送POST请求到指定的URL,设置请求数据为{ phone: phone, code: code }。
  2. 处理成功响应
    在then回调中,使用console.log输出响应数据。解析响应数据为LoginData类型。如果parsedData.flag为true,表示登录或注册成功。设置this.loginData为解析后的数据。使用dataPreferences.putSync保存token,并调用dataPreferences.flush刷新数据。使用router.pushUrl跳转到目标页面 (/pages/Tabs)。
    如果parsedData.flag为false,表示登录或注册失败。使用console.error输出错误信息。
    根据错误信息的具体内容,设置this.errorMessage为相应的提示信息。
  3. 处理失败响应
    在catch回调中,使用console.error输出详细的错误信息,包括错误信息、响应数据、配置信息、状态码和状态文本。设置this.errorMessage为“登录或注册失败,请重试”,并附带具体的错误信息。

倒计时功能

倒计时功能通过startCountdown方法实现。该方法启动一个定时器,每秒更新一次验证码按钮的倒计时状态。

startCountdown() : void {
  if (this.isCounting) return; // 如果已经在倒计时,则不重复启动

  this.isCounting = true;  // 开始计时
  this.countdown = 60;  // 初始化倒计时为60秒

  // 每秒更新一次倒计时
  this.interval = setInterval(() => {
    if (this.countdown > 0) {
      this.countdown--;
    } else {
      clearInterval(this.interval!);
      this.isCounting = false;  // 结束倒计时
      this.interval = null; // 重置 interval 引用
    }
  }, 1000);
}

显示倒计时
在界面点击发送显示倒计时

              Column() {
                 // 发送验证码按钮
                 Text(this.isCounting ? `(${this.countdown})` : '发送')
                   .textAlign(TextAlign.Center)
                   .width('100%')
                   .height(39)
                   .fontColor('#000000')
                   .fontSize(16)
                   .borderRadius(6)
                   .backgroundColor('#CCCCCC')//倒计时显示
                   .enabled(!this.isCounting && this.phone.length === 11 && /^[0-9]+$/.test(this.phone))
                   .onClick(() => {
                     this.sendVerificationCode(this.phone);
                   })
               }
               .margin({ left: 5 })

详细步骤

  1. 检查是否已经在倒计时
    如果this.isCounting为true,则直接返回,避免重复启动倒计时。
  2. 初始化倒计时状态
  3. 启动定时器
    使用setInterval每秒执行一次回调函数。在回调函数中,检查this.countdown是否大于0。如果是,则每秒递减1。如果this.countdown等于0,则清除定时器 (clearInterval(this.interval!)),设置this.isCounting为false,并重置this.interval为null。

通过以上解析,你已经了解到如何在ArkUI中实现发送验证码、倒计时和登录/注册功能。接下来,我来介绍一下Axios配置与拦截器,以便更加完善这个登录注册功能


Axios配置与拦截器

在本部分中,我们将介绍与登录注册相关的Axios配置,特别是请求拦截器的实现。这部分内容将帮助你理解如何确保在用户未登录的情况下,阻止对需要认证的API的访问,并引导用户到登录注册页面。

Axios配置

首先,我们来看一下Axios的配置部分

import { AxiosError, AxiosResponse, InternalAxiosRequestConfig } from '@ohos/axios';
import { dataPreferences } from '../../entryability/EntryAbility';
import axios from '../AxiosRequest';
import { router } from '@kit.ArkUI';

axios.defaults.baseURL = "http://xxx/xxx";

const instance = axios.create({
  headers: { 'Content-Type': 'application/json' },
});

const noTokenRequiredUrls = [
  'http://xxx/login/phone',
  'http://xxx/login/sendMsg',
  '/xxx'
];

const noNeedRouter = [
  '/xxx'
];

详细说明

  • 基础URL (baseURL):设置所有请求的基础URL为http://xxx/xxx,简化请求路径。
  • 实例创建 (instance):使用axios.create创建一个Axios实例,并设置默认请求头为{‘Content-Type’: ‘application/json’}。
  • 无需Token的URL列表 (noTokenRequiredUrls):定义一个数组,包含不需要token的URL,如登录和发送验证码的接口。
  • 无需路由的URL列表 (noNeedRouter):定义一个数组,包含不需要重定向到登录页面的URL,如获取xxx数据的接口。

请求拦截器

请求拦截器用于在发送请求之前检查用户是否已经登录,并根据需要添加token或重定向到登录页面。

// 添加请求拦截器
instance.interceptors.request.use(async (config: InternalAxiosRequestConfig) => {
  const isNoTokenRequired = noTokenRequiredUrls.some(url => config.url?.includes(url));
  let token: string = "";
  if (!isNoTokenRequired) {
    token = await dataPreferences?.get("token", "") as string;
    if (!token || token == "" || token == "null" || token == "undefined") {
      if (!noNeedRouter.some(url => config.url?.includes(url))) {
        router.pushUrl({
          url: "pages/LoginRegister"
        });
      }
      return Promise.reject('用户未登录');
    }
    config.headers["token"] = token;
  }
  return config;
}, (error: AxiosError) => {
  // 对请求错误做些什么
  return Promise.reject(error);
});

详细步骤

  1. 检查是否需要Token:
  • 使用noTokenRequiredUrls数组检查当前请求的URL是否在无需token的URL列表中。
  • 如果请求的URL在列表中,则isNoTokenRequired为true,跳过token检查。
  1. 获取Token:
    如果请求需要token,则从dataPreferences中获取token。
  2. 验证Token:
  • 检查获取到的token是否为空、null或undefined。
  • 如果token无效,则根据请求URL是否在noNeedRouter列表中决定是否重定向到登录页面。
    如果请求URL不在noNeedRouter列表中,则使用router.pushUrl重定向到pages/LoginRegister。
    返回一个被拒绝的Promise,表示用户未登录。
  1. 添加Token到请求头:
    如果token有效,则将其添加到请求头中 (config.headers[“token”] = token)。
  2. 返回配置:
    返回修改后的请求配置 (config)。
  3. 处理请求错误:
    如果请求配置过程中发生错误,则返回一个被拒绝的Promise,包含具体的错误信息。

为了更好地理解这些配置如何应用于具体的功能,我们来看一个与登录注册相关的接口示例

export async function planJourney(routeId: number, day: number) {
  const params = JSON.stringify({
    routeId: routeId,
    day: day
  });
  return (await instance.post(`/planJourney`, params)).data;
}
  • 请求路径:/planJourney是需要认证的API路径。
  • 请求方法:使用instance.post方法发送POST请求。
  • 请求数据:将routeId和day参数序列化为JSON字符串并作为请求数据发送。
  • 拦截器应用:
    在请求拦截器中,检查/planJourney是否在noTokenRequiredUrls列表中。
    由于/planJourney不在列表中,拦截器将获取token并添加到请求头中。
    如果token无效,则重定向到登录页面 (pages/LoginRegister)。

通过以上对Axios配置和拦截器的详细说明,你可以了解到如何在项目中进行基础的HTTP请求配置,并确保在用户未登录的情况下,阻止对需要认证的API的访问,并引导用户到登录注册页面。


总结

通过本文的详细介绍,我们深入探讨了如何在ArkUI框架中实现一个完整的登录和注册功能,并结合Axios库进行HTTP请求的处理。我们从技术栈的选择开始,逐步解析了代码的整体结构和界面设计,详细说明了发送验证码、登录/注册以及倒计时功能的实现细节。此外,我们还介绍了如何配置Axios拦截器,确保在用户未登录的情况下,阻止对需要认证的API的访问,并引导用户到登录注册页面。希望这个案例可以帮到大家。


openHarmony官方文档
HarmonyOS官方文档
axios官方文档
希望这些资源能够帮助你进一步学习和掌握ArkUI和Axios的相关知识。
深入解析:ArkUI与Axios构建安全高效的登录注册流程-鸿蒙开发者社区

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2024-12-25 11:09:08修改
5
收藏 3
回复
举报
4条回复
按时间正序
/
按时间倒序
mb6759406178e33
mb6759406178e33

太详细了,大佬

回复
2024-12-24 16:31:23
mb676a9c00da0e6
mb676a9c00da0e6

太强了,真的很详细,学到了

回复
2024-12-24 19:34:00
X叶域Q
X叶域Q

写的真详细,原来登录还能这样

回复
2024-12-24 21:11:07
bond_heloworld
bond_heloworld

太强了,真的很详细,学到了

回复
2024-12-26 10:30:58
回复
    相关推荐