之前几次好多人写了教程,我就懒得写教程了。这次没啥人写教程,因此我来写一个保姆级的教程。

前几次教程

第6次作业
https://www.wulel.cn/archives/154/
https://blog.bt7.xyz/index.php/archives/aliyun-cloud-day6.html
https://3j0.cn/2020/08/05/clouddev/#directory003529651546788978
https://blog.seeleclover.com/2020/08/10/Aliyun-workbench-Learning-Day6/
https://iybai.cn/2020/08/10/day-6/

第5次作业
https://blog.bt7.xyz/index.php/archives/aliyun-cloud-day5.html
https://hexo.weii.ga/2020/08/05/%E4%BA%91%E5%BC%80%E5%8F%91%E7%AC%94%E8%AE%B0/
https://www.jiahuiblog.com/a-li-yun-day05/
https://www.wulel.cn/archives/151/
https://blog.csdn.net/zhijiex/article/details/107869742
https://mooncn.win/study/405.html
https://3j0.cn/2020/08/05/clouddev/#directory054702631395156797
第4次作业
https://www.jiahuiblog.com/a-li-yun-day04/
https://blog.bt7.xyz/index.php/archives/aliyun-cloud-day4.html
https://www.nanwayan.cn/article/99
https://mooncn.win/study/400.html
第3次作业
https://blog.bt7.xyz/index.php/archives/aliyun-cloud-day3.html
https://hexo.weii.ga/2020/08/05/云开发笔记/
https://3j0.cn/2020/08/05/clouddev/
https://www.wulel.cn/archives/143/
https://www.nanwayan.cn/article/97
https://onesite.pro/2020/08/05/day03/
https://jaav.com.cn/posts/5b34.html
https://mooncn.win/study/398.html
https://axh2018.gitee.io/posts/5b34.html
https://blog.010911.xyz/post/a-li-yun-kai-fa-xiao-yuan-he-huo-ren-chuang-zao-ying-di-san-tian/
第2次作业
https://blog.bt7.xyz/index.php/archives/aliyun-cloud-day2.html
http://mhsl.tech/midwayjs.html
https://3j0.cn/2020/08/05/clouddev/
https://www.xn2001.com/archives/541.html
https://blog.nextcat.top/150.html
第1次作业
https://blog.bt7.xyz/index.php/archives/aliyun-cloud-day1.html
https://3j0.cn/2020/08/05/clouddev/

如果你是小白请完全仿照我的步骤来做,不要跳步骤

创建应用

按以下路径选择:
应用列表 - 创建新应用 - 实验室 - WEB - Midway Serverless OTS数据库示例 - 下一步

“应用名称” 与 “应用介绍” 随便填(不知道咋填就填数字7也行),地域选择:杭州

点击完成,等待“开发部署按钮”出现。

安装依赖

点击“终端”,进入终端。
按顺序输入

npm install -g cnpm --registry=https://registry.npm.taobao.org
cnpm i

这里为了完成作业使用的是cnpm,cloudide部署是不支持cnpm的,这点要注意

等待执行完成。(建议执行两次cnpm i
提示:

$cnpm i
✔ Installed 18 packages
✔ Linked 0 latest versions
✔ Run 0 scripts
✔ All packages installed (used 24ms(network 19ms), speed 0B/s, json 0(0B), tarball 0B)

有四个对勾即可。

准备OTS数据库

前往:

https://otsnext.console.aliyun.com/

左上角选择“杭州”,点击“创建实例”,按以下格式编写。
实例名称 - 如果不会可以用在键盘上随便敲几个英文(小写)
实例规格 - 高性能实例
点击确认,可以马上创建完成。

准备RAM子用户

创建用户

如果你不懂阿里云权限管理请不要自作主张的使用你个人头像那的accesskey,这是因为阿里云的accesskey拥有你账户的几乎全部权限,一但泄露后果非常严重。

前往:

https://ram.console.aliyun.com/users

点击“创建用户”,登录名称与显示名称均输入ots
访问方式勾选:编程访问,点击确定
可能会进行手机验证码的验证,进行验证即可。
验证通过后,新的界面有:

用户登录名称
状态
登录密码
AccessKey ID
AccessKey Secret

这几个选项。AccessKey IDAccessKey Secret是目前我们需要的东西。

在“用户信息”处有一个:下载csv文件,点击下载,可以用excel/wps打开。这个文件内也有以上的信息,请保存好。
你自己手动保存到你存密码的文件夹、软件内。
点击返回,创建用户完成。

分配权限

点击用户名称:ots@************.onaliyun.com,进入ots@************.onaliyun.com用户的管理界面
按如下方式给RAM用户权限
RAM-OTS权限
按理说个人权限处只有如下的一行,如果不对说明你跳着看了。

权限应用范围 - 全局
权限策略名称 - AliyunOTSFullAccess
权限策略类型 - 系统策略
备注 -     管理表格存储服务(OTS)的权限

此时可以关闭RAM访问控制网页

网站预热

在cloudide执行

cnpm run dev

会有黄色的代码提示,正常现象,不用管他。

当显示:

---------------------------------------

开发服务器已成功启动

请打开 >>> `http://6548dde4-c7aa-4613-8c73-379ca56d0887-3000.xide.aliyun.com/`

---------------------------------------
感谢使用 Midway Serverless,欢迎 Star!
https://github.com/midwayjs/midway
---------------------------------------

时,倒数30s,然后在电脑的浏览器上访问:http://6548dde4-c7aa-4613-8c73-379ca56d0887-3000.xide.aliyun.com/
会显示TODO的网页,如果不显示的话使用无痕模式打开。

处理前端文件

进入cloudide

别把index.tsxindex.ts弄混了

index.tsx

清空src/index.tsx,然后粘贴以下内容:

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')
);

保存文件

index.html

清空public/index.html,然后粘贴以下内容:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>Todo List</title>
    <link href="https://cdn.bootcdn.net/ajax/libs/tailwindcss/1.6.2/tailwind.min.css" rel="stylesheet">
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>
</html>

刷新之前访问的地址:http://6548dde4-c7aa-4613-8c73-379ca56d0887-3000.xide.aliyun.com/
大概会自动刷新
这个是我的地址,你得访问你自己的。

连接数据库

进入CLOUDIDE
打开文件:src/apis/config/config.default.ts
里面的:

  exports.tbConfig = {
    accessKeyId: process.env.MIDWAY_OTS_ACCESSKEY,
    secretAccessKey: process.env.MIDWAY_OTS_SECRET,
    endpoint: process.env.MIDWAY_OTS_ENDPOINT, // e.g. https://todo-test-list.cn-hangzhou.ots.aliyuncs.com
    instancename: process.env.MIDWAY_OTS_INSTANCE // e.g. todo-test-list
  };

是要你填数据库信息的地方

之前的OTS地址没要你关,点击OTS实例名称进入OTS。

复制:实例访问地址 - 公网 那一栏的内容,替换掉process.env.MIDWAY_OTS_ENDPOINT
复制:OTS名称,替换掉process.env.MIDWAY_OTS_INSTANCE

注意是公网!!!公网!!!

用excel打开前面创建RAM用户获得的CSV文件
AccessKeyIdAccessKeySecret下面的内容分别替换掉process.env.MIDWAY_OTS_ACCESSKEYprocess.env.MIDWAY_OTS_SECRET

如下所示

  exports.tbConfig = {
    accessKeyId: 'LTAI4G6qMJWKEhYGSqC8FRmm',
    secretAccessKey: 'WZvKwinonLgYP56kOwKiRC0hSJBGF8',
    endpoint: 'https://fassdadw.cn-hangzhou.ots.aliyuncs.com', // e.g. https://todo-test-list.cn-hangzhou.ots.aliyuncs.com
    instancename: 'fassdadw' // e.g. todo-test-list
  };

注意英文引号,注意句尾的逗号

编辑完成后保存文件,先别关OTS网页。

新增注册登录功能

index.tsx

编辑src/index.tsx,大概是获取前端赋予的值的意思。不懂的同学直接还是清空文件,然后用下面的代码覆盖

import React, { useState } from 'react'
import ReactDOM from 'react-dom';

export default function App() {
  const [name, setName] = useState('')
  const [password, setPassword] = useState('')

  const handleRegister = () => {
    console.log('name is', name)
    console.log('password is', password)

    fetch(`/api/register?name=${name}&password=${password}`)
    .then(resp => resp.json())
    .then(resp => {
      if (resp.success === true) {
        alert('注册成功')
      }
    })
  }

  const handleLogin = () => {
    console.log('name is', name)
    console.log('password is', password)

    fetch(`/api/login?name=${name}&password=${password}`)
    .then(resp => resp.json())
    .then(resp => {
      if (resp.success === true) {
        alert(`登录成功,用户名:${resp.user.name}`)
      } else {
        alert(`登录失败,提示信息:${resp.message}`)
      }
    })
  }

  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
                onChange={e => {
                  console.log('当前输入的账号是:', e.target.value)
                  setName(e.target.value)
                }}
                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
                onChange={e => {
                  setPassword(e.target.value)
                }}
                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" onClick={handleRegister} 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" onClick={handleLogin} 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')
);

保存文件

user.ts

创建新文件src/apis/user.ts
复制粘贴以下内容:

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 },
        { status: '1' }
      ]
    };
    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.yml

编辑:f.yml,在

  add:
    handler: todo.add
    events:
      - apigw:
          path: /api/add

后面添加

  register:
    handler: user.register
    events:
      - apigw:
          path: /api/register
  login:
    handler: user.login
    events:
      - apigw:
          path: /api/login

注意保证每个模块之间的对齐。(例如add、register、login这三个要保证对齐)

新建表

进入OTS数据库,上面要你别关的那个。
创建数据表

数据表名称 - user
表主键 - id

点击确定
还是别关网页。

测试

我们可以从浏览器和OTS界面查看代码是否真的能正确运行。

浏览器检测

打开之前的网站,,浏览器使用f12打开调试模式,按如图所示选择:
Network - Disable cache - XHR
输入用户名和密码后,点击注册,再点击确认。(只有点击确认后,才会刷新调试页面)
然后在浏览器调试页面的name处就会出现post的地址,鼠标选中地址,点击Preview,显示为true。即正常保存到数据库。
f12

OTS数据库

从之前创建的数据表列表,表名user处,点击进入,选择数据管理,查看数据的内容是否有新增。
OTS

其实现在登录也可以正常登录了,你可以试试。调试方法是通过浏览器检测一样。检查get是否正常(200状态)。
查看post还是get是查看header选项栏。

作业

下面就是需要提交的作业的内容了。

默认状态

默认状态

注册成功

注册成功

登录成功

登录成功

登录失败,用户不存在

登录失败,用户不存在

登录失败,密码错误

登录失败,密码错误

后续

关闭OTS

前往:

https://otsnext.console.aliyun.com/

删除:数据表列表,比如我之前创建的user。然后点击释放,释放实例。
大概需要10分钟。

删除RAM用户

前往:

https://ram.console.aliyun.com/users

点击删除,删掉之前创建的用户。

删除应用

前往:

https://workbench.aliyun.com/application

删掉你创建的项目,记得选中同时删除云资源。


参考:
云开发创造营 · 第七天课程资料

最后修改:2020 年 08 月 13 日 06 : 37 AM