Skip to content

任务七 实现登录注册功能

应用程序接口

应用程序接口(API:application programming interface)是一组定义、程序及协议的集合,通过 API 接口实现计算机软件之间的相互通信。它就像一座桥梁,从一个程序接收请求或消息,然后将其传递给另一个程序,翻译消息并根据 API 的程序设计执行协议。

API的工作原理是以受控的方式开放软件的一小部分功能和数据,这使开发人员无需研究整个系统的代码即可访问该程序和相关数据。

比如,研发人员花藤开发了软件A,研发人员小雷正在研发软件B。 有一天,研发人员小雷想要调用软件A的部分功能来用,但是他又不想从头看一遍软件A的源码和功能实现过程,怎么办呢?研发人员花藤想了一个好主意:我把软件A里你需要的功能打包好,写成一个函数。你按照我说的流程,对函数进行请求和调用,就可以直接使用我的功能。API就是研发人员花藤说的那个函数。

日常生活中,我们有很多类似API的场景,比如,电脑需要调用手机里面的信息,这时候你会拿一根数据线将电脑手机连接起来,电脑和手机上连接数据线的接口就相当于“API接口”。

现在大多数软件项目均采用前后端分离的架构方式,前端呈现的数据需要后端以接口的方式进行传递。这个流程可以用一个生活中的例子来理解,顾客去一家餐馆吃饭,服务员帮助顾客点完菜之后,将菜单送到后厨,在菜做好时服务员会回到后厨并把菜端给顾客。

在这个案例中,服务员是前端开发人员,厨师是后端开发人员,顾客就是用户。服务员将顾客点的菜(请求参数)传至后厨(请求地址),接收到请求后,后厨将会在一定时间内反馈(返回结果),然后服务员再去把菜端过来。这就是请求一个接口的整体流程。

后端开发者在开发接口后,还需要编写一份接口文档。接口文档顾名思义就是接口的说明文档,它是我们调用接口的依据。接口文档包含了对接口URL、参数以及输出内容的说明,我们参照接口文档就能方便的知道接口的作用,以及接口如何进行调用。

接口文档一般由6项内容组成:

  1. 接口名称:用来标识各个接口的简单说明,如登录接口,获取图书列表接口等
  2. 接口URl:接口的调用地址
  3. 调用方式:接口的调用方式,如GET或POST
  4. 参数格式:接口需要传递的参数 ,每个参数必须包含参数名称,参数类型,是否必选,参数说明这4项内容
  5. 响应格式:接口的返回值的详细描述、一般包含数据名称、数据类型、说明3项内容
  6. 返回示例(可选):通过对象的形式,例举服务器返回数据的结构

本项目为读者提供了接口说明文档,在项目功能实现的任务中,我们会参照接口说明文档进行接口的调用。

接口文档地址:https://console-docs.apipost.cn/preview/c83bb06ba5a3add5/6174dc93dd96469c

HTTP请求

HTTP(HyperText Transfer Protocol,超文本传输协议)是一套计算机通过网络进行通信的规则。通过HTTP可以使客户端(如Web浏览器)从服务端(如Web服务器)请求信息和服务,HTTP遵循请求(Request)/应答(Response)模型,即客户端向服务端发送请求,服务端处理请求并返回应答,所有HTTP连接都被构造成一套请求和应答。

HTTP三点注意事项:

  1. HTTP是无连接的:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
  2. HTTP是媒体独立的:这意味着,只要客户端和服务器知道如何处理的数据内容,任何类型的数据都可以通过HTTP发送。客户端以及服务器指定使用适合的MIME-type内容类型。大多数Web浏览器都拥有一系列的可配置的辅助应用程序,它们告诉浏览器应该如何处理Web服务器发送过来的各种内容类型。
  3. HTTP是无状态的:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

通信步骤

HTTP通信步骤如图所示:

HTTP通信机制是在一次完整的HTTP通信过程中,Web浏览器与Web服务器之间将完成下列7个步骤:

  1. 建立TCP连接

在HTTP工作开始之前,Web浏览器首先要通过网络与Web服务器建立连接,该连接是通过TCP来完成的,该协议与IP协议共同构建Internet,即著名的TCP/IP协议族,因此Internet又被称作是TCP/IP网络。HTTP是比TCP更高层次的应用层协议,根据规则,只有低层协议建立之后才能进行更深层协议的连接,因此,首先要建立TCP连接,默认TCP连接的端口号是80(可以进行修改)。

  1. Web浏览器向Web服务器发送请求命令

一旦建立了TCP连接,Web浏览器就会向Web服务器发送请求命令 。

