云应用-Node.js版
本文介绍如何基于 NodeJS 技术栈创建并部署一个简单的 Todo App 应用程序。
前提条件
操作步骤
整个操作流程分为以下 8 步:
创建小程序
- 登录 蚂蚁金服开放平台,选择 开发者中心 > 小程序 > 创建。
- 填写 基本信息,点击 创建 按钮,创建应用名为 示例应用 小程序。
说明:一个账号下最多可以创建10个小程序;未提交过审核的小程序可以删除,删除的小程序不在计数范围。
创建云应用后端服务
- 在 我的小程序 页面,选择刚创建的小程序,点击 查看,进入 开发管理 页面。
- 点击左侧导航栏的 云服务(公测),在 云服务列表 页面点击 创建云服务 > 创建云应用。
- 在 创建云应用 页面,选择 NodeJS** 技术栈,填入 应用名称 和 描述 (选填),点击 创建**。
构建环境
- 返回 云服务 页面,点击刚创建的云服务卡片中的 构建环境 按钮。
- 在 购买环境资源 页面,选择合适的环境配置方案,此处选择 小程序云应用入门(Mysql 版),点击 同意《产品服务协议》 > 确认配置。
说明:当前测试环境该方案免费提供,但若连续 7 日未部署过代码,环境会被自动回收。
- 在 确认订单 页面,点击 确认购买。购买成功后会自动进入 构建环境 页面。构建过程会耗时几分钟,构建成功后,您可以选择 查看应用详情 ,或者 返回应用列表 。
准备开发环境
打开小程序开发者工具,点击 新建项目。
在新建项目向导中,选择 小程序,选择 Todo App **示例,点击**下一步。
输入 项目名称,项目路径会自动填充,选择 云应用 为后端服务,点击 完成。
项目创建好后,进入开发界面。点击右上角的 登录 按钮,用支付宝扫码登录。
关联前面 创建的小程序 应用。
关联前面 构建的云应用环境,点击 确定。
数据准备
开发后端代码之前,需要先建立应用运行所需的数据库。
- 在 云应用详情 页面的 数据库 页签,点击查看**Web 控制台,登录后进入 PhpMyAdmin** 的数据库管理页面。
- 在 sample 数据库中,新建表 task,数据结构如下。请确保表结构与图中完全一致,否则程序可能无法运行,特别是如下几处:a. 字段名称;b. 字段类型;c. id字段为主键,且自增(A_I);d. done字段默认值为0;e. 各字段是否允许空值。
您也可以通过执行以下SQL建表。3.sql CREATE TABLE
4. 点击 保存,完成创建该示例程序所需的数据库表。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 地址。
- 在 server 目录下,运行命令
npm install && npm run dev
可在本地运行程序。
需要注意的是,为了使用小程序服务端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 请求的域名录入进去。
- 上传完毕后,登录到开放平台提交审核。审核完毕后,小程序即可进行发布操作了。