注意
此产品已停用。 有关使用 .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 数据存储的未经身份验证的访问。 它适用于以下客户端库快速入门:
为应用程序启用主页
许多应用程序是 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 驱动程序。
Microsoft SQL Server 2019 Developer 下载并安装。
运行 Configuration Manager:
- 展开树菜单中 SQL Server 网络配置 节点。
- 为 实例名称选择协议。
- 右键单击 TCP/IP,然后选择“启用 。 在弹出对话框中选择“确定”“。
- 在树菜单中选择 SQL Server Services。
- 右键单击 SQL Server(实例名称) 并选择 重启。
- 关闭 Configuration Manager。
还必须创建 Azure 移动应用可用于连接到数据库的用户名和密码。 确保创建的用户具有 dbcreator
服务器角色。 有关配置用户的详细信息,请参阅 SQL Server 文档
请务必记录所选用户名和密码。 可能需要根据数据库要求分配更多服务器角色或权限。
警告
使用用户名、密码或访问密钥管理连接时请小心。 不应将这些机密提交到源代码管理中,也不应放置在可能由意外用户访问的不安全位置。 在本地开发期间,通常会连接到不需要存储机密或直接连接到 Azure 的本地数据库。 在生产期间,尽可能使用无机密方法(例如 Microsoft Entra ID) 连接到 Azure SQL 数据库。
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
文件中的大多数设置在 azure 门户 具有等效的应用设置。 使用以下列表在 应用设置中配置应用:
应用设置 | 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 门户中的诊断日志中。