例如:GET/sample/hello.jsp HTTP/1.1 。

  1. Web浏览器发送请求头信息

浏览器发送其请求命令之后,还要以头信息的形式向Web服务器发送一些别的信息,之后浏览器发送了一空白行来通知服务器,它已经结束了该头信息的发送。

  1. Web服务器应答

客户机向服务器发出请求后,服务器会客户机回送应答,即响应。

例如:HTTP/1.1 200 OK

响应的第一部分是协议的版本号和应答状态码。

  1. Web服务器发送应答头信息

正如客户端会随同请求发送关于自身的信息一样,服务器也会随同应答向用户发送关于它自己的数据及被请求的文档。

  1. Web服务器向浏览器发送数据

Web服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着,它就以Content-Type应答头信息所描述的格式发送用户所请求的实际数据。

  1. Web服务器关闭TCP连接

一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入 Connection:keep-alive 这行代码。TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。

请求方式

根据HTTP标准,HTTP请求可以使用多种请求方法。HTTP1.0定义了三种请求方法:GET、POST和HEAD方法。HTTP1.1新增了五种请求方法:OPTIONS、PUT、DELETE、TRACE和CONNECT方法。其中最常用的请求方法为GET和POST。

方法描述
GET请求指定的页面信息,并返回实体主体。
HEAD类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头
POST向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。
PUT从客户端向服务器传送的数据取代指定的文档的内容。
DELETE请求服务器删除指定的页面。
CONNECTHTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
OPTIONS允许客户端查看服务器的性能。
TRACE回显服务器收到的请求,主要用于测试或诊断。
PATCH实体中包含一个表,表中说明与该URI所表示的原内容的区别。

消息结构

  • 客户端请求消息:客户端发送一个HTTP请求到服务器的请求消息包括以下格式:请求行(Request Line)、请求头部(Header)、空行和请求数据四个部分组成。
  • 服务器响应消息:HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。

状态码

当用户访问一个网页时,用户的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前,此网页所在的服务器会返回一个包含HTTP状态码的信息头(Server Header)用以响应浏览器的请求。

HTTP状态码的英文为HTTP Status Code。

HTTP状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型。响应分为五类:信息响应(100–199),成功响应(200–299),重定向(300–399),客户端错误(400–499)和服务器错误 (500–599)。

下面是常见的HTTP状态码:

  • 200 - 请求成功
  • 301 - 资源(网页等)被永久转移到其它URL
  • 404 - 请求的资源(网页等)不存在
  • 500 - 内部服务器错误

接口设计规范

在接口设计中存在一个被普遍承认和遵守的——RESTful设计原则,它被Roy Felding提出。而REST的核心原则是将API拆分为逻辑上的资源。这些资源通过HTTP被操作(GET 、POST、PUT、DELETE),REST也可以说是促使开发者更规范的使用HTTP协议。

示例

  • GET /tickets # 获取ticket列表
  • GET /tickets/12 # 查看某个具体的ticket
  • POST /tickets # 新建一个ticke
  • PUT /tickets/12 # 更新ticket 12
  • DELETE /tickets/12 #删除ticekt 12

可以看出使用REST的好处在于可以充分利用HTTP的强大实现对资源的创建(Create)、更新(Update)、读取(Read)和删除(Delete)功能。而这里你只需要一个 /tickets ,再没有其他什么命名规则和URL规则。但是有的需要使用复数使得你的URL更加规整。这让API使用者更加容易理解,对开发者来说也更容易实现。

示例

  • GET /tickets/12/messages # 获取关于ticket 12的messages
  • GET /tickets/12/messages/5 # 获取关于ticket 12的某个具体messages
  • POST /tickets/12/messages # 新建关于ticket 12的messages
  • PUT /tickets/12/messages/5 # 更新关于ticket 12的messages 5
  • PATCH /tickets/12/messages/5 # 局部更新关于ticket 12的messages 5
  • DELETE /tickets/12/messages/5 # 删除关于ticket 12的messages 5

uni-app发送网络请求

在uni-app中使用uni.request方法可以发送网络请求,在各个小程序平台运行时,网络相关的 API 在使用前需要配置域名白名单。

参数名类型必填默认值说明平台差异说明
urlString开发者服务器接口地址
dataObject/String/ArrayBuffer请求的参数App 3.3.7 以下不支持 ArrayBuffer 类型
headerObject设置请求的 header,header 中不能设置 RefererApp、H5端会自动带上cookie,且H5端不可手动修改
methodStringGET有效值详见下方说明
timeoutNumber60000超时时间,单位 msH5(HBuilderX 2.9.9+)、APP(HBuilderX 2.9.9+)、微信小程序(2.10.0)、支付宝小程序
successFunction收到开发者服务器成功返回的回调函数
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

method 有效值

注意:method有效值必须大写,每个平台支持的method有效值不同。

methodAppH5微信小程序支付宝小程序百度小程序字节跳动小程序、飞书小程序快手小程序京东小程序
GET
POST
PUTxxx
DELETExxxx
CONNECTxxxxxx
HEADxxxx
OPTIONSxxxx
TRACExxxxxx

success 返回参数说明

参数类型说明
dataObject/String/ArrayBuffer开发者服务器返回的数据
statusCodeNumber开发者服务器返回的 HTTP 状态码
headerObject开发者服务器返回的 HTTP Response Header
cookiesArray.<string>开发者服务器返回的 cookies,格式为字符串数组

示例

uni.request({
    url: 'https://www.example.com/request', //仅为示例,并非真实接口地址。
    data: {
        text: 'uni.request'
    },
    header: {
        'custom-header': 'hello' //自定义请求头信息
    },
    success: (res) => {
        console.log(res.data);
        this.text = 'request success';
    }});

一个完整的应用软件,可能会涉及很多网络请求,为了便于管理、收敛请求入口、拦截请求和响应、处理请求和响应数据等等,工程上最好的作法就是将所有网络请求放到同一个源码文件中,即对网络请求进行封装。在启嘉校园项目中,大部分后端接口的请求参数都需要token(仅在登录状态下才可调用),以及需要对接口响应状态码code进行处理,为了避免在每次网络请求时做相同的传参或响应处理,我们对uni.request进行了二次封装,去处理这些重复性操作。

uni-app条件编译处理多端差异

uni-app 已将常用的组件、JS API 封装到框架中,开发者按照uni-app规范开发即可保证多平台兼容,大部分业务均可直接满足。但每个平台有自己的一些特性,因此会存在一些无法跨平台的情况,比如小程序授权登录功能只能在小程序平台中使用,在其它平台中需采用账号+密码、第三方平台授权等登录方式,解决此问题的常规解决方案存在以下两个弊端

  • 大量编写 if else,会造成代码执行性能低下和管理混乱。
  • 编译到不同的工程后二次修改,会让后续升级变的很麻烦。

因此uni-app为开发者提供了一种完美的解决方案——条件编译。条件编译是用特殊的注释作为标记,在编译时根据这些特殊的注释,将注释里面的代码编译到不同平台。

条件编译语法

以 #ifdef 或 #ifndef 加 %PLATFORM% 开头,以 #endif 结尾。

  • #ifdef:if defined 仅在某平台存在
  • #ifndef:if not defined 除了某平台均存在
  • %PLATFORM%:平台名称

uni.setTabBarBadge参数说明:

条件编译语法说明
#ifdef APP-PLUS需条件编译的代码#endif仅出现在 App 平台下的代码
#ifndef H5需条件编译的代码#endif除了 H5 平台,其它平台均存在的代码
#ifdef H5 || MP-WEIXIN需条件编译的代码#endif在 H5 平台或微信小程序平台存在的代码(因为没有交集,这里只有||,不可能出现&&)

%PLATFORM% 可取值:

生效条件
VUE3HBuilderX 3.2.0+
APP-PLUSApp
APP-PLUS-NVUE或APP-NVUEApp nvue 页面
APP-ANDROIDApp Android 平台 仅限 uts文件
APP-IOSApp iOS 平台 仅限 uts文件
H5H5
MP-WEIXIN微信小程序
MP-ALIPAY支付宝小程序
MP-BAIDU百度小程序
MP-TOUTIAO字节跳动小程序
MP-LARK飞书小程序
MP-QQQQ小程序
MP-KUAISHOU快手小程序
MP-JD京东小程序
MP-360360小程序
MP微信小程序/支付宝小程序/百度小程序/字节跳动小程序/飞书小程序/QQ小程序/360小程序
QUICKAPP-WEBVIEW快应用通用(包含联盟、华为)
QUICKAPP-WEBVIEW-UNION快应用联盟
QUICKAPP-WEBVIEW-HUAWEI快应用华为

文件支持类型

  • .vue
  • .js
  • .css
  • pages.json
  • 各预编译语言文件,如:.scss、.less、.stylus、.ts、.pug

条件编译注意事项

  • 条件编译是利用注释实现的,在不同语法里注释写法不一样,js使用 // 注释、css 使用 /* 注释 */、vue/nvue 模板里使用
  • 条件编译APP-PLUS包含APP-NVUE和APP-VUE,APP-PLUS-NVUE和APP-NVUE没什么区别,为了简写后面出了APP-NVUE
  • 使用条件编译请保证编译前和编译后文件的正确性,比如json文件中不能有多余的逗号
  • VUE3 需要在项目的 manifest.json 文件根节点配置 "vueVersion" : "3"

