迁移到适用于 Azure Functions 的 Node.js 编程模型版本 4

本文讨论 Node.js 编程模型版本 3 和版本 4 之间的差异,以及如何升级现有 v3 应用。 如果要创建新的 v4 应用而不是升级现有 v3 应用,请参阅面向 Visual Studio Code (VS Code)Azure Functions Core Tools 的教程。 本文使用“提示”提醒来突出显示升级应用时应采取的最重要的具体操作。

版本 4 旨在为 Node.js 开发人员提供以下权益:

  • 为 Node.js 开发人员提供熟悉且直观的体验。
  • 通过支持完全自定义,增强文件结构的灵活性。
  • 切换到以代码为中心的方法来定义函数配置。

注意事项

注意

Node.js 编程模型的版本 4 目前为公共预览版。

  • 在预览期间,v4 模型要求将应用设置 AzureWebJobsFeatureFlags 设为 EnableWorkerIndexing。 有关详细信息,请参阅启用 v4 编程模型
  • 不应将 Node.js“编程模型”与 Azure Functions“运行时”混淆。
    • 编程模型:定义代码的创作方式,并特定于 JavaScript 和 TypeScript。
    • 运行时:定义 Azure Functions 的基础行为,并在所有语言之间共享。
  • 编程模型版本严格绑定到 @azure/functions npm 包的版本,并且独立于运行时进行版本控制。 运行时和编程模型都使用“4”作为其最新主版本,但这纯粹是巧合。
  • 请记住,不能在同一函数应用中混用 v3 和 v4 编程模型。 在应用中注册一个 v4 函数后,function.json 文件中注册的任何 v3 函数都将被忽略。

要求

Node.js 编程模型的版本 4 需要以下最低版本:

加入 npm 包

在 v4 中,@azure/functions npm 包中包含了支持 Node.js 编程模型的主要源代码。 在以前的版本中,该代码直接在 Azure 中提供,而 npm 包仅具有 TypeScript 类型。 现在,需要将此包加入 TypeScript 和 JavaScript 应用中。 你可以加入现有 v3 应用的包,但这不是必需的。

提示

确保 @azure/functions 包在 package.json 文件的 dependencies 部分(而不是 devDependencies)中列出。 可以使用以下命令安装 v4:

npm install @azure/functions

设置应用入口点

在编程模型的 v4 中,可以根据需要构建代码。 在应用的根目录中,仅需要 host.jsonpackage.json 文件。

否则,可以通过在 package.json 文件中设置 main 字段来定义文件结构。 可以使用 glob 模式main 字段设为单个文件或多个文件。 下表所示为 main 字段的示例值:

示例 说明
src/index.js 从单个根文件注册函数。
src/functions/*.js 从其自己的文件中注册每个函数。
src/{index.js,functions/*.js} 从其自己的文件中注册每个函数的组合,但仍有一个常规应用级代码的根文件。
示例 说明
dist/src/index.js 从单个根文件注册函数。
dist/src/functions/*.js 从其自己的文件中注册每个函数。
dist/src/{index.js,functions/*.js} 从其自己的文件中注册每个函数的组合,但仍有一个常规应用级代码的根文件。

提示

请确保在 package.json 文件中定义 main 字段。

切换参数的顺序

现在,触发器输入(而不是调用上下文)是函数处理程序的第一个参数。 调用上下文(现在是第二个参数)已在 v4 中简化,并且不像触发器输入一样必需。 如果不使用它,则可以将其禁用。

提示

切换参数的顺序。 例如,如果你使用的是 HTTP 触发器,可将 (context, request) 切换为 (request, context),或只切换为 (request)(如果未使用上下文)。

在代码中定义函数

你不再需要创建和维护这些单独的 function.json 配置文件。 现在,可以直接在 TypeScript 或 JavaScript 文件中完全定义函数。 此外,许多属性现在具有默认值,因此无需每次都指定它们。

const { app } = require('@azure/functions');

app.http('httpTrigger1', {
    methods: ['GET', 'POST'],
    authLevel: 'anonymous',
    handler: async (request, context) => {
        context.log(`Http function processed request for url "${request.url}"`);

        const name = request.query.get('name') || (await request.text()) || 'world';

        return { body: `Hello, ${name}!` };
    },
});
import { app, HttpRequest, HttpResponseInit, InvocationContext } from '@azure/functions';

export async function httpTrigger1(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
    context.log(`Http function processed request for url "${request.url}"`);

    const name = request.query.get('name') || (await request.text()) || 'world';

    return { body: `Hello, ${name}!` };
}

app.http('httpTrigger1', {
    methods: ['GET', 'POST'],
    authLevel: 'anonymous',
    handler: httpTrigger1,
});

提示

将配置从 function.json 文件中移动到代码。 触发器的类型将与新模型中 app 对象上的方法相对应。 例如,如果在 function.json 中使用 httpTrigger 类型,则可在代码中调用 app.http() 以注册函数。 如果使用 timerTrigger,则调用 app.timer()

查看上下文的使用情况

在 v4 中,已对 context 对象进行简化,以减少重复并简化单元测试的编写。 例如,我们简化了主输入和输出,以便仅将其作为函数处理程序的参数和返回值进行访问。

不能再访问 context 对象上的主输入和输出,但仍必须访问 context 对象上的辅助输入和输出。 有关辅助输入和输出的详细信息,请参阅 Node.js 开发人员指南

获取主输入作为参数

主输入也称为触发器,并且是唯一必需的输入或输出。 必须有(且仅有)一个触发器。

版本 4 仅支持一种获取触发器输入的方法,作为第一个参数:

async function httpTrigger1(request, context) {
  const onlyOption = request;
async function httpTrigger1(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
  const onlyOption = request;

提示

请确保未使用 context.reqcontext.bindings 来获取输入。

将主要输出设置为返回值

版本 4 仅支持一种通过返回值设置主输出的方法:

return { 
  body: `Hello, ${name}!` 
};
async function httpTrigger1(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
    // ...
    return { 
      body: `Hello, ${name}!` 
    };
}

提示

请确保始终在函数处理程序中返回输出,而不是使用 context 对象对其进行设置。

上下文日志记录

在 v4 中,日志记录方法已移动到根 context 对象,如以下示例所示。 有关日志记录的详细信息,请参阅 Node.js 开发人员指南

context.log('This is an info log');
context.error('This is an error');
context.warn('This is an error');

创建测试上下文

版本 3 不支持在 Azure Functions 运行时外部创建调用上下文,因此创作单元测试可能很困难。 版本 4 允许创建调用上下文的实例,但测试期间的信息并不详细,除非你自行添加。

const testInvocationContext = new InvocationContext({
  functionName: 'testFunctionName',
  invocationId: 'testInvocationId'
});

查看 HTTP 类型的使用情况

现在,HTTP 请求和响应类型是提取标准的一部分。 它们不再是 Azure Functions 所独有的。

这些类型将在 Node.js 中使用 undici 包。 此包遵循提取标准,并且目前将要集成到 Node.js 核心中。

HttpRequest

  • 正文。 可以使用特定于要接收的类型的方法来访问正文:

    const body = await request.text();
    const body = await request.json();
    const body = await request.formData();
    const body = await request.arrayBuffer();
    const body = await request.blob();
    
  • 标头

    const header = request.headers.get('content-type');
    
  • 查询参数

    const name = request.query.get('name');
    

HttpResponse

  • 状态

    return { status: 200 };
    
  • 正文:

    使用 body 属性返回大多数类型,例如 stringBuffer

    return { body: "Hello, world!" };
    

    使用属性 jsonBody 获取返回 JSON 响应的最简单方法:

    return { jsonBody: { hello: "world" } };
    
  • 标头。 可以通过两种方法设置标头,具体取决于使用的是 HttpResponse 类还是 HttpResponseInit 接口:

    const response = new HttpResponse();
    response.headers.set('content-type', 'application/json');
    return response;
    
    return {
      headers: { 'content-type': 'application/json' }
    };
    

提示

使用 HTTP 请求或响应类型更新任何逻辑,以匹配新的方法。

提示

使用 HTTP 请求或响应类型更新任何逻辑,以匹配新的方法。 应该会收到 TypeScript 生成错误,以帮助你确定是否使用了旧方法。

疑难解答

请参阅 Node.js 故障排除指南