如何使用适用于 Azure 移动应用的 Node.js SDK
注意
此产品已停用。 有关使用 .NET 8 或更高版本的项目的替换,请参阅 Community Toolkit Datasync 库。
本文提供了详细信息和示例,演示如何使用 Azure 移动应用的 NodeJS 后端。
介绍
Azure 移动应用提供向 Web 应用程序添加移动优化数据访问 Web API 的功能。 Azure 移动应用 SDK 适用于 ASP.NET Framework 和 Node.js Web 应用程序。 SDK 提供以下操作:
- 用于数据访问的表操作(读取、插入、更新、删除)
- 自定义 API 操作
这两项操作都提供 Azure 应用服务允许的所有标识提供者的身份验证。 这些提供商包括 Facebook、Twitter、Google 和 Microsoft 等社交标识提供者,以及企业标识的 Microsoft Entra ID。
支持的平台
Azure 移动应用 Node.js SDK 支持 Node 6.x 及更高版本,并且已测试到 Node 12.x。 其他版本的 Node 可能正常工作,但不受支持。
Azure 移动应用 Node.js SDK 支持两个数据库驱动程序:
- node-mssql 驱动程序支持 Azure SQL 数据库和本地 SQL Server 实例。
- sqlite3 驱动程序仅支持单个实例上的 SQLite 数据库。
使用命令行创建基本节点后端
每个 Azure 移动应用 Node.js 后端都以 Express 应用程序的形式启动。 Express 是可用于 Node.js的最常用 Web 服务框架。 可以按如下所示创建基本 Express 应用程序:
在命令或 PowerShell 窗口中,为项目创建目录:
$ mkdir basicapp
运行
npm init
以初始化包结构:$ cd basicapp $ npm init
npm init
命令会询问一组初始化项目的问题。 请参阅示例输出:从 npm 存储库安装
express
和azure-mobile-apps
库:npm install --save express azure-mobile-apps
创建
app.js
文件来实现基本移动服务器:var express = require('express'), azureMobileApps = require('azure-mobile-apps'); var app = express(), mobile = azureMobileApps(); // Define a TodoItem table. mobile.tables.add('TodoItem'); // Add the Mobile API so it is accessible as a Web API. app.use(mobile); // Start listening on HTTP. app.listen(process.env.PORT || 3000);
此应用程序创建具有单个终结点(/tables/TodoItem
)的移动优化 Web API,该终结点使用动态架构提供对基础 SQL 数据存储的未经身份验证的访问。 它适用于以下客户端库快速入门:
可以在 GitHub
为应用程序启用主页
许多应用程序是 Web 和移动应用的组合。 可以使用 Express 框架合并这两个方面。 但是,有时你可能只想实现移动接口。 提供主页以确保应用服务已启动并运行非常有用。 可以提供自己的主页,也可以启用临时主页。 若要启用临时主页,请使用以下代码实例化 Azure 移动应用:
var mobile = azureMobileApps({ homePage: true });
如果只想在本地开发时提供此选项,则可以将此设置添加到 azureMobile.js
配置文件:
module.exports = {
homePage: true,
};
可以根据需要将其他设置添加到 azureMobile.js 文件。
表操作
azure-mobile-apps Node.js Server SDK 提供了一种机制,用于将存储在 Azure SQL 数据库中的数据表公开为 Web API。 它提供五个操作:
操作 | 描述 |
---|---|
GET /tables/tablename |
获取表中的所有记录。 |
GET /tables/tablename/:id |
获取表中的特定记录。 |
POST /tables/tablename |
在表中创建记录。 |
PATCH /tables/tablename/:id |
更新表中的记录。 |
DELETE /tables/tablename/:id |
删除表中的记录。 |
此 Web API 支持 OData v3,并扩展表架构以支持 脱机数据同步。
使用动态架构定义表
必须先定义表,然后才能使用表。 可以使用静态架构(在其中定义架构中的列)或动态(SDK 基于传入请求控制架构)来定义表。 此外,还可以通过将 JavaScript 代码添加到定义来控制 Web API 的特定方面。
最佳做法是,应在 tables
目录中的 JavaScript 文件中定义每个表,然后使用 tables.import()
方法导入表。 扩展基本应用示例后,可以调整 app.js 文件:
var express = require('express'),
azureMobileApps = require('azure-mobile-apps');
var app = express(),
mobile = azureMobileApps();
// Define the database schema that is exposed.
mobile.tables.import('./tables');
// Provide initialization of any tables that are statically defined.
mobile.tables.initialize().then(function () {
// Add the Mobile API so it is accessible as a Web API.
app.use(mobile);
// Start listening on HTTP.
app.listen(process.env.PORT || 3000);
});
在 ./tables/TodoItem.js中定义表:
var azureMobileApps = require('azure-mobile-apps');
var table = azureMobileApps.table();
// Additional configuration for the table goes here.
module.exports = table;
表默认使用动态架构。
使用静态架构定义表
可以显式定义要通过 Web API 公开的列。 azure-mobile-apps Node.js SDK 会自动将脱机数据同步所需的任何额外列添加到所提供的列表中。 例如,快速入门客户端应用程序需要一个包含两列的表:text
(字符串)和 complete
(布尔值)。 可以在表定义 JavaScript 文件(位于 tables
目录中)中定义该表,如下所示:
var azureMobileApps = require('azure-mobile-apps');
var table = azureMobileApps.table();
// Define the columns within the table.
table.columns = {
"text": "string",
"complete": "boolean"
};
// Turn off the dynamic schema.
table.dynamicSchema = false;
module.exports = table;
如果静态定义表,则还必须调用 tables.initialize()
方法,以在启动时创建数据库架构。
tables.initialize()
方法返回一个 承诺,以便 Web 服务在初始化数据库之前不处理请求。
在本地计算机上使用 SQL Server Express 作为开发数据存储
Azure 移动应用 Node.js SDK 提供三个选项用于现现提供数据:
- 使用 内存 驱动程序提供非持久性示例存储。
- 使用 mssql 驱动程序提供用于开发的 SQL Server Express 数据存储。
- 使用 mssql 驱动程序为生产提供 Azure SQL 数据库数据存储。
Azure 移动应用 Node.js SDK 使用 mssql Node.js 包 建立和使用与 SQL Server Express 和 SQL 数据库的连接。 此包要求在 SQL Server Express 实例上启用 TCP 连接。
提示
内存驱动程序不提供一组完整的测试设施。 如果要在本地测试后端,建议使用 SQL Server Express 数据存储和 mssql 驱动程序。
运行 Configuration Manager:
- 展开树菜单中 SQL Server 网络配置 节点。
- 为
实例名称 选择协议。 - 右键单击
TCP/IP ,然后选择“启用。 在弹出对话框中选择“确定”“。 - 在树菜单中选择 SQL Server Services。
- 右键单击 SQL Server(实例名称) 并选择 重启。
- 关闭 Configuration Manager。
还必须创建 Azure 移动应用可用于连接到数据库的用户名和密码。 确保创建的用户具有 dbcreator
服务器角色。 有关配置用户的详细信息,请参阅 SQL Server 文档
请务必记录所选用户名和密码。 可能需要根据数据库要求分配更多服务器角色或权限。
Node.js 应用程序读取此数据库的连接字符串的 SQLCONNSTR_MS_TableConnectionString
环境变量。 可以在环境中设置此变量。 例如,可以使用 PowerShell 设置此环境变量:
$env:SQLCONNSTR_MS_TableConnectionString = "Server=127.0.0.1; Database=mytestdatabase; User Id=azuremobile; Password=T3stPa55word;"
通过 TCP/IP 连接访问数据库。 提供连接的用户名和密码。
配置项目以便进行本地开发
Azure 移动应用从本地文件系统读取名为 azureMobile.js
的 JavaScript 文件。 请勿使用此文件在生产环境中配置 Azure 移动应用 SDK。 而是在 Azure 门户中使用
azureMobile.js 文件应导出配置对象。 最常见的设置包括:
- 数据库设置
- 诊断日志记录设置
- 备用 CORS 设置
此示例 azureMobile.js 文件实现上述数据库设置:
module.exports = {
cors: {
origins: [ 'localhost' ]
},
data: {
provider: 'mssql',
server: '127.0.0.1',
database: 'mytestdatabase',
user: 'azuremobile',
password: 'T3stPa55word'
},
logging: {
level: 'verbose'
}
};
建议将 azureMobile.js
添加到 .gitignore
文件(或其他源代码控制忽略文件),以防止密码存储在云中。
为移动应用配置应用设置
应用设置 | azureMobile.js 设置 | 描述 | 有效值 |
---|---|---|---|
MS_MobileAppName | 名字 | 应用的名称 | 字符串 |
MS_MobileLoggingLevel | logging.level | 要记录的消息的最低日志级别 | error, warning, info, verbose, debug, silly |
MS_DebugMode | 调试 | 启用或禁用调试模式 | true、false |
MS_TableSchema | data.schema | SQL 表的默认架构名称 | string (默认值:dbo) |
MS_DynamicSchema | data.dynamicSchema | 启用或禁用调试模式 | true、false |
MS_DisableVersionHeader | version (设置为 undefined) | 禁用 X-ZUMO-Server-Version 标头 | true、false |
MS_SkipVersionCheck | skipversioncheck | 禁用客户端 API 版本检查 | true、false |
更改大多数应用设置需要重启服务。
使用 Azure SQL 作为生产数据存储
将 Azure SQL 数据库用作数据存储在所有 Azure 应用服务应用程序类型中都是相同的。 如果尚未这样做,请按照以下步骤创建 Azure 应用服务后端。 创建 Azure SQL 实例,然后将应用设置 SQLCONNSTR_MS_TableConnectionString
设置为要使用的 Azure SQL 实例的连接字符串。 确保运行后端的 Azure 应用服务可以与 Azure SQL 实例通信。
需要身份验证才能访问表
若要将应用服务身份验证与 tables
终结点配合使用,必须先 在 Azure 门户中配置应用服务身份验证。 有关详细信息,请参阅要使用的标识提供者的配置指南:
每个表都有一个访问属性,可用于控制对表的访问。 以下示例显示了一个静态定义的表,其中包含所需的身份验证。
var azureMobileApps = require('azure-mobile-apps');
var table = azureMobileApps.table();
// Define the columns within the table.
table.columns = {
"text": "string",
"complete": "boolean"
};
// Turn off the dynamic schema.
table.dynamicSchema = false;
// Require authentication to access the table.
table.access = 'authenticated';
module.exports = table;
访问属性可以采用以下三个值之一:
- 匿名 表示允许客户端应用程序在没有身份验证的情况下读取数据。
- 经过身份验证的 指示客户端应用程序必须使用请求发送有效的身份验证令牌。
- 禁用 表示此表当前已禁用。
如果未定义访问属性,则允许未经身份验证的访问。
对表使用身份验证声明
可以设置设置身份验证时请求的各种声明。 这些声明通常无法通过 context.user
对象提供。 但是,可以使用 context.user.getIdentity()
方法检索它们。
getIdentity()
方法返回解析为对象的承诺。 该对象由身份验证方法(facebook
、google
、twitter
、microsoftaccount
或 aad
)进行密钥。
注意
如果通过 Microsoft Entra ID 使用Microsoft身份验证,则身份验证方法 aad
,而不是 microsoftaccount
。
例如,如果设置了 Microsoft Entra 身份验证并请求电子邮件地址声明,则可以使用下表控制器将电子邮件地址添加到记录中:
var azureMobileApps = require('azure-mobile-apps');
// Create a new table definition.
var table = azureMobileApps.table();
table.columns = {
"emailAddress": "string",
"text": "string",
"complete": "boolean"
};
table.dynamicSchema = false;
table.access = 'authenticated';
/**
* Limit the context query to those records with the authenticated user email address
* @param {Context} context the operation context
* @returns {Promise} context execution Promise
*/
function queryContextForEmail(context) {
return context.user.getIdentity().then((data) => {
context.query.where({ emailAddress: data.aad.claims.emailaddress });
return context.execute();
});
}
/**
* Adds the email address from the claims to the context item - used for
* insert operations
* @param {Context} context the operation context
* @returns {Promise} context execution Promise
*/
function addEmailToContext(context) {
return context.user.getIdentity().then((data) => {
context.item.emailAddress = data.aad.claims.emailaddress;
return context.execute();
});
}
// Configure specific code when the client does a request.
// READ: only return records that belong to the authenticated user.
table.read(queryContextForEmail);
// CREATE: add or overwrite the userId based on the authenticated user.
table.insert(addEmailToContext);
// UPDATE: only allow updating of records that belong to the authenticated user.
table.update(queryContextForEmail);
// DELETE: only allow deletion of records that belong to the authenticated user.
table.delete(queryContextForEmail);
module.exports = table;
若要查看可用的声明,请使用 Web 浏览器查看网站的 /.auth/me
终结点。
禁用对特定表操作的访问
除了显示在表上之外,访问属性还可用于控制单个操作。 有四个操作:
-
read
是表上的 RESTful GET 操作。 -
insert
是表上的 RESTful POST 操作。 -
update
是表上的 RESTful PATCH 操作。 -
delete
是表上的 RESTful DELETE 操作。
例如,你可能想要提供只读未经身份验证的表:
var azureMobileApps = require('azure-mobile-apps');
var table = azureMobileApps.table();
// Read-only table. Only allow READ operations.
table.read.access = 'anonymous';
table.insert.access = 'disabled';
table.update.access = 'disabled';
table.delete.access = 'disabled';
module.exports = table;
调整用于表操作的查询
表操作的一个常见要求是提供数据的受限视图。 例如,可以提供使用经过身份验证的用户 ID 标记的表,以便只能读取或更新自己的记录。 下表定义提供此功能:
var azureMobileApps = require('azure-mobile-apps');
var table = azureMobileApps.table();
// Define a static schema for the table.
table.columns = {
"userId": "string",
"text": "string",
"complete": "boolean"
};
table.dynamicSchema = false;
// Require authentication for this table.
table.access = 'authenticated';
// Ensure that only records for the authenticated user are retrieved.
table.read(function (context) {
context.query.where({ userId: context.user.id });
return context.execute();
});
// When adding records, add or overwrite the userId with the authenticated user.
table.insert(function (context) {
context.item.userId = context.user.id;
return context.execute();
});
module.exports = table;
通常运行查询的操作具有可以使用 where
子句进行调整的查询属性。 查询属性是一个 QueryJS 对象,该对象用于将 OData 查询转换为数据后端可以处理的内容。 对于简单的相等情况(如前面的情况),可以使用地图。 还可以添加特定的 SQL 子句:
context.query.where('myfield eq ?', 'value');
在表上配置软删除
软删除实际上不会删除记录。 而是通过将已删除的列设置为 true,将其标记为在数据库中删除。 除非移动客户端 SDK 使用 includeDeleted()
,否则 Azure 移动应用 SDK 会自动从结果中删除软删除的记录。 若要为软删除配置表,请在表定义文件中设置 softDelete
属性:
var azureMobileApps = require('azure-mobile-apps');
var table = azureMobileApps.table();
// Define the columns within the table.
table.columns = {
"text": "string",
"complete": "boolean"
};
// Turn off the dynamic schema.
table.dynamicSchema = false;
// Turn on soft delete.
table.softDelete = true;
// Require authentication to access the table.
table.access = 'authenticated';
module.exports = table;
建立永久删除记录的机制,例如客户端应用程序、WebJob、Azure 函数或自定义 API。
使用数据为数据库设定种子
创建新应用程序时,可能需要为包含数据的表设定种子。 可以在表定义 JavaScript 文件中设定数据种子,如下所示:
var azureMobileApps = require('azure-mobile-apps');
var table = azureMobileApps.table();
// Define the columns within the table.
table.columns = {
"text": "string",
"complete": "boolean"
};
table.seed = [
{ text: 'Example 1', complete: false },
{ text: 'Example 2', complete: true }
];
// Turn off the dynamic schema.
table.dynamicSchema = false;
// Require authentication to access the table.
table.access = 'authenticated';
module.exports = table;
仅当使用 Azure 移动应用 SDK 创建表时,才会对数据进行种子设定。 如果该表已存在于数据库中,则不会向表中注入任何数据。 如果启用动态架构,则会从种子数据推断架构。
建议显式调用 tables.initialize()
方法,以在服务开始运行时创建表。
启用 Swagger 支持
Azure 移动应用附带内置 Swagger 支持。 若要启用 Swagger 支持,请先将 swagger-ui 安装为依赖项:
npm install --save swagger-ui
然后,可以在 Azure 移动应用构造函数中启用 Swagger 支持:
var mobile = azureMobileApps({ swagger: true });
你可能只想在开发版本中启用 Swagger 支持。 可以使用 NODE_ENV
应用设置在开发中启用 Swagger 支持:
var mobile = azureMobileApps({ swagger: process.env.NODE_ENV !== 'production' });
swagger
终结点位于 http://站点.azurewebsites.net/swagger。 可以通过 /swagger/ui
终结点访问 Swagger UI。 如果选择在整个应用程序中要求进行身份验证,Swagger 将生成错误。 为获得最佳结果,请选择在 Azure 应用服务身份验证/授权设置中允许未经身份验证的请求,然后使用 table.access
属性控制身份验证。
如果只想在本地开发 Swagger 支持,还可以将 Swagger 选项添加到 azureMobile.js
文件中。
自定义 API
除了通过 /tables
终结点访问数据访问 API 外,Azure 移动应用还可以提供自定义 API 覆盖范围。 自定义 API 的定义方式与表定义类似,可以访问所有相同的设施,包括身份验证。
定义自定义 API
自定义 API 的定义方式与表 API 大致相同:
- 创建
api
目录。 - 在
api
目录中创建 API 定义 JavaScript 文件。 - 使用导入方法导入
api
目录。
下面是基于我们之前使用的基本应用示例的原型 API 定义:
var express = require('express'),
azureMobileApps = require('azure-mobile-apps');
var app = express(),
mobile = azureMobileApps();
// Import the custom API.
mobile.api.import('./api');
// Add the Mobile API so it is accessible as a Web API.
app.use(mobile);
// Start listening on HTTP
app.listen(process.env.PORT || 3000);
让我们使用 Date.now()
方法返回服务器日期的示例 API。 下面是 api/date.js
文件:
var api = {
get: function (req, res, next) {
var date = { currentTime: Date.now() };
res.status(200).type('application/json').send(date);
});
};
module.exports = api;
每个参数都是标准 RESTful 谓词之一:GET、POST、PATCH 或 DELETE。 此方法是发送所需输出的标准 ExpressJS 中间件 函数。
需要身份验证才能访问自定义 API
Azure 移动应用 SDK 以相同的方式为 tables
终结点和自定义 API 实现身份验证。 若要将身份验证添加到上一部分中开发的 API,请添加 access
属性:
var api = {
get: function (req, res, next) {
var date = { currentTime: Date.now() };
res.status(200).type('application/json').send(date);
});
};
// All methods must be authenticated.
api.access = 'authenticated';
module.exports = api;
还可以指定特定操作的身份验证:
var api = {
get: function (req, res, next) {
var date = { currentTime: Date.now() };
res.status(200).type('application/json').send(date);
}
};
// The GET methods must be authenticated.
api.get.access = 'authenticated';
module.exports = api;
用于 tables
终结点的同一令牌必须用于需要身份验证的自定义 API。
处理大型文件上传
Azure 移动应用 SDK 使用 正文分析器中间件 接受和解码提交中的正文内容。 可以预配置正文分析器以接受更大的文件上传:
var express = require('express'),
bodyParser = require('body-parser'),
azureMobileApps = require('azure-mobile-apps');
var app = express(),
mobile = azureMobileApps();
// Set up large body content handling.
app.use(bodyParser.json({ limit: '50mb' }));
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
// Import the custom API.
mobile.api.import('./api');
// Add the Mobile API so it is accessible as a Web API.
app.use(mobile);
// Start listening on HTTP.
app.listen(process.env.PORT || 3000);
该文件在传输之前进行 base-64 编码。 此编码会增加实际上传的大小(以及必须考虑的大小)。
执行自定义 SQL 语句
Azure 移动应用 SDK 允许通过请求对象访问整个上下文。 可以轻松地对定义的数据提供程序执行参数化 SQL 语句:
var api = {
get: function (request, response, next) {
// Check for parameters. If not there, pass on to a later API call.
if (typeof request.params.completed === 'undefined')
return next();
// Define the query. Anything that the mssql
// driver can handle is allowed.
var query = {
sql: 'UPDATE TodoItem SET complete=@completed',
parameters: [{
completed: request.params.completed
}]
};
// Execute the query. The context for Azure Mobile Apps is available through
// request.azureMobile. The data object contains the configured data provider.
request.azureMobile.data.execute(query)
.then(function (results) {
response.json(results);
});
}
};
api.get.access = 'authenticated';
module.exports = api;
调试
调试、诊断和排查 Azure 移动应用问题
Azure 应用服务为 Node.js 应用程序提供了多种调试和故障排除技术。 若要开始排查 Node.js Azure 移动应用后端问题,请参阅以下文章:
- 监视 Azure 应用服务
- 在 Azure 应用服务 中启用诊断日志记录
- 排查 Azure 应用服务 上的节点应用程序问题
Node.js 应用程序可以访问各种诊断日志工具。 在内部,Azure 移动应用 Node.js SDK 使用 [Winston] 进行诊断日志记录。 启用调试模式或在 Azure 门户中将 MS_DebugMode
应用设置设置为 true 时,会自动启用日志记录。 生成的日志显示在 Azure 门户中的诊断日志中。