руководство разработчика Функции Azure Node.js

В этом руководстве описано, как разрабатывать Функции Azure с помощью JavaScript или TypeScript. В статье предполагается, что вы уже ознакомились с руководством разработчика Функции Azure.

Внимание

Содержимое этой статьи изменяется на основе выбранной модели программирования Node.js в селекторе в верхней части этой страницы. Выбранная версия должна соответствовать версии пакета npm, используемого @azure/functions в приложении. Если в списке package.jsonнет этого пакета, значение по умолчанию — версия 3. Дополнительные сведения о различиях между версиями 3 и 4 см. в руководстве по миграции.

Как разработчик Node.js, вы также можете быть заинтересованы в одной из следующих статей:

Начало работы Основные понятия Пошаговое изучение

Рекомендации

  • Модель программирования Node.js не должна путаться с средой выполнения Функции Azure:
    • Модель программирования: определяет способ разработки кода и зависит от JavaScript и TypeScript.
    • Среда выполнения. Определяет базовое поведение Функции Azure и совместно используется на всех языках.
  • Версия модели программирования строго привязана к версии @azure/functions пакета npm. Она является версией независимо от среды выполнения. Среда выполнения и модель программирования используют номер 4 в качестве последней основной версии, но это совпадение.
  • Нельзя смешивать модели программирования версии 3 и версии 4 в одном приложении-функции. После регистрации одной функции версии 4 в приложении все функции версии 3, зарегистрированные в function.json файлах, игнорируются.

Поддерживаемые версии

В следующей таблице показана каждая версия модели программирования Node.js вместе со своими поддерживаемыми версиями среды выполнения Функции Azure и Node.js.

Версия модели программирования Уровень поддержки Версия среды выполнения функций версия Node.js Description
4.x Общедоступная версия 4.25+ 20.x, 18.x Поддерживает гибкую структуру файлов и ориентированный на код подход к триггерам и привязкам.
3.x Общедоступная версия 4.x 20.x, 18.x, 16.x, 14.x Требуется определенная структура файлов с триггерами и привязками, объявленными в файле "function.json"
2.x Н/Д 3.x 14.x, 12.x, 10.x Достигнуто окончание поддержки 13 декабря 2022 г. Дополнительные сведения см . в версиях функций.
1.x Н/Д 2.x 10.x, 8.x Достигнуто окончание поддержки 13 декабря 2022 г. Дополнительные сведения см . в версиях функций.

Структура папок

Требуемая структура папок для проекта JavaScript выглядит следующим образом:

<project_root>/
 | - .vscode/
 | - node_modules/
 | - myFirstFunction/
 | | - index.js
 | | - function.json
 | - mySecondFunction/
 | | - index.js
 | | - function.json
 | - .funcignore
 | - host.json
 | - local.settings.json
 | - package.json

Основная папка проекта, <project_root>, может содержать следующие файлы:

  • VSCODE/: (Необязательно) Содержит хранимую конфигурацию Visual Studio Code. Дополнительные сведения см. в разделе параметров Visual Studio Code.
  • myFirstFunction/function.json: содержит конфигурацию триггера, входных данных и выходных данных функции. Имя каталога определяет имя функции.
  • myFirstFunction/index.js: хранит код функции. Чтобы изменить путь к файлу по умолчанию, ознакомьтесь с помощью scriptFile.
  • .funcignore: (необязательно) объявляет файлы, которые не должны публиковаться в Azure. Обычно этот файл содержит vscode/ , чтобы игнорировать параметры редактора, тест или игнорировать тестовые случаи и local.settings.json , чтобы предотвратить публикацию параметров локального приложения.
  • host.json: содержит параметры глобальной конфигурации, влияющие на все функции в экземпляре приложения-функции. Этот файл не публикуется в Azure. При локальном запуске поддерживаются не все параметры. Дополнительные сведения см. в разделе host.json.
  • local.settings.json. Используется для хранения параметров приложения и строка подключения при локальном запуске. Этот файл не публикуется в Azure. Дополнительные сведения см. в разделе local.settings.file.
  • package.json. Содержит параметры конфигурации, такие как список зависимостей пакета, основная точка входа и скрипты.

Рекомендуемая структура папок для проекта JavaScript выглядит следующим образом:

<project_root>/
 | - .vscode/
 | - node_modules/
 | - src/
 | | - functions/
 | | | - myFirstFunction.js
 | | | - mySecondFunction.js
 | - test/
 | | - functions/
 | | | - myFirstFunction.test.js
 | | | - mySecondFunction.test.js
 | - .funcignore
 | - host.json
 | - local.settings.json
 | - package.json

Основная папка проекта, <project_root>, может содержать следующие файлы:

  • VSCODE/: (Необязательно) Содержит хранимую конфигурацию Visual Studio Code. Дополнительные сведения см. в разделе параметров Visual Studio Code.
  • src/functions/: расположение по умолчанию для всех функций и связанных с ними триггеров и привязок.
  • test/: (необязательно) Содержит тестовые варианты приложения-функции.
  • .funcignore: (необязательно) объявляет файлы, которые не должны публиковаться в Azure. Обычно этот файл содержит vscode/ , чтобы игнорировать параметры редактора, тест или игнорировать тестовые случаи и local.settings.json , чтобы предотвратить публикацию параметров локального приложения.
  • host.json: содержит параметры глобальной конфигурации, влияющие на все функции в экземпляре приложения-функции. Этот файл не публикуется в Azure. При локальном запуске поддерживаются не все параметры. Дополнительные сведения см. в разделе host.json.
  • local.settings.json. Используется для хранения параметров приложения и строка подключения при локальном запуске. Этот файл не публикуется в Azure. Дополнительные сведения см. в разделе local.settings.file.
  • package.json. Содержит параметры конфигурации, такие как список зависимостей пакета, основная точка входа и скрипты.

Регистрация функции

Модель версии 3 регистрирует функцию на основе существования двух файлов. Во-первых, вам нужен файл, расположенный function.json в папке один уровень вниз от корневого каталога приложения. Во-вторых, вам нужен файл JavaScript, экспортируемый функцией. По умолчанию модель ищет index.js файл в той же папке, что и ваша function.json. Если вы используете TypeScript, необходимо использовать scriptFile свойство, function.json указывая на скомпилированный файл JavaScript. Чтобы настроить расположение файла или имя экспорта функции, ознакомьтесь с настройкой точки входа функции.

Экспортируемая функция всегда должна быть объявлена как в async function модели версии 3. Вы можете экспортировать синхронную функцию, но затем необходимо вызвать context.done() сигнал о завершении функции, которая устарела и не рекомендуется.

Функция передает вызов context в качестве первого аргумента и входные данные в качестве оставшихся аргументов.

В следующем примере показана простая функция, которая регистрирует, срабатывает ли она и отвечает с помощью Hello, world!:

{
  "bindings": [
    {
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "authLevel": "anonymous",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    }
  ]
}
module.exports = async function (context, request) {
    context.log('Http function was triggered.');
    context.res = { body: 'Hello, world!' };
};

Модель программирования загружает функции на main основе поля в вашей package.jsonмодели. Поле можно задать main для одного файла или нескольких файлов с помощью шаблона glOB-объектов. В следующей таблице показаны примеры значений main для поля:

Пример Description
src/index.js Зарегистрируйте функции из одного корневого файла.
src/functions/*.js Зарегистрируйте каждую функцию из собственного файла.
src/{index.js,functions/*.js} Сочетание, в котором вы регистрируете каждую функцию из собственного файла, но у вас по-прежнему есть корневой файл для общего кода на уровне приложений.

Чтобы зарегистрировать функцию, необходимо импортировать app объект из @azure/functions модуля npm и вызвать метод, характерный для типа триггера. Первый аргумент при регистрации функции — это имя функции. Второй аргумент — это объект, указывающий options конфигурацию для триггера, обработчика и любых других входных или выходных данных. В некоторых случаях, когда конфигурация триггера не требуется, обработчик можно передать непосредственно в качестве второго аргумента options вместо объекта.

Регистрация функции может выполняться из любого файла в проекте, если этот файл загружается (прямо или косвенно) на main основе поля в файле package.json . Функция должна быть зарегистрирована в глобальной области, так как после запуска выполнения невозможно зарегистрировать функции.

В следующем примере показана простая функция, которая регистрирует, срабатывает ли она и отвечает с помощью Hello, world!:

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

app.http('helloWorld1', {
    methods: ['POST', 'GET'],
    handler: async (request, context) => {
        context.log('Http function was triggered.');
        return { body: 'Hello, world!' };
    }
});

Входные и выходные данные

Функция требуется для того, чтобы именно один первичный вход был вызван триггером. Он также может иметь вторичные входные данные и /или выходные данные. Входные и выходные данные настраиваются в function.json файлах, а также называются привязками.

Входные данные

Входные данные — это привязки, для direction которых задано значение in. Основное различие между триггером и вторичным входным данными заключается в том, что type для триггера заканчивается тип и тип blobTrigger blob.Trigger Большинство функций используют триггер, а не многие вторичные типы входных данных поддерживаются.

Доступ к входным данным можно получить несколькими способами:

  • [Рекомендуется] В качестве аргументов, переданных функции: используйте аргументы в том же порядке, в который они определены function.json. Свойство name , определенное в function.json нем, не обязательно соответствует имени аргумента, хотя рекомендуется для организации.

    module.exports = async function (context, myTrigger, myInput, myOtherInput) { ... };
    
  • В качестве свойств context.bindings: используйте ключ, соответствующий свойству, определенному name в function.json.

    module.exports = async function (context) {
        context.log("This is myTrigger: " + context.bindings.myTrigger);
        context.log("This is myInput: " + context.bindings.myInput);
        context.log("This is myOtherInput: " + context.bindings.myOtherInput);
    };
    

Выходные данные

Выходные данные — это привязки с direction набором out и могут быть заданы несколькими способами:

  • [Рекомендуется для однократных выходных данных] Возвращает значение напрямую: если вы используете асинхронную функцию, вы можете вернуть это значение напрямую. Необходимо изменить name свойство выходной привязки $return function.json следующим образом:

    {
        "name": "$return",
        "type": "http",
        "direction": "out"
    }
    
    module.exports = async function (context, request) {
        return {
            body: "Hello, world!"
        };
    }
    
  • [Рекомендуется для нескольких выходных данных] Возвращает объект, содержащий все выходные данные: если вы используете асинхронную функцию, вы можете вернуть объект со свойством, соответствующим имени каждой привязки в вашей function.json. В следующем примере используются выходные привязки с именем httpResponse и queueOutput:

    {
        "name": "httpResponse",
        "type": "http",
        "direction": "out"
    },
    {
        "name": "queueOutput",
        "type": "queue",
        "direction": "out",
        "queueName": "helloworldqueue",
        "connection": "storage_APPSETTING"
    }
    
    module.exports = async function (context, request) {
        let message = 'Hello, world!';
        return {
            httpResponse: {
                body: message
            },
            queueOutput: message
        };
    };
    
  • Задайте значения context.bindingsв: если вы не используете асинхронную функцию или не хотите использовать предыдущие параметры, можно задать значения непосредственно в context.bindings, где ключ соответствует имени привязки. В следующем примере используются выходные привязки с именем httpResponse и queueOutput:

    {
        "name": "httpResponse",
        "type": "http",
        "direction": "out"
    },
    {
        "name": "queueOutput",
        "type": "queue",
        "direction": "out",
        "queueName": "helloworldqueue",
        "connection": "storage_APPSETTING"
    }
    
    module.exports = async function (context, request) {
        let message = 'Hello, world!';
        context.bindings.httpResponse = {
            body: message
        };
        context.bindings.queueOutput = message;
    };
    

Тип привязки данных

Свойство можно использовать dataType для входной привязки для изменения типа входных данных, однако у него есть некоторые ограничения:

  • В Node.js поддерживаются только string и binary поддерживаются (stream не)
  • Для входных dataType данных HTTP свойство игнорируется. Вместо этого используйте свойства объекта request , чтобы получить текст в нужном формате. Дополнительные сведения см. в http-запросе.

В следующем примере триггера очереди хранилища типом по умолчанию является stringтип, но если задано binarydataType значение, тип myQueueItem изменяется на Node.jsBuffer.

{
    "name": "myQueueItem",
    "type": "queueTrigger",
    "direction": "in",
    "queueName": "helloworldqueue",
    "connection": "storage_APPSETTING",
    "dataType": "binary"
}
const { Buffer } = require('node:buffer');

module.exports = async function (context, myQueueItem) {
    if (typeof myQueueItem === 'string') {
        context.log('myQueueItem is a string');
    } else if (Buffer.isBuffer(myQueueItem)) {
        context.log('myQueueItem is a buffer');
    }
};

Функция требуется для того, чтобы именно один первичный вход был вызван триггером. У него также могут быть вторичные входные данные, первичные выходные данные, называемые возвращаемыми выходными данными и /или вторичными выходными данными. Входные и выходные данные также называются привязками вне контекста модели программирования Node.js. До версии 4 модели эти привязки были настроены в function.json файлах.

Входные данные триггера

Триггер является единственным обязательным входным или выходным данным. Для большинства типов триггеров необходимо зарегистрировать функцию с помощью метода объекта app с именем типа триггера. Вы можете указать конфигурацию, определенную для триггера непосредственно в аргументе options . Например, триггер HTTP позволяет указать маршрут. Во время выполнения значение, соответствующее этому триггеру, передается в качестве первого аргумента обработчику.

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

app.http('helloWorld1', {
    route: 'hello/world',
    handler: async (request, context) => {
        ...
    }
});

Return output

Возвращаемые выходные данные являются необязательными и в некоторых случаях настроены по умолчанию. Например, триггер HTTP, зарегистрированный с app.http помощью, настраивается для автоматического возврата выходных данных ответа HTTP. Для большинства типов выходных данных вы указываете конфигурацию возврата для options аргумента output с помощью объекта, экспортированного из @azure/functions модуля. Во время выполнения вы устанавливаете эти выходные данные, возвращая его из обработчика.

В следующем примере используется триггер таймера и выходные данные очереди хранилища:

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

app.timer('timerTrigger1', {
    schedule: '0 */5 * * * *',
    return: output.storageQueue({
        connection: 'storage_APPSETTING',
        ...
    }),
    handler: (myTimer, context) => {
        return { hello: 'world' }
    }
});

Дополнительные входные и выходные данные

Помимо триггера и возврата, при регистрации функции можно указать дополнительные входные или выходные данные для options аргумента. output Объектыinput, экспортированные из @azure/functions модуля, предоставляют методы, относящиеся к типу, для создания конфигурации. Во время выполнения вы получаете или задаете значения с context.extraInputs.get или context.extraOutputs.setпередаете исходный объект конфигурации в качестве первого аргумента.

Следующий пример — это функция, активируемый очередью хранилища, с дополнительными входными данными BLOB-объектов хранилища, скопированными в дополнительные выходные данные BLOB-объекта хранилища. Сообщение очереди должно быть именем файла и заменяет {queueTrigger} его как скопированное имя большого двоичного объекта с помощью выражения привязки.

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

const blobInput = input.storageBlob({
    connection: 'storage_APPSETTING',
    path: 'helloworld/{queueTrigger}',
});

const blobOutput = output.storageBlob({
    connection: 'storage_APPSETTING',
    path: 'helloworld/{queueTrigger}-copy',
});

app.storageQueue('copyBlob1', {
    queueName: 'copyblobqueue',
    connection: 'storage_APPSETTING',
    extraInputs: [blobInput],
    extraOutputs: [blobOutput],
    handler: (queueItem, context) => {
        const blobInputValue = context.extraInputs.get(blobInput);
        context.extraOutputs.set(blobOutput, blobInputValue);
    }
});

Универсальные входные и выходные данные

Объекты app, inputoutput экспортируемые @azure/functions модулем, triggerпредоставляют методы, относящиеся к типу для большинства типов. Для всех типов, которые не поддерживаются, предоставляется метод, generic позволяющий вручную указать конфигурацию. Этот generic метод также можно использовать, если вы хотите изменить параметры по умолчанию, предоставляемые методом, определенным типом.

В следующем примере показана простая функция, активироваемая HTTP, с помощью универсальных методов вместо методов, относящихся к типу.

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

app.generic('helloWorld1', {
    trigger: trigger.generic({
        type: 'httpTrigger',
        methods: ['GET', 'POST']
    }),
    return: output.generic({
        type: 'http'
    }),
    handler: async (request, context) => {
        context.log(`Http function processed request for url "${request.url}"`);

        return { body: `Hello, world!` };
    }
});

Контекст вызова

Каждый вызов функции передает объект вызова context , используемый для чтения входных данных, задания выходных данных, записи в журналы и чтения различных метаданных. В модели версии 3 объект контекста всегда является первым аргументом, переданным обработчику.

Объект context имеет следующие свойства.

Свойство Description
invocationId Идентификатор вызова текущей функции.
executionContext См . контекст выполнения.
bindings См . привязки.
bindingData Метаданные о входных данных триггера для этого вызова, не включая само значение. Например, триггер концентратора событий имеет enqueuedTimeUtc свойство.
traceContext Контекст распределенной трассировки. Дополнительные сведения см. в разделе Trace Context.
bindingDefinitions Конфигурация входных и выходных данных, как определено в function.json.
req См . HTTP-запрос.
res См . http-ответ.

context.executionContext

Объект context.executionContext имеет следующие свойства.

Свойство Description
invocationId Идентификатор вызова текущей функции.
functionName Имя вызываемой функции. Имя папки, function.json содержащей файл, определяет имя функции.
functionDirectory Папка, содержащая function.json файл.
retryContext См . контекст повторных попыток.

context.executionContext.retryContext

Объект context.executionContext.retryContext имеет следующие свойства.

Свойство Description
retryCount Число, представляющее текущую попытку повтора.
maxRetryCount Максимальное количество повторных попыток выполнения. Значение -1 указывает на неограниченное число повторных попыток.
exception Исключение, вызвавшее повторную попытку.

Объект context.bindings

Объект context.bindings используется для чтения входных данных или задания выходных данных. В следующем примере показан триггер очереди хранилища, который используется context.bindings для копирования входных данных большого двоичного объекта хранилища в выходные данные BLOB-объекта хранилища. Содержимое сообщения очереди заменяется {queueTrigger} в качестве имени файла, скопированного с помощью выражения привязки.

{
    "name": "myQueueItem",
    "type": "queueTrigger",
    "direction": "in",
    "connection": "storage_APPSETTING",
    "queueName": "helloworldqueue"
},
{
    "name": "myInput",
    "type": "blob",
    "direction": "in",
    "connection": "storage_APPSETTING",
    "path": "helloworld/{queueTrigger}"
},
{
    "name": "myOutput",
    "type": "blob",
    "direction": "out",
    "connection": "storage_APPSETTING",
    "path": "helloworld/{queueTrigger}-copy"
}
module.exports = async function (context, myQueueItem) {
    const blobValue = context.bindings.myInput;
    context.bindings.myOutput = blobValue;
};

context.done

Метод context.done не рекомендуется. Перед поддержкой асинхронных функций вы сигнализируете, что функция выполняется путем вызова context.done():

module.exports = function (context, request) {
    context.log("this pattern is now deprecated");
    context.done();
};

Теперь рекомендуется удалить вызов context.done() и пометить функцию как асинхронную, чтобы она возвращала обещание (даже если вы ничего не await сделали). Как только функция завершится (другими словами, возвращенное обещание разрешается), модель версии 3 знает, что ваша функция выполнена.

module.exports = async function (context, request) {
    context.log("you don't need context.done or an awaited call")
};

Каждый вызов функции передает объект вызова context с информацией о вызове и методах, используемых для ведения журнала. В модели версии 4 объект обычно является вторым аргументом, context переданным обработчику.

Класс InvocationContext имеет следующие свойства:

Свойство Description
invocationId Идентификатор вызова текущей функции.
functionName Имя функции.
extraInputs Используется для получения значений дополнительных входных данных. Дополнительные сведения см. в дополнительных входных данных и выходных данных.
extraOutputs Используется для задания значений дополнительных выходных данных. Дополнительные сведения см. в дополнительных входных данных и выходных данных.
retryContext См . контекст повторных попыток.
traceContext Контекст распределенной трассировки. Дополнительные сведения см. в разделе Trace Context.
triggerMetadata Метаданные о входных данных триггера для этого вызова, не включая само значение. Например, триггер концентратора событий имеет enqueuedTimeUtc свойство.
options Параметры, используемые при регистрации функции, после их проверки и с явно заданными значениями по умолчанию.

Контекст повтора

Объект retryContext имеет следующие свойства.

Свойство Description
retryCount Число, представляющее текущую попытку повтора.
maxRetryCount Максимальное количество повторных попыток выполнения. Значение -1 указывает на неограниченное число повторных попыток.
exception Исключение, вызвавшее повторную попытку.

Дополнительные сведения см. в разделе retry-policies.

Ведение журнала

В Функции Azure рекомендуется использовать context.log() для записи журналов. Функции Azure интегрируется с приложение Azure Insights, чтобы лучше записывать журналы приложений-функций. Application Insights, часть Azure Monitor, предоставляет средства для сбора, визуализации и анализа журналов приложений и выходных данных трассировки. Дополнительные сведения см. в статье Мониторинг Функций Azure.

Примечание.

Если вы используете альтернативный метод Node.js console.log , эти журналы отслеживаются на уровне приложения и не будут связаны с какой-либо конкретной функцией. Настоятельно рекомендуется использовать context для ведения журнала вместо console того, чтобы все журналы были связаны с определенной функцией.

В следующем примере записывается журнал на уровне сведений по умолчанию, включая идентификатор вызова:

context.log(`Something has happened. Invocation ID: "${context.invocationId}"`);

Уровни журнала

Помимо метода по умолчанию context.log доступны следующие методы, которые позволяют записывать журналы на определенных уровнях:

Метод Description
context.log.error() Записывает событие уровня ошибки в журналы.
context.log.warn() Записывает событие уровня предупреждения в журналы.
context.log.info() Записывает событие уровня информации в журналы.
context.log.verbose() Записывает событие уровня трассировки в журналы.
Метод Description
context.trace() Записывает событие уровня трассировки в журналы.
context.debug() Записывает событие уровня отладки в журналы.
context.info() Записывает событие уровня информации в журналы.
context.warn() Записывает событие уровня предупреждения в журналы.
context.error() Записывает событие уровня ошибки в журналы.

Настройка уровня журнала

Функции Azure позволяет определить пороговый уровень, используемый при отслеживании и просмотре журналов. Чтобы задать пороговое значение, используйте logging.logLevel свойство в host.json файле. Это свойство позволяет определить уровень по умолчанию, применяемый ко всем функциям, или пороговое значение для каждой отдельной функции. Дополнительные сведения см. в статье Настройка мониторинга для Функций Azure.

Отслеживание пользовательских данных

По умолчанию Функции Azure записывает выходные данные как трассировки в Application Insights. Для получения дополнительных элементов управления можно использовать пакет SDK для Application Insights Node.js для отправки пользовательских данных в экземпляр Application Insights.

const appInsights = require("applicationinsights");
appInsights.setup();
const client = appInsights.defaultClient;

module.exports = async function (context, request) {
    // Use this with 'tagOverrides' to correlate custom logs to the parent function invocation.
    var operationIdOverride = {"ai.operation.id":context.traceContext.traceparent};

    client.trackEvent({name: "my custom event", tagOverrides:operationIdOverride, properties: {customProperty2: "custom property value"}});
    client.trackException({exception: new Error("handled exceptions can be logged with this method"), tagOverrides:operationIdOverride});
    client.trackMetric({name: "custom metric", value: 3, tagOverrides:operationIdOverride});
    client.trackTrace({message: "trace message", tagOverrides:operationIdOverride});
    client.trackDependency({target:"http://dbname", name:"select customers proc", data:"SELECT * FROM Customers", duration:231, resultCode:0, success: true, dependencyTypeName: "ZSQL", tagOverrides:operationIdOverride});
    client.trackRequest({name:"GET /customers", url:"http://myserver/customers", duration:309, resultCode:200, success:true, tagOverrides:operationIdOverride});
};

Параметр tagOverrides присваивает параметру operation_Id значение идентификатора вызова функции. Этот параметр позволяет сопоставлять все автоматически созданные и настраиваемые журналы для вызова данной функции.

Триггеры HTTP

Триггеры HTTP и веб-перехватчика используют объекты запроса и ответа для представления HTTP-сообщений.

Триггеры HTTP и веб-перехватчика используются HttpRequest и HttpResponse объекты для представления HTTP-сообщений. Классы представляют подмножество стандарта получения с помощью пакета Node.js undici .

HTTP-запрос

Доступ к запросу можно получить несколькими способами:

  • В качестве второго аргумента функции:

    module.exports = async function (context, request) {
        context.log(`Http function processed request for url "${request.url}"`);
    
  • context.req Из свойства:

    module.exports = async function (context, request) {
        context.log(`Http function processed request for url "${context.req.url}"`);
    
  • Из именованных входных привязок: этот параметр работает так же, как любая привязка, не связанная с HTTP. Имя привязки должно function.json соответствовать ключу context.bindingsили "request1" в следующем примере:

    {
        "name": "request1",
        "type": "httpTrigger",
        "direction": "in",
        "authLevel": "anonymous",
        "methods": [
            "get",
            "post"
        ]
    }
    
    module.exports = async function (context, request) {
        context.log(`Http function processed request for url "${context.bindings.request1.url}"`);
    

Объект HttpRequest имеет следующие свойства.

Свойство Type Описание
method string Метод HTTP-запроса, используемый для вызова этой функции.
url string URL-адрес запроса.
headers Record<string, string> Заголовки HTTP-запроса. Этот объект учитывает регистр. Вместо этого рекомендуется использовать request.getHeader('header-name') без учета регистра.
query Record<string, string> Запрос ключей и значений строковых параметров из URL-адреса.
params Record<string, string> Ключи и значения параметров маршрута.
user HttpRequestUser | null Объект, представляющий пользователя, вошедшего в систему, через проверку подлинности Функций, проверку подлинности SWA или null, если такой пользователь не вошел в систему.
body Buffer | string | any Если тип носителя — application/octet-stream или multipart/*, body это буфер. Если значение является строкой с возможностью синтаксического анализа JSON, body является объектом синтаксического анализа. body В противном случае — строка.
rawBody string Текст в виде строки. Несмотря на имя, это свойство не возвращает буфер.
bufferBody Buffer Тело в качестве буфера.

К запросу можно получить доступ в качестве первого аргумента обработчика для триггерной функции HTTP.

async (request, context) => {
    context.log(`Http function processed request for url "${request.url}"`);

Объект HttpRequest имеет следующие свойства.

Свойство Type Описание
method string Метод HTTP-запроса, используемый для вызова этой функции.
url string URL-адрес запроса.
headers Headers Заголовки HTTP-запроса.
query URLSearchParams Запрос ключей и значений строковых параметров из URL-адреса.
params Record<string, string> Ключи и значения параметров маршрута.
user HttpRequestUser | null Объект, представляющий пользователя, вошедшего в систему, через проверку подлинности Функций, проверку подлинности SWA или null, если такой пользователь не вошел в систему.
body ReadableStream | null Текст в виде удобочитаемого потока.
bodyUsed boolean Логическое значение, указывающее, считывается ли текст.

Чтобы получить доступ к тексту запроса или ответа, можно использовать следующие методы:

Метод Тип возвращаемых данных
arrayBuffer() Promise<ArrayBuffer>
blob() Promise<Blob>
formData() Promise<FormData>
json() Promise<unknown>
text() Promise<string>

Примечание.

Функции тела могут выполняться только один раз; последующие вызовы будут разрешаться с пустыми строками или ArrayBuffers.

HTTP-ответ

Ответ можно задать несколькими способами:

  • context.res Задайте свойство:

    module.exports = async function (context, request) {
        context.res = { body: `Hello, world!` };
    
  • Возвращает ответ: если функция асинхронна, и вы задаете имя $return привязки в вашемfunction.json, вы можете вернуть ответ напрямую, а не задать его.context

    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
    
    module.exports = async function (context, request) {
        return { body: `Hello, world!` };
    
  • Задайте именованную выходную привязку: этот параметр работает так же, как любая привязка, не связанная с HTTP. Имя привязки должно function.json соответствовать ключу context.bindingsили "response1" в следующем примере:

    {
        "type": "http",
        "direction": "out",
        "name": "response1"
    }
    
    module.exports = async function (context, request) {
        context.bindings.response1 = { body: `Hello, world!` };
    
  • Вызов context.res.send(): этот параметр не рекомендуется. Он неявно вызывает context.done() и не может использоваться в асинхронной функции.

    module.exports = function (context, request) {
        context.res.send(`Hello, world!`);
    

При создании нового объекта при настройке ответа этот объект должен соответствовать интерфейсу HttpResponseSimple , который имеет следующие свойства:

Свойство Type Описание
headers Record<string, string> (необязательно) Заголовки HTTP-ответа.
cookies Cookie[] (необязательно) Файлы cookie ответа HTTP.
body any (необязательно) Текст ответа HTTP.
statusCode number (необязательно) Код состояния HTTP-ответа. Если значение не задано, по умолчанию используется 200значение .
status number (необязательно) То же самое, что statusCodeи . Это свойство игнорируется, если statusCode задано.

Можно также изменить context.res объект, не перезаписав его. Объект по умолчанию context.res использует HttpResponseFull интерфейс, который поддерживает следующие методы в дополнение к свойствам HttpResponseSimple :

Метод Description
status() Задает состояние.
setHeader() Задает поле заголовка. ПРИМЕЧАНИЕ. Кроме res.header() того, res.set() поддерживаются и выполняются такие же действия.
getHeader() Получение поля заголовка. ПРИМЕЧАНИЕ. res.get() Также поддерживается и выполняет то же самое.
removeHeader() Удаляет заголовок.
type() Задает заголовок content-type.
send() Этот метод является устаревшим. Он задает текст и вызовы context.done() для указания завершения функции синхронизации. ПРИМЕЧАНИЕ. res.end() Также поддерживается и выполняет то же самое.
sendStatus() Этот метод является устаревшим. Он задает код состояния и вызовы context.done() для указания завершения функции синхронизации.
json() Этот метод является устаревшим. Он задает для типа контента значение application/json, задает текст и вызовы context.done() для указания завершения функции синхронизации.

Ответ можно задать несколькими способами:

  • Как простой интерфейс с типом HttpResponseInit: этот параметр является наиболее кратким способом возврата ответов.

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

    Интерфейс HttpResponseInit имеет следующие свойства:

    Свойство Type Описание
    body BodyInit (необязательно) Текст ответа HTTP в виде одного из ArrayBuffer, BlobNodeJS.ArrayBufferViewAsyncIterable<Uint8Array>Iterable<Uint8Array>URLSearchParamsFormDatanullили .string
    jsonBody any (необязательно) Текст ответа HTTP, сериализуемый в формате JSON. Если задано, HttpResponseInit.body свойство игнорируется в пользу этого свойства.
    status number (необязательно) Код состояния HTTP-ответа. Если значение не задано, по умолчанию используется 200значение .
    headers HeadersInit (необязательно) Заголовки HTTP-ответа.
    cookies Cookie[] (необязательно) Файлы cookie ответа HTTP.
  • Как класс с типом HttpResponse: этот параметр предоставляет вспомогательные методы для чтения и изменения различных частей ответа, таких как заголовки.

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

    Класс HttpResponse принимает необязательный HttpResponseInit аргумент в качестве аргумента его конструктора и имеет следующие свойства:

    Свойство Type Описание
    status number Код состояния HTTP-ответа.
    headers Headers Заголовки HTTP-ответа.
    cookies Cookie[] Файлы cookie ответа HTTP.
    body ReadableStream | null Текст в виде удобочитаемого потока.
    bodyUsed boolean Логическое значение, указывающее, был ли текст прочитан уже.

HTTP-потоки

HTTP-потоки — это функция, которая упрощает обработку больших данных, потоковую передачу ответов OpenAI, доставку динамического содержимого и поддержку других основных сценариев HTTP. Он позволяет передавать запросы и ответы от конечных точек HTTP в приложении-функции Node.js. Используйте http-потоки в сценариях, когда приложению требуется обмен данными в режиме реального времени и взаимодействие между клиентом и сервером по протоколу HTTP. Вы также можете использовать HTTP-потоки для получения оптимальной производительности и надежности приложений при использовании HTTP.

Внимание

Http-потоки не поддерживаются в модели версии 3. Обновите модель версии 4 , чтобы использовать функцию потоковой передачи HTTP.

Существующие HttpRequest и HttpResponse типы в модели программирования версии 4 уже поддерживают различные способы обработки текста сообщения, включая поток.

Необходимые компоненты

Включение потоков

Выполните следующие действия, чтобы включить HTTP-потоки в приложении-функции в Azure и в локальных проектах:

  1. Если вы планируете передавать большие объемы данных, измените FUNCTIONS_REQUEST_BODY_SIZE_LIMIT параметр в Azure. Допустимый максимальный размер текста по умолчанию, 104857600который ограничивает запросы размером около 100 МБ.

  2. Для локальной разработки также добавьте FUNCTIONS_REQUEST_BODY_SIZE_LIMIT в файл local.settings.json.

  3. Добавьте следующий код в приложение в любой файл, включенный в основное поле.

    const { app } = require('@azure/functions'); 
    
    app.setup({ enableHttpStream: true });
    

Примеры потоков

В этом примере показана функция, активировающая HTTP, которая получает данные через HTTP-запрос POST, а функция передает эти данные в указанный выходной файл:

const { app } = require('@azure/functions');
const { createWriteStream } = require('fs');
const { Writable } = require('stream');

app.http('httpTriggerStreamRequest', {
    methods: ['POST'],
    authLevel: 'anonymous',
    handler: async (request, context) => {
        const writeStream = createWriteStream('<output file path>');
        await request.body.pipeTo(Writable.toWeb(writeStream));

        return { body: 'Done!' };
    },
});

В этом примере показана функция, активировающая HTTP, которая передает содержимое файла в качестве ответа на входящие HTTP-запросы GET:

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

app.http('httpTriggerStreamResponse', {
    methods: ['GET'],
    authLevel: 'anonymous',
    handler: async (request, context) => {
        const body = createReadStream('<input file path>');

        return { body };
    },
});

Пример приложения, готового к выполнению с помощью потоков, см. в этом примере на сайте GitHub.

Рекомендации по потоковой передаче

  • Используется request.body для получения максимального преимущества от использования потоков. Вы по-прежнему можете использовать такие методы request.text(), как , которые всегда возвращают текст в виде строки.

Обработчики

Перехватчики не поддерживаются в модели версии 3. Обновите модель версии 4, чтобы использовать перехватчики.

Используйте перехватчик для выполнения кода в разных точках жизненного цикла Функции Azure. Перехватчики выполняются в том порядке, в каком порядке они зарегистрированы и могут быть зарегистрированы из любого файла в приложении. В настоящее время существуют две области перехватчиков, уровень приложения и уровень вызова.

Вызов крючки

Перехватчики вызовов выполняются один раз в каждом вызове функции, либо до перехватчика preInvocation , либо после перехватчика postInvocation . По умолчанию перехватчик выполняется для всех типов триггеров, но также можно фильтровать по типам. В следующем примере показано, как зарегистрировать перехватчик вызова и фильтровать по типу триггера:

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

app.hook.preInvocation((context) => {
    if (context.invocationContext.options.trigger.type === 'httpTrigger') {
        context.invocationContext.log(
            `preInvocation hook executed for http function ${context.invocationContext.functionName}`
        );
    }
});

app.hook.postInvocation((context) => {
    if (context.invocationContext.options.trigger.type === 'httpTrigger') {
        context.invocationContext.log(
            `postInvocation hook executed for http function ${context.invocationContext.functionName}`
        );
    }
});

Первым аргументом обработчика перехватчика является объект контекста, характерный для этого типа перехватчика.

Объект PreInvocationContext имеет следующие свойства.

Свойство Description
inputs Аргументы, переданные вызову.
functionHandler Обработчик функции для вызова. Изменения этого значения влияют на саму функцию.
invocationContext Объект контекста вызова, переданный функции.
hookData Рекомендуемое место для хранения и совместного использования данных между перехватчиками в одной области. Необходимо использовать уникальное имя свойства, чтобы он не конфликтует с данными других перехватчиков.

Объект PostInvocationContext имеет следующие свойства.

Свойство Description
inputs Аргументы, переданные вызову.
result Результат функции. Изменения этого значения влияют на общий результат функции.
error Ошибка, возникаемая функцией, или значение NULL или не определено, если ошибка отсутствует. Изменения этого значения влияют на общий результат функции.
invocationContext Объект контекста вызова, переданный функции.
hookData Рекомендуемое место для хранения и совместного использования данных между перехватчиками в одной области. Необходимо использовать уникальное имя свойства, чтобы он не конфликтует с данными других перехватчиков.

Перехватчики приложений

Перехватчики приложений выполняются один раз на экземпляр приложения, во время запуска в appStart перехватчике или во время завершения в перехватчике appTerminate . Перехватчики завершения приложения имеют ограниченное время выполнения и не выполняются во всех сценариях.

В настоящее время среда выполнения Функции Azure не поддерживает ведение журнала контекста за пределами вызова. Используйте пакет npm Application Insights для регистрации данных во время перехватчиков на уровне приложения.

В следующем примере регистрируются перехватчики приложений:

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

app.hook.appStart((context) => {
    // add your logic here
});

app.hook.appTerminate((context) => {
    // add your logic here
});

Первым аргументом обработчика перехватчика является объект контекста, характерный для этого типа перехватчика.

Объект AppStartContext имеет следующие свойства.

Свойство Description
hookData Рекомендуемое место для хранения и совместного использования данных между перехватчиками в одной области. Необходимо использовать уникальное имя свойства, чтобы он не конфликтует с данными других перехватчиков.

Объект AppTerminateContext имеет следующие свойства.

Свойство Description
hookData Рекомендуемое место для хранения и совместного использования данных между перехватчиками в одной области. Необходимо использовать уникальное имя свойства, чтобы он не конфликтует с данными других перехватчиков.

Масштабирование и параллелизм

По умолчанию Функции Azure автоматически отслеживает нагрузку на приложение и создает больше экземпляров узлов для Node.js по мере необходимости. Функции Azure использует встроенные (не настраиваемые пользователем) пороговые значения для различных типов триггеров, чтобы решить, когда следует добавлять экземпляры, например возраст сообщений и размер очереди для QueueTrigger. Дополнительные сведения см. в статье, посвященной планам с оплатой по мере использования и планам "Премиум".

Такого поведения при масштабировании достаточно для многих приложений Node.js. Для приложений, зависящих от ЦП, производительность можно повысить путем увеличения числа рабочих процессов обработки языка. Число рабочих процессов на узел можно увеличить с 1 до 10 до 10, используя параметр приложения FUNCTIONS_WORKER_PROCESS_COUNT . Затем Функции Azure пытаются равномерно распределять одновременные вызовы функций между этими рабочими процессами. Такое поведение делает его менее вероятным, что функция с большим объемом ЦП блокирует выполнение других функций. Этот параметр применяется к каждому узлу, который Функции Azure создает при масштабировании приложения в соответствии с требованиями.

Предупреждение

FUNCTIONS_WORKER_PROCESS_COUNT Используйте параметр с осторожностью. Несколько процессов, выполняемых в одном экземпляре, могут привести к непредсказуемому поведению и увеличению времени загрузки функции. Если этот параметр используется, настоятельно рекомендуется компенсировать эти недостатки, выполнив команду из файла пакета.

Версия узла

Зафиксировав параметр process.version из любой функции, можно увидеть текущую версию, которую использует среда выполнения. Список supported versions версий Node.js, поддерживаемых каждой моделью программирования.

Установка версии Node.js

Способ обновления версии Node.js зависит от ОС, на которой работает ваше приложение-функция.

При запуске в Windows Node.js версия устанавливается параметром WEBSITE_NODE_DEFAULT_VERSION приложения. Этот параметр можно обновить с помощью Azure CLI или в портал Azure.

Дополнительные сведения о версиях Node.js см. в статье "Поддерживаемые версии".

Перед обновлением Node.js версии убедитесь, что приложение-функция работает в последней версии среды выполнения Функции Azure. Если вам нужно обновить версию среды выполнения, см. статью "Миграция приложений с Функции Azure версии 3.x до версии 4.x".

Выполните команду Azure CLI az functionapp config appsettings set , чтобы обновить версию Node.js для приложения-функции, работающего в Windows:

az functionapp config appsettings set  --settings WEBSITE_NODE_DEFAULT_VERSION=~20 \
 --name <FUNCTION_APP_NAME> --resource-group <RESOURCE_GROUP_NAME> 

Этот параметр задает WEBSITE_NODE_DEFAULT_VERSION для приложения поддерживаемую версию ~20LTS.

После внесения изменений приложение-функция перезапускается. Дополнительные сведения о поддержке функций для Node.js см . в политике поддержки среды выполнения языка.

Переменные среды

Переменные среды могут быть полезны для операционных секретов (строка подключения, ключей, конечных точек и т. д.) или параметров среды, таких как переменные профилирования. Переменные среды можно добавлять как в локальные, так и облачные среды, а также получать к ним process.env доступ в коде функции.

В следующем примере регистрируется WEBSITE_SITE_NAME переменная среды:

module.exports = async function (context) {
    context.log(`WEBSITE_SITE_NAME: ${process.env["WEBSITE_SITE_NAME"]}`);
}
async function timerTrigger1(myTimer, context) {
    context.log(`WEBSITE_SITE_NAME: ${process.env["WEBSITE_SITE_NAME"]}`);
}

В локальной среде разработки

При локальном запуске проект функций содержит local.settings.json файл, в котором хранятся переменные среды в объекте Values .

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "",
    "FUNCTIONS_WORKER_RUNTIME": "node",
    "CUSTOM_ENV_VAR_1": "hello",
    "CUSTOM_ENV_VAR_2": "world"
  }
}

В облачной среде Azure

При запуске в Azure приложение-функция позволяет задавать и использовать параметры приложения, такие как строка подключения службы, и предоставляет эти параметры в виде переменных среды во время выполнения.

Существует несколько способов для добавления, обновления и удаления параметров приложения-функции.

После изменения параметров приложения-функции нужно перезапустить приложение-функцию.

Переменные рабочей среды

Существует несколько переменных среды Функций, относящихся к Node.js:

languageWorkers__node__arguments

Этот параметр позволяет указать настраиваемые аргументы при запуске процесса Node.js. Он чаще всего используется локально для запуска рабочей роли в режиме отладки, но также может использоваться в Azure, если вам нужны пользовательские аргументы.

Предупреждение

По возможности избегайте использования languageWorkers__node__arguments в Azure, так как это может негативно повлиять на время холодного запуска. Вместо использования предварительно подготовленных рабочих ролей среда выполнения должна начать новую рабочую роль с нуля с пользовательскими аргументами.

logging__logLevel__Worker

Этот параметр настраивает уровень журнала по умолчанию для журналов Node.js рабочих ролей. По умолчанию отображаются только журналы предупреждений или ошибок, но их можно настроить information или debug помочь диагностировать проблемы с Node.js рабочей ролью. Дополнительные сведения см. в разделе о настройке уровней журнала.

Модули ECMAScript (предварительная версия)

Примечание.

Поскольку модули ECMAScript в настоящее время являются предварительной версией функции в Node.js 14 или более поздних версий в Функции Azure.

Модули ECMAScript (модули ES) — это новая официальная стандартная система модулей для Node.js. Пока что в примерах кода в этой статье используется синтаксис CommonJS. При выполнении Функций Azure в Node.js 14 или более поздней версии вы можете использовать в них синтаксис модулей ES.

Чтобы использовать в функции модули ES, переименуйте ее файл, заменив расширение на .mjs. Следующий пример файла index.mjs — это функция, активируемая через HTTP, которая использует синтаксис модулей ES для импорта библиотеки uuid и возвращает значение.

import { v4 as uuidv4 } from 'uuid';

async function httpTrigger1(context, request) {
    context.res.body = uuidv4();
};

export default httpTrigger;
import { v4 as uuidv4 } from 'uuid';

async function httpTrigger1(request, context) {
    return { body: uuidv4() };
};

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

Настройка точки входа функции

Свойства function.json, а именно scriptFile и entryPoint, позволяют настроить расположение и имя экспортированной функции. Свойство scriptFile требуется при использовании TypeScript и должно указывать на скомпилированный JavaScript.

С использованием scriptFile

По умолчанию функция JavaScript выполняется из файла index.js, который расположен в том же родительском каталоге, что и соответствующий файл function.json.

scriptFile можно использовать для получения структуры папок, которая выглядит следующим образом:

<project_root>/
 | - node_modules/
 | - myFirstFunction/
 | | - function.json
 | - lib/
 | | - sayHello.js
 | - host.json
 | - package.json

Файл function.json для myFirstFunction должен включать свойство scriptFile, указывающее на файл с экспортированной функцией, которую нужно выполнить.

{
  "scriptFile": "../lib/sayHello.js",
  "bindings": [
    ...
  ]
}

С использованием entryPoint

В модели версии 3 необходимо экспортировать функцию, используя module.exports ее для обнаружения и запуска. По умолчанию функция, которая выполняется при запуске, является единственным экземпляром экспорта (с именем run или index) из этого файла. В следующем примере устанавливается entryPoint function.json настраиваемое значение logHello:

{
  "entryPoint": "logHello",
  "bindings": [
    ...
  ]
}
async function logHello(context) {
    context.log('Hello, world!');
}

module.exports = { logHello };

Локальная отладка

Рекомендуется использовать VS Code для локальной отладки, которая запускает процесс Node.js в режиме отладки автоматически и подключается к процессу. Дополнительные сведения см. в статье о локальном запуске функции.

Если вы используете другое средство для отладки или хотите запустить процесс Node.js в режиме отладки вручную, добавьте "languageWorkers__node__arguments": "--inspect" в Values local.settings.json. Аргумент --inspect сообщает Node.js прослушивать отладочный клиент по умолчанию на порте 9229. Дополнительные сведения см. в руководстве по отладке Node.js.

Рекомендации

В этом разделе описывается несколько влияющих шаблонов для приложений Node.js, которые рекомендуется следовать.

Выбор планов службы приложений для конфигурации с одним виртуальным ЦП

При создании приложения-функции, которое использует план службы приложения, мы советуем выбирать план для конфигурации с одним виртуальным ЦП вместо плана для нескольких виртуальных ЦП. В настоящее время функции Node.js работают более эффективно на виртуальных машинах с одним виртуальным ЦП, а использование больших виртуальных машин не обеспечивает ожидаемых улучшений производительности. При необходимости вы можете вручную горизонтально увеличить масштаб дополнительных экземпляров виртуальных машин с одним ЦП или включить автоматическое масштабирование. Дополнительные сведения см. в статье Масштабирование числа экземпляров вручную или автоматически.

Запуск из файла пакета

При разработке Функции Azure в бессерверной модели размещения холодные запуски являются реальностью. Холодный запуск относится к первому запуску приложения-функции после периода бездействия, что занимает больше времени для запуска. Для приложений Node.js с большими деревьями зависимостей, в частности, холодный запуск может быть значительным. Чтобы ускорить процесс холодного запуска, по возможности выполняйте функции в виде файла пакета. Многие методы развертывания используют эту модель по умолчанию, но если вы испытываете большой холодный запуск, убедитесь, что вы работаете таким образом.

Использование одного статического клиента

При использовании клиента для конкретной службы в приложении Функции Azure не создавайте новый клиент с каждым вызовом функции, так как вы можете попасть в ограничения подключения. Вместо этого создайте в глобальной области один статический клиент. Дополнительные сведения см. в статье Управление подключениями в Функциях Azure.

Использование async и await

При написании Функции Azure в Node.js необходимо написать код с помощью async ключевых слов и await ключевых слов. Использование в коде async и await вместо обратных вызовов или .then и .catch с обещаниями помогает избежать двух распространенных проблем:

  • Возникновение неперехваченных исключений, которые приводят к аварийному завершению процесса Node.js, что может повлиять и на выполнение других функций.
  • Непредвиденное поведение, например отсутствие журналов из context.log, вызванное асинхронными вызовами, которые не ожидаются должным образом.

В следующем примере асинхронный метод fs.readFile вызывается с функцией обратного вызова error-first в качестве второго параметра. Этот код вызывает обе проблемы, упомянутые ранее. Исключение, которое явно не поймано в правильной области, может завершить весь процесс (проблема 1). Возврат без обеспечения завершения обратного вызова означает, что ответ http иногда будет иметь пустой текст (проблема 2).

// DO NOT USE THIS CODE
const { app } = require('@azure/functions');
const fs = require('fs');

app.http('httpTriggerBadAsync', {
    methods: ['GET', 'POST'],
    authLevel: 'anonymous',
    handler: async (request, context) => {
        let fileData;
        fs.readFile('./helloWorld.txt', (err, data) => {
            if (err) {
                context.error(err);
                // BUG #1: This will result in an uncaught exception that crashes the entire process
                throw err;
            }
            fileData = data;
        });
        // BUG #2: fileData is not guaranteed to be set before the invocation ends
        return { body: fileData };
    },
});

В следующем примере асинхронный метод fs.readFile вызывается с функцией обратного вызова error-first в качестве второго параметра. Этот код вызывает обе проблемы, упомянутые ранее. Исключение, которое явно не поймано в правильной области, может завершить весь процесс (проблема 1). Вызов устаревшего context.done() метода вне области обратного вызова может сигнализировать о завершении функции перед чтением файла (проблема 2). В этом примере слишком ранний вызов context.done() приводит к непопаданию в журнал записей начиная с Data from file:.

// NOT RECOMMENDED PATTERN
const fs = require('fs');

module.exports = function (context) {
    fs.readFile('./hello.txt', (err, data) => {
        if (err) {
            context.log.error('ERROR', err);
            // BUG #1: This will result in an uncaught exception that crashes the entire process
            throw err;
        }
        context.log(`Data from file: ${data}`);
        // context.done() should be called here
    });
    // BUG #2: Data is not guaranteed to be read before the Azure Function's invocation ends
    context.done();
}

async await Используйте ключевые слова, чтобы избежать обоих этих проблем. Большинство API в экосистеме Node.js были преобразованы в поддержку обещаний в некоторой форме. Например, начиная с версии 14, Node.js предоставляет fs/promises API для замены API обратного fs вызова.

В следующем примере все необработанные исключения, создаваемые во время выполнения функции, завершаются только сбоем отдельного вызова, вызвавшее исключение. Ключевое await слово означает, что шаги после readFile выполнения выполняются только после завершения.

// Recommended pattern
const { app } = require('@azure/functions');
const fs = require('fs/promises');

app.http('httpTriggerGoodAsync', {
    methods: ['GET', 'POST'],
    authLevel: 'anonymous',
    handler: async (request, context) => {
        try {
            const fileData = await fs.readFile('./helloWorld.txt');
            return { body: fileData };
        } catch (err) {
            context.error(err);
            // This rethrown exception will only fail the individual invocation, instead of crashing the whole process
            throw err;
        }
    },
});

С async и await также не требуется совершать обратный вызов context.done().

// Recommended pattern
const fs = require('fs/promises');

module.exports = async function (context) {
    let data;
    try {
        data = await fs.readFile('./hello.txt');
    } catch (err) {
        context.log.error('ERROR', err);
        // This rethrown exception will be handled by the Functions Runtime and will only fail the individual invocation
        throw err;
    }
    context.log(`Data from file: ${data}`);
}

Устранение неполадок

См. руководство по устранению неполадок Node.js.

Следующие шаги

Дополнительные сведения см. на следующих ресурсах: