目录

阿里云七天云开发校园合伙人创造营 Day 7

在 Midway Serverless 数据库示例模板中实现一个注册和登录系统

云开发7天训练营学习计划

完结撒花 🎉 ,点击上面的链接可以回顾这七天的内容哦~

Day 7: 在 Midway Serverless 数据库示例模板中实现一个注册和登陆系统

关于 Midway Serverless

关于 Midway Serverless 的相关介绍,请参考 Day2 的文章

每日任务

今日任务为使用 Midway Serverless 数据库示例模板实现一个注册和登录系统,展示注册和登录功能。

本次任务原理跟 Day5 相似,请先阅读 Day5 文章每日任务 部分

1. 准备开发环境

登入云开发平台,进入到应用列表,点击 创建新应用,新建一个 WEB 实验室解决方案的 Midway Serverless OTS数据库示例

创建好后,进入 开发部署

/clouddev-day7/create.png
创建应用

进入 IDE 后,先在终端执行:

1
npm i --registry=https://registry.npm.taobao.org

安装 npm 依赖,因为依赖包很多,这里用了淘宝的镜像源,安装会更快。

2. 开发调试

本次任务是直接在 ToDo List 示例的基础上修改,先附上需要改动的地方:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
.
├── f.yml                          # 修改函数定义文件
├── public
|   └── index.html                 # 修改前端 HTML 主页
└── src
	├── index.tsx                  # 修改 React 前端代码
   	└── apis
		├── user.ts                # 增加函数文件
		└── config
			└── config.default.ts  # 部署环境变量配置

同时,我将全部代码上传到 GitHub,可以用于覆盖原来的代码,具体上传代码到远程仓库的方法在 Day1 文章每日任务 第三部分有提及。

2.1 修改代码

按照开发逻辑,首先修改后端代码,在 src/apis 下新增文件 user.ts,主要定义了注册、登录这两个函数,实现一些数据库操作,完整代码如下(点击展开):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import { Func, Inject, Provide } from '@midwayjs/decorator';
import TableStore from 'tablestore';
import format from 'otswhere/format';

@Provide()
export class UserService {

  @Inject()
  ctx;

  @Inject()
  tb;

  @Func('user.login')
  async login() {
    const { name, password } = this.ctx.query;

    const params = {
      tableName: 'user',
      direction: TableStore.Direction.BACKWARD,
      inclusiveStartPrimaryKey: [{ id: TableStore.INF_MAX }],
      exclusiveEndPrimaryKey: [{ id: TableStore.INF_MIN }]
    };

    return new Promise(resolve => {
      this.tb.getRange(params, (_, data) => {
        const rows = format.rows(data, { email: true });
        const userExists = rows.list.findIndex(user => user.name === name) !== -1

        if (!userExists) {
          resolve({
            success: false,
            message: '用户不存在'
          })
          return
        }

        const user = rows.list.find(user => user.name === name);
        if (user.password !== password) {
          resolve({
            success: false,
            message: '密码不正确'
          })
          return
        }

        resolve({
          success: true,
          user
        });
      });
    })
  }

  @Func('user.register')
  async register() {
    const { name, password } = this.ctx.query;
    const params = {
      tableName: "user",
      condition: new TableStore.Condition(TableStore.RowExistenceExpectation.IGNORE, null),
      primaryKey: [
        { id: `${Date.now()}-${Math.random()}` }
      ],
      attributeColumns: [
        { name },
        { password }
      ]
    };
    return new Promise(resolve => {
      this.tb.putRow(params, async function (err, data) {
        if (err) {
          resolve({
            success: false,
            errmsg: err.message
          });
        } else {
          resolve({
            success: true,
            data
          });
        }
      });
    });
  }
}

下一步,在函数定义文件 f.ymlfunctions 下声明定义注册、登录这两个函数,增加代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
register:
  handler: user.register
  events:
    - apigw:
        path: /api/register
login:
  handler: user.login
  events:
    - apigw:
        path: /api/login

接下来修改前端代码,重写 src/index.tsx 文件构造前端页面(点击展开):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import React, { useEffect, useState } from 'react'
import ReactDOM from 'react-dom';

export default function App() {
  return (
    <div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
      <div className="max-w-md w-full">
        <div>
          <img className="mx-auto h-12 w-auto" src="https://tailwindui.com/img/logos/workflow-mark-on-white.svg" alt="Workflow" />
          <h2 className="mt-6 text-center text-3xl leading-9 font-extrabold text-gray-900">
            注册或者登录
            </h2>
        </div>
        <form className="mt-8" action="#" method="POST">
          <input type="hidden" name="remember" defaultValue="true" />
          <div className="rounded-md shadow-sm">
            <div>
              <input
                aria-label="Email address" name="email" type="email" required className="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:shadow-outline-blue focus:border-blue-300 focus:z-10 sm:text-sm sm:leading-5" placeholder="Email address" />
            </div>
            <div className="-mt-px">
              <input
                aria-label="Password" name="password" type="password" required className="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:shadow-outline-blue focus:border-blue-300 focus:z-10 sm:text-sm sm:leading-5" placeholder="Password" />
            </div>
          </div>
          <div className="mt-6">
            <button type="button" className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm leading-5 font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:shadow-outline-indigo active:bg-indigo-700 transition duration-150 ease-in-out">
              <span className="absolute left-0 inset-y-0 flex items-center pl-3">
              </span>
                注册
              </button>
          </div>
          <div className="mt-6">
            <button type="button" className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm leading-5 font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:shadow-outline-indigo active:bg-indigo-700 transition duration-150 ease-in-out">
              <span className="absolute left-0 inset-y-0 flex items-center pl-3">
              </span>
                登录
              </button>
          </div>
        </form>
      </div>
    </div>
  )
}

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

修改 public/index.html 文件,在 head 标签结束前引入 CSS :

1
2
3
4
<head>
  <!-- 接上... -->
  <link href="https://cdn.bootcdn.net/ajax/libs/tailwindcss/1.6.2/tailwind.min.css" rel="stylesheet">
</head>
2.2 连接数据库
注意
要实现功能,需要开通阿里云 OTS 数据库,创建数据表,以及申请一个 AccessKey。具体请回顾 Day5 文章 每日任务 的第二部分。

与 Day5 文章中不同的是,这里只需要创建一个名为 user 的数据表,表主键为 id

/clouddev-day7/createtb.png
创建数据表

2.3 调试

修改好环境变量后,可以执行 npm run dev 运行项目,点击生成的链接查看页面。

/clouddev-day7/preview.jpg
注册登入页面

尝试注册一个新账号,注册成功时会弹窗提示:

/clouddev-day7/register.jpg
注册

尝试使用此账号登录,登录成功时会弹窗提示:

/clouddev-day7/login.jpg
登录

示例

登录异常情况示例:

  1. 用户不存在

/clouddev-day7/error1.jpg
用户不存在

  1. 密码错误

/clouddev-day7/error2.jpg
密码错误

3. 部署上线

警告
正式部署上线前记得将 src/apis/config/config.default.ts 文件改回默认状态。

任务拓展

本次任务同样可以使用 MySQL 数据库,只需在 Midway Serverless MySQL数据库示例 中增加并修改函数文件 user.ts ,并在自己的数据库中新建如下一张数据表即可。

1
2
3
4
5
6
CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `password` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

总结

这个简单的注册、登录只能作学习、演示使用,重点是如何在 Midway Serverless 中使用阿里云 BaaS 服务进行数据的操作和调用。

这个 demo 可改进的地方很多,比如注册时用户名是要求 Email 的,可以增加判断语句限定用户名必须为 Email 地址;又如密码错误次数过多,可以增加尝试次数限制、填写验证码、账号暂时锁定等;也可以限定密码的规范,进行密码加密或者增强验证,将注册和登录分为两个页面,增加跳转路由等等。

由于时间关系需要准备项目实战的内容,以上的改进成为了遗憾,后续 视情况看心情 更新完善,也欢迎大家上 GitHub 提 issue 和 PR。

七天创造营就到此结束了,对后续 项目实战 内容感兴趣的朋友们记得 关注我 哦~