如何使用适用于 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 应用程序:

  1. 在命令或 PowerShell 窗口中,为项目创建目录:

    $ mkdir basicapp
    
  2. 运行 npm init 以初始化包结构:

    $ cd basicapp
    $ npm init
    

    npm init 命令会询问一组初始化项目的问题。 请参阅示例输出:

    npm init 输出

  3. 从 npm 存储库安装 expressazure-mobile-apps 库:

    npm install --save express azure-mobile-apps
    
  4. 创建 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 驱动程序。

  1. 下载并安装 Microsoft SQL Server 2019 Developer

  2. 运行 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 文件(或其他源代码控制忽略文件),以防止密码存储在云中。

为移动应用配置应用设置

文件中的大多数设置在 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() 方法返回解析为对象的承诺。 该对象由身份验证方法(facebookgoogletwittermicrosoftaccountaad)进行密钥。

注意

如果通过 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 大致相同:

  1. 创建 api 目录。
  2. api 目录中创建 API 定义 JavaScript 文件。
  3. 使用导入方法导入 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 移动应用后端问题,请参阅以下文章:

Node.js 应用程序可以访问各种诊断日志工具。 在内部,Azure 移动应用 Node.js SDK 使用 [Winston] 进行诊断日志记录。 启用调试模式或在 Azure 门户中将 MS_DebugMode 应用设置设置为 true 时,会自动启用日志记录。 生成的日志显示在 Azure 门户中的诊断日志中。