示例

比如我们想在微信小程序中添加关注微信公众号功能,可以直接使用微信小程序的公众号关注组件official-account实现,但是当发布到其它平台时不需要关注微信公众号功能,因此可以使用条件编译来控制组件是否编译。

<view>
    <view>微信公众号关注组件</view>
    <view>
        <!-- uni-app未封装,但可直接使用微信原生的official-account组件-->
        <!-- #ifdef MP-WEIXIN -->
		        <officia-account></official-account>
	<!-- #endif -->
    </view>
</view>

除此之外,条件编译还可以在js、css、json文件和目录中使用。

uni-app登录方法uni.login

uni.login是一个客户端API,统一封装了各个平台的各种常见的登录方式,包括App手机号一键登陆、三方登录(微信、微博、QQ、Apple、google、facebook)、各家小程序内置登录。

除了前端API,DCloud还提供了uni-id,这是一个云端一体的、完整的、账户开源框架。不仅包括客户端API,还包括前端页面、服务器代码、管理后台等所有与登录账户有关的服务,包括短信验证码、密码加密存储、忘记密码、头像更新等所有常见账户相关功能。

App平台支持的登录方式

  • 手机号一键登录(univerify)
  • 苹果登录(Sign in with Apple)
  • 微信登录
  • QQ登录
  • 新浪微博登录
  • Google登录
  • Facebook登录

小程序平台支持的登录方式

  • 微信小程序登录
  • 支付宝小程序登录
  • 百度小程序登录
  • 字节跳动小程序登录
  • QQ小程序登录
  • 快手小程序登录
  • 京东小程序登录

Web平台支持的登录方式

Web平台常见的登录包括用户名密码、短信验证码、PC端微信扫描、微信公众号登录。这些没有封装在 uni.login API中,但都封装在了uni-id中。

如不使用uni-id,微信内嵌浏览器运行H5版时,可通过JS SDK实现微信登录,需要引入一个单独的JS。

大多数登录方式,都需要申请开通相关服务,具体开通流程相关要求可以到官方文档中查看。以微信小程序登录方式为例,uni.login需要主要参数有三个。

uni.login主要参数:

参数名类型必填说明
providerString登录服务提供商,通过 uni.getProvider]获取,如果不设置则弹出登录列表选择界面
timeoutNumber超时时间,单位ms
successFunction接口调用成功的回调

success返回参数说明:

参数类型说明平台差异说明
userInfoOBJECT用户信息对象
rawDataString不包括敏感信息的原始数据字符串,用于计算签名。
signatureString使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息。微信小程序、字节跳动小程序、飞书小程序、快手小程序、京东小程序
encryptedDataString包括敏感数据在内的完整用户信息的加密数据,详细见加密数据解密算法。微信小程序、字节跳动小程序、飞书小程序、快手小程序
ivString加密算法的初始向量,详细见加密数据解密算法。微信小程序、字节跳动小程序、飞书小程序、快手小程序
errMsgString描述信息

示例

uni.login({
	provider: 'weixin', //使用微信登录
	success: function(loginRes) {
		console.log(loginRes.authResult);
	}
});

由于使用“手机号一键登录”需要开发者登录“DCloud开发者中心”申请一键登录服务,并且使用此项服务需要收取费用。为了降低学习难度和学习成本,同时又希望读者能感受uni-app多端发布的优势,所以,本项目在H5和APP端并未采用“手机号一键登录”方式,而是采用了注册账号+密码的登录方式。

这里需要提醒读者的一点是,在本项目的微信小程序端登录并未让用户绑定注册用户名和密码,所以在不同端下未实现账号数据的互通,如果想实现账号互通,需要后端开发者修改数据库和后端的处理逻辑,对前端开发的影响并不大。

AppID和AppSecret鉴权机制

AppID是产品的账号ID,在注册时由平台下发,一般是由字母和数字所组成一串符号。该账号和人的身份证号一样,是应用软件的唯一识别码。我们常见的公众号、小程序、开放平台、第三方平台、移动应用、网站应用和小商店都具有AppID。

AppID和AppSecret通常使用在调用接口时将其携带传递到服务器服务器会返回对应的token,后续业务接口的调用通过token进行校验。

AppID和AppSecret获取方式

AppID和AppSecret可在微信公众平台 -> 开发 -> 开发管理 -> 开发设置页面中获取(如图8-4所示),需要注意的是,微信小程序密钥AppSecret不会明文存储在微信公众平台上,因此小程序密钥一定要及时保存下来。

token验证

token由后端开发人员自定义生成,作为访问资源接口(API)时所需要的资源验证凭证,每一次请求都需要携带token。

简单的token一般由用户标识uid、时间戳time、签名sign组成。其中uid是用来识别用户的唯一身份标识,可以使用数据库中的用户主键表示,也可以使用微信接口服务提供的openid表示;time是时间戳,用来计算登录用户的应过期时间,如果token过期,则需要重新登录;sign是签名,token前几位以hash算法压缩成的一定长度的16进制字符串。

token的身份验证流程总结如下:

  • 客户端使用用户名和密码进行登录(如果使用微信授权,则使用微信提供的code进行登录)
  • 服务端收到请求,去验证用户名与密码
  • 验证成功后,服务端会签发一个token 并把这个token 发送给客户端,客户端收到token后,会把它存储起来,比如放在cookie 里 或者 localStorage里
  • 客户端每次向服务端请求资源的时候需要带着服务端签发的token,服务端收到请求后,先验证客户端请求里带着的token ,如果验证成功,就向客户端返回请求的数据

token验证的特点

  • token 完全由应用管理,所以它可以避开同源策略(Cookie不允许跨域访问)
  • token 可以避免CSRF攻击
  • token 只要客户端能够进行存储就能够使用,并不需要Cookie
  • 基于token 的用户验证是一种服务端无状态的认证方式,相比使用session技术识别用户,在使用token时,服务端不用存放token数据,用解析token的计算时间换取 session的存储空间,从而减轻服务器的压力
  • token的最大缺点是服务器不保存会话状态,所以在使用期间不可能取消令牌或更改令牌的权限。也就是说,一旦token签发,在有效期内将会一直有效

应用生命周期

uni-app 支持如下应用生命周期函数。

函数名说明
onLaunch当uni-app 初始化完成时触发(全局只触发一次)
onShow当uni-app启动,或从后台进入前台显示
onHide当uni-app从前台进入后台
onError当uni-app报错时触发
onUniNViewMessage对 nvue 页面发送的数据进行监听
onUnhandledRejection对未处理的 Promise 拒绝事件监听函数(2.8.1+)
onPageNotFound页面不存在监听函数
onThemeChange监听系统主题变化

示例

<script>
	// 只能在App.vue里监听应用的生命周期
	export default {
		onLaunch: function() {
			console.log('App Launch')
		},
		onShow: function() {
			console.log('App Show')
		},
		onHide: function() {
			console.log('App Hide')
		}
	}
</script>

注意

  • 应用生命周期仅可在App.vue中监听,在其它页面监听无效
  • 应用启动参数,可以在API uni.getLaunchOptionsSync获取
  • onlaunch里进行页面跳转,如遇白屏报错,请参考https://ask.dcloud.net.cn/article/35942
  • onPageNotFound 页面实际上已经打开了(比如通过分享卡片、小程序码)且发现页面不存在,才会触发,API跳转不存在的页面不会触发(如 uni.navigateTo)

getApp()页面调用接口

getApp() 函数用于获取当前应用实例,一般用于获取globalData 。

示例

const app = getApp()
console.log(app.globalData)

注意

  • 不要在定义于 App() 内的函数中,或调用 App 前调用 getApp() ,可以通过 this.$scope 获取对应的app实例
  • 通过 getApp() 获取实例之后,不要私自调用生命周期函数
  • 当在首页nvue中使用getApp()不一定可以获取真正的App对象。对此提供了const app = getApp({allowDefault: true})用来获取原始的App对象,可以用来在首页对globalData等初始化

globalData全局变量机制

小程序有globalData,这是一种简单的全局变量机制。这套机制在uni-app里也可以使用,并且全端通用。

以下是 App.vue 中定义globalData的相关配置:

<script>  
    export default {  
        globalData: {  
            text: 'text'  
        }
    }  
</script>  

JS中操作globalData的方式:getApp().globalData.text = 'test'。

在应用onLaunch时,getApp对象还未获取,暂时可以使用this.globalData获取globalData。

如果需要把globalData的数据绑定到页面上,可在页面的onShow页面生命周期里进行变量重赋值。

nvue的weex编译模式中使用globalData的话,由于weex生命周期不支持onShow,但熟悉5+的话,可利用监听webview的addEventListener show事件实现onShow效果,或者直接使用weex生命周期中的beforeCreate。但建议开发者使用uni-app编译模式,而不是weex编译模式。

globalData是简单的全局变量,如果使用状态管理,请使用vuex(main.js中定义)。

Released under the MIT License.