руководство разработчика Функции 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
тип, но если задано binary
dataType
значение, тип 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
, input
output
экспортируемые @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
,Blob
NodeJS.ArrayBufferView
AsyncIterable<Uint8Array>
Iterable<Uint8Array>
URLSearchParams
FormData
null
или .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 уже поддерживают различные способы обработки текста сообщения, включая поток.
Необходимые компоненты
@azure/functions
Пакет npm версии 4.3.0 или более поздней.- Функции Azure среды выполнения версии 4.28 или более поздней.
- Функции Azure Core Tools версии 4.0.5530 или более поздней версии, содержащей правильную версию среды выполнения.
Включение потоков
Выполните следующие действия, чтобы включить HTTP-потоки в приложении-функции в Azure и в локальных проектах:
Если вы планируете передавать большие объемы данных, измените
FUNCTIONS_REQUEST_BODY_SIZE_LIMIT
параметр в Azure. Допустимый максимальный размер текста по умолчанию,104857600
который ограничивает запросы размером около 100 МБ.Для локальной разработки также добавьте
FUNCTIONS_REQUEST_BODY_SIZE_LIMIT
в файл local.settings.json.Добавьте следующий код в приложение в любой файл, включенный в основное поле.
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
для приложения поддерживаемую версию ~20
LTS.
После внесения изменений приложение-функция перезапускается. Дополнительные сведения о поддержке функций для 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.
Следующие шаги
Дополнительные сведения см. на следующих ресурсах: