基于 NodeJS 创建部署云应用 Todo App
本文介绍如何基于 NodeJS 技术栈创建并部署一个简单的 Todo App 应用程序。
前提条件
操作步骤
整个操作流程分为以下 8 步:
创建小程序
- 登录 蚂蚁金服开放平台,选择 开发者中心 > 小程序 > 创建。
- 填写 基本信息,点击 创建 按钮,创建应用名为 示例应用 小程序。
说明:一个账号下最多可以创建10个小程序;未提交过审核的小程序可以删除,删除的小程序不在计数范围。
创建云应用后端服务
- 在 我的小程序 页面,选择刚创建的小程序,点击 查看,进入 开发管理 页面。
- 点击左侧导航栏的 云服务(公测),在 云服务列表 页面点击 创建云服务 > 创建云应用。
- 在 创建云应用 页面,选择 NodeJS** 技术栈,填入 应用名称 和 描述 (选填),点击 创建**。
构建环境
- 返回 云服务 页面,点击刚创建的云服务卡片中的 构建环境 按钮。
- 在 购买环境资源 页面,选择合适的环境配置方案,此处选择 小程序云应用入门(Mysql版),点击 同意《产品服务协议》 > 确认配置。
说明:当前测试环境该方案免费提供,但若连续 7 日未部署过代码,环境会被自动回收。
- 在 订单详情 页面,点击 确认购买。购买成功后会自动进入 构建环境 页面。构建过程会耗时几分钟,构建成功后,您可以选择 查看应用详情 ,或者 返回应用列表 。
准备开发环境
打开小程序开发者工具,点击 新建项目。
在新建项目向导中,选择 小程序,选择 Todo 云应用-NodeJS **示例,点击下一步**。
输入 项目名称,项目路径会自动填充,选择 云应用 为后端服务,点击 完成。
项目创建好后,进入开发界面。点击右上角的 登录 按钮,用支付宝扫码登录。
关联前面 创建的小程序 应用。
关联前面 构建的云应用环境,点击 确定。
数据准备
开发后端代码之前,需要先建立应用运行所需的数据库。
在 云应用详情 页面的 数据库 页签,点击查看**Web 控制台,登录后进入 PhpMyAdmin 的数据库管理页面。(数据库自带admin账户,admin账户的初始密码可点击 数据库** 标签页中的眼睛图标()查看,可使用admin账户登录PhpMyAdmin )
在页面左侧,选中 sample 数据库中,新建表 task,
task表的数据结构如下图。
请确保您新建的表结构与图中完全一致,否则示例程序可能无法运行,注意:字段名称、 字段类型、 id 字段为主键且自增(A_I)、 done 字段默认值为 0、各字段是否允许空值。点击 保存,完成创建该示例程序所需的数据库表。除了如第 2 步所述创建表的操作,您也可以通过执行以下 SQL 语句创建 task 表。
USE sample; CREATE TABLE `task` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`label` varchar(140) CHARACTER SET utf8 DEFAULT NULL,`done` tinyint(1) NOT NULL DEFAULT '0',`img_url` varchar(256) CHARACTER SET utf8 DEFAULT NULL,`user_id` varchar(50) CHARACTER SET utf8 DEFAULT NULL,PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
开发云应用后端服务
后端代码结构如下图,运行后端服务之前需要修改图中标注的几个文件。
- config/config.default.js 文件:其中的 config.mysql 对象中数据库相关的参数需修改为您创建的云应用的对应值。
具体值可以从 云应用详情 页的 数据库 页签中得到。
- app/controller/home.js 文件:getUserInfo 方法中初始化 AlipaySdk 对象的代码如下:
参数 appId、privateKey 和 alipayPublicKey 分别为小程序 ID 和应用的私钥、公钥。其中,私钥和公钥分别存储在了文件 private-key.pem 和 public-key.pem 中。
小程序的ID可以在小程序页面的左上角获得。小程序应用密钥的获取方法请参看 文档。使用 RSA密钥生成工具 生成 PKCS1 格式的私钥、公钥后,将私钥内容存入 private-key.pem 文件中,再将公钥设置到开放平台,最后将生成的支付宝公钥内容存入 public-key.pem 中。
此外,home.js 中还有个变量 DEMO_DOMAIN 需要修改为云应用服务所在的域名或 IP 地址。
重要:要使用小程序服务端 SDK 获取用户信息,小程序 > 开发管理 > 功能列表 中,必须添加 获取会员信息 功能。
后端代码介绍
下面对后端代码做简单介绍。
文件 app/router.js 中定义了 url 路由。
module.exports = app => {
const { router, controller } = app;
router.get('/', controller.home.index);
router.get('/todos', controller.home.getTodos);
router.get('/users', controller.home.getUserInfo);
router.post('/todos/delete', controller.home.deleteTodo);
router.post('/todos/change', controller.home.changeState);
router.post('/todos/add', controller.home.addTodo);
router.post('/upload', controller.home.upload);
};
可以看到,后端服务的各接口都路由到了 app/controller/home.js 文件中的各个方法。
其中,方法 getUserInfo 拿到前端传过来的 auth code,通过 AlipaySdk 拿到 access token 后再通过 AlipaySdk 获得用户的信息。
async getUserInfo() {
// 拿到前端传过来的auth code
const authcode = this.ctx.request.query.authcode;
// 创建AlipaySdk对象
const alipaySdk = new AlipaySdk({
appId: 'fill in your app ID',
privateKey: fs.readFileSync('./private-key.pem', 'ascii'),
alipayPublicKey: fs.readFileSync('./public-key.pem', 'ascii'),
});
// 调用alipay.system.oauth.token方法,用auth code 换取 access token
const authMethod = 'alipay.system.oauth.token';
const authParams = {
grant_type: 'authorization_code',
code: authcode,
};
try {
const authResult = await alipaySdk.exec(authMethod, authParams);
// 调用alipay.user.info.share方法,用access token 拿到用户信息
const userMethod = 'alipay.user.info.share';
const userParams = {
auth_token: authResult.accessToken,
}
const result = await alipaySdk.exec(userMethod, userParams);
// 将用户信息写入返回的消息体中,返回给前端
this.ctx.body = result;
} catch (err) {
console.log('get user info err>>>>>', err);
}
}
方法 getTodos 从数据库中取得某个用户的所有 todo 项,并返回给前端。MySQL 数据库访问使用了插件 egg-mysql,插件具体用法请参看 文档。
async getTodos() {
// 从url的query中取得userId
var userId = this.ctx.query.userId;
var tasks;
if (userId) {
// 如果前端有传userId,则向数据库请求该userId下的所有task
tasks = await this.app.mysql.select('task', {
where: {user_id: userId}
});
} else {
// 如果前端没有传userId,则返回数据库中所有的task
tasks = await this.app.mysql.select('task');
}
// 将task转换成前端所需的格式
const todos = tasks.map(item => {return {
text: item.label,
completed: (item.done > 0 ? true : false),
id: item.id,
iconUrl: item.img_url
}});
// 将todo项写入消息体,返回给前端
this.ctx.body = {
success: true,
todoList: todos
};
}
方法 upload 接收前端上传的文件,并存到默认的静态资源目录中(app/public)。
async upload() {
// 从请求中获取文件流
const { ctx } = this;
const stream = await ctx.getFileStream();
// 生成文件名( UPLOAD_DIR 为存储上传图片的文件夹,该文件夹必须存在)
var fileId = uuid.v1() + path.extname(stream.filename);
const name = UPLOAD_DIR + '/' + fileId;
try {
// 处理文件
stream.pipe(fs.createWriteStream(name));
} catch(err) {
console.log('pipe error!', err);
// 将上传的文件流消费掉,不然浏览器响应会卡死
await sendToWormhole(stream);
throw err;
}
// 返回上传图片的访问地址,DEMO_DOMAIN 为云应用域名
ctx.body = {
imgUrl: 'https://' + DEMO_DOMAIN +'/public/' + fileId,
};
}
开发小程序前端界面
关于 Todo App 前端代码的详细介绍参见 文档。前端代码结构如下图。
其中 app.js 文件中的变量 demoDomain 需要修改为您自己的小程序云应用的域名,该域名可以从 云应用详情 页的 域名 页签中复制得到。
发布部署应用
点开 云服务 右边的菜单,点击 上传服务端代码。
上传及发布过程中,可以通过 查看日志 关注部署进度。
部署完成后,在小程序开发者工具中编译应用,即可查看程序运行效果。
测试完成后,点击页面右上角的 上传 按钮将小程序上传到开放平台,也可以点击 预览,用手机支付宝 APP 扫码预览在手机上的真实效果。若要在手机上向服务器发送请求,您还需要到 小程序管理 页面 > 设置 > httpRequest 接口请求域名白名单,把 HTTP 请求的域名录入进去。
上传完毕后,登录到开放平台提交审核。审核完毕后,小程序即可进行发布操作了。