介绍
在本文中,我们将使用 azure 函数和 ComsmosDB 创建一个无服务器 REST API 应用程序。使用的语言是 Node.js/TypeScript。此外,由于我们使用 CosmosDB 作为数据库,因此我们将使用 Azure 函数触发器接收 Change Feed 事件作为奖励。
创建资源
创建资源组
az group create
--name ${ AZ_RESOURCE_GROUP }
--location ${ AZ_RESOURCE_GROUP_LOCATION }
创建 Azure Function App 资源
在创建 Azure Function App 资源之前,我们需要创建一个 Azure 存储帐户来支持 blob、队列和表存储。
az storage account create
--name ${ AZ_STORAGE_ACCOUNT }
--location ${ AZ_RESOURCE_GROUP_LOCATION }
--resource-group ${ AZ_RESOURCE_GROUP }
--sku Standard_LRS
将上面创建的 Azure 存储帐户的名称作为参数传递给 --storage-account ,以创建 Azure Function App 资源。
az functionapp create
--name ${ AZ_FUNCTION }
--storage-account ${ AZ_STORAGE_ACCOUNT }
--consumption-plan-location ${ AZ_RESOURCE_GROUP_LOCATION }
--resource-group ${ AZ_RESOURCE_GROUP }
--functions-version 4
--runtime node
创建 CosmosDB 资源
创建 CosmosDB 资源。始终将 --enable-free-tier 传递给 true 用于演示或测试目的。
az cosmosdb create
--resource-group ${ AZ_RESOURCE_GROUP }
--name ${ AZ_COSMOS_DB }
--enable-free-tier true
--enable-analytical-storage false
创建 Azure Function 本地项目
创建项目文件夹
mkdir az-function-rest-api cd az-function-rest-api npm init -y
安装库
npm install --save-exact @azure/cosmos
安装开发库
npm install --save-exact -D @azure/functions @types/node azure-functions-core-tools typescript
生成 Azure 函数项目
npx func init --worker-runtime node --language typescript
创建 Http 触发函数
这次我们将创建 4 个函数。
npx func new --name get-todos --template 'HTTP trigger' --authlevel 'anonymous' --language typescript
npx func new --name post-todos --template 'HTTP trigger' --authlevel 'anonymous' --language typescript
npx func new --name delete-todos --template 'HTTP trigger' --authlevel 'anonymous' --language typescript
npx func new --name patch-todos --template 'HTTP trigger' --authlevel 'anonymous' --language typescript
编辑 package.json
包.json
{
"name" : "az-function-rest-api" ,
"version" : "1.0.0" ,
"description" : "" ,
"main" : "index.js" ,
"scripts" : {
"build" : "tsc" , # <= 追加
"watch" : "tsc -w" , # <= 追加
"prestart" : "npm run build" , # <= 追加
"start" : "func start" # <= 追加
},
"keywords" : [],
"author" : "" ,
"license" : "ISC" ,
"dependencies" : {
"@azure/cosmos" : "3.17.1"
},
"devDependencies" : {
"@azure/functions" : "3.2.0" ,
"@types/node" : "18.11.5" ,
"azure-functions-core-tools" : "4.0.4829" ,
"typescript" : "4.8.4"
}
}
当前项目结构
. ├── delete-todos │ ├── function .json │ └── index.ts ├── get-todos │ ├── function .json │ └── index.ts ├── host.json ├── local.settings.json ├── package-lock.json ├── package.json ├── patch-todos │ ├── function .json │ └── index.ts ├── post-todos │ ├── function .json │ └── index.ts └── tsconfig.json
在本地运行 Azure 函数
npm run start
默认情况下,每个函数的文件夹名称会自动设置为端点路径。
? npm run start
> az-function-rest-api@1.0.0 prestart
> npm run build
> az-function-rest-api@1.0.0 build
> tsc
> az-function-rest-api@1.0.0 start
> func start
Azure Functions Core Tools
Core Tools Version: 4.0.4829 Commit hash : N/A ( 64-bit )
Function Runtime Version: 4.11.2.19273
[ 2022-10-25T05:54:42.841Z] Worker process started and initialized.
Functions:
delete-todos: [ GET,POST] http://localhost:7071/api/delete-todos
get-todos: [ GET,POST] http://localhost:7071/api/get-todos
patch-todos: [ GET,POST] http://localhost:7071/api/patch-todos
post-todos: [ GET,POST] http://localhost:7071/api/post-todos
当我实际发送 HTTP 请求时,响应会正确返回。
curl "http://localhost:7071/api/delete-todos?name=azure"
? curl "http://localhost:7071/api/get-todos?name=azure" Hello, azure. This HTTP triggered function executed successfully.%
端点路径和 HTTP 方法可以从每个函数文件夹中自动生成的 function.json 中设置。
您可以在 bindings.methods 列表中指定要允许的HTTP 方法。 您可以使用 bidnings.route 指定端点路径。路径参数也可以指定为占位符,并且可以从函数中引用。您可以通过添加 ? 使路径参数成为可选参数。
获取待办事项/function.json
{
"bindings" : [
{
"authLevel" : "Anonymous" ,
"type" : "httpTrigger" ,
"direction" : "in" ,
"name" : "req" ,
"methods" : [ "get" ], # <= postを消去
"route" : "todos/{id?}" # <= 追加
},
{
"type" : "http" ,
"direction" : "out" ,
"name" : "res"
}
],
"scriptFile" : "dist/get-todos/index.js"
}
编辑 get-todo/index.ts 以接收并查看路径参数。路径参数的数据存储在 context 对象的 bindingData 属性中,可以从函数的参数中接收。
获取待办事项/index.ts
import { AzureFunction , Context , HttpRequest } from " @azure/functions "
const httpTrigger : AzureFunction = async function ( context : Context , req : HttpRequest ): Promise < void > {
const id = context . bindingData . id ; // <= context.bindingData のなかにpath paramのデータが保管されている。
context . res = {
body : {
id
}
};
};
export default httpTrigger ;
再次运行该函数,可以看到 function.json 中设置的内容已经体现出来了。
npm run start
? npm run start
> az-function-rest-api@1.0.0 prestart
> npm run build
> az-function-rest-api@1.0.0 build
> tsc
> az-function-rest-api@1.0.0 start
> func start
Azure Functions Core Tools
Core Tools Version: 4.0.4829 Commit hash : N/A ( 64-bit )
Function Runtime Version: 4.11.2.19273
[ 2022-10-25T06:16:51.255Z] Worker process started and initialized.
Functions:
delete-todos: [ GET,POST] http://localhost:7071/api/delete-todos
get-todos: [ GET] http://localhost:7071/api/todos/ { id ? } # <= 設定が反映されている。
patch-todos: [ GET,POST] http://localhost:7071/api/patch-todos
post-todos: [ GET,POST] http://localhost:7071/api/post-todos
如果将路径参数传递给路由并发送 HTTP 请求,则可以确认返回带有路径参数的 id 。
curl -i -X GET http://localhost:7071/api/todos/123
? curl -i -X GET http://localhost:7071/api/todos/123
{
"id" : 123
} %
创建 REST API
完成的项目结构
. ├── delete-todos │ ├── function .json │ └── index.ts ├── get-todos │ ├── function .json │ └── index.ts ├── host.json ├── lib │ ├── db │ │ └── db-config.ts │ ├── dtos │ │ ├── CreateTodoDto.ts │ │ └── UpdateTodoDto.ts │ ├── errors │ │ └── HttpError.ts │ ├── models │ │ └── TodoItem.ts │ ├── repositories │ │ └── todo-repository.ts │ ├── services │ │ └── todo-service.ts │ └── utils │ └── generate-id.ts ├── local.settings.json ├── package-lock.json ├── package.json ├── patch-todos │ ├── function .json │ └── index.ts ├── post-todos │ ├── function .json │ └── index.ts └── tsconfig.json
获取 todos 函数
获取待办事项/index.ts
import { AzureFunction , Context , HttpRequest } from " @azure/functions " ;
import { HttpError } from " lib/errors/HttpError " ;
import { todoService } from " lib/services/todo-service " ;
const httpTrigger : AzureFunction = async function (
context : Context ,
req : HttpRequest
): Promise < void > {
const todoId = context . bindingData . id ;
try {
if ( todoId ) {
const result = await todoService . getOne ( todoId );
context . res = {
status : 200 ,
body : result ,
};
return ;
}
} catch ( err ) {
if ( err instanceof HttpError ) {
context . res = {
status : err . StatusCode ,
body : {
error : {
message : err . message ,
},
},
};
return ;
}
}
const result = await todoService . getOnes ();
context . res = {
status : 200 ,
body : result ,
};
};
export default httpTrigger ;
获取待办事项/function.json
{
"bindings" : [
{
"authLevel" : "Anonymous" ,
"type" : "httpTrigger" ,
"direction" : "in" ,
"name" : "req" ,
"methods" : [ "get" ],
"route" : "todos/{id?}"
},
{
"type" : "http" ,
"direction" : "out" ,
"name" : "res"
}
],
"scriptFile" : "dist/get-todos/index.js"
}
发布待办事项功能
待办事项/index.ts
import { AzureFunction , Context , HttpRequest } from " @azure/functions " ;
import { todoService } from " lib/services/todo-service " ;
const httpTrigger : AzureFunction = async function (
context : Context ,
req : HttpRequest
): Promise < void > {
const todoCreateDto = req . body ;
const result = await todoService . createOne ( todoCreateDto );
context . res = {
status : 201 ,
body : result ,
};
};
export default httpTrigger ;
待办事项后/function.json
{
"bindings" : [
{
"authLevel" : "Anonymous" ,
"type" : "httpTrigger" ,
"direction" : "in" ,
"name" : "req" ,
"methods" : [ "post" ],
"route" : "todos"
},
{
"type" : "http" ,
"direction" : "out" ,
"name" : "res"
}
],
"scriptFile" : "dist/post-todos/index.js"
}
删除待办事项功能
删除待办事项/index.ts
import { AzureFunction , Context , HttpRequest } from " @azure/functions " ;
import { HttpError } from " lib/errors/HttpError " ;
import { todoService } from " lib/services/todo-service " ;
const httpTrigger : AzureFunction = async function (
context : Context ,
req : HttpRequest
): Promise < void > {
const todoId = context . bindingData . id ;
try {
await todoService . deleteOne ( todoId );
} catch ( err ) {
if ( err instanceof HttpError ) {
context . res = {
status : err . StatusCode ,
body : {
error : {
message : err . message ,
},
},
};
return ;
}
}
context . res = {
status : 204 ,
};
};
export default httpTrigger ;
删除待办事项/function.json
{
"bindings" : [
{
"authLevel" : "Anonymous" ,
"type" : "httpTrigger" ,
"direction" : "in" ,
"name" : "req" ,
"methods" : [ "delete" ],
"route" : "todos/{id}"
},
{
"type" : "http" ,
"direction" : "out" ,
"name" : "res"
}
],
"scriptFile" : "dist/delete-todos/index.js"
}
补丁待办事项功能
补丁待办事项/index.ts
import { AzureFunction , Context , HttpRequest } from " @azure/functions " ;
import { HttpError } from " lib/errors/HttpError " ;
import { todoService } from " lib/services/todo-service " ;
const httpTrigger : AzureFunction = async function (
context : Context ,
req : HttpRequest
): Promise < void > {
const todoId = context . bindingData . id ;
const updateTodoDto = req . body ;
try {
await todoService . updateOne ( updateTodoDto , todoId );
} catch ( err ) {
if ( err instanceof HttpError ) {
context . res = {
status : err . StatusCode ,
body : {
error : {
message : err . message ,
},
},
};
return ;
}
}
context . res = {
status : 204 ,
};
};
export default httpTrigger ;
补丁待办事项/function.json
{
"bindings" : [
{
"authLevel" : "Anonymous" ,
"type" : "httpTrigger" ,
"direction" : "in" ,
"name" : "req" ,
"methods" : [ "patch" ],
"route" : "todos/{id}"
},
{
"type" : "http" ,
"direction" : "out" ,
"name" : "res"
}
],
"scriptFile" : "dist/patch-todos/index.js"
}
通用模块
模型
lib/models/TodoItem.ts
export interface TodoItem {
id ?: string ;
title : string ;
isCompleted : boolean ;
}
服务
lib/services/todo-service.ts
import { CreateTodoDto } from " dtos/CreateTodoDto " ;
import { UpdateTodoDto } from " dtos/UpdateTodoDto " ;
import { HttpError } from " errors/HttpError " ;
import { TodoItem } from " models/TodoItem " ;
import { todoRepository } from " repositories/todo-repository " ;
import { generateId } from " utils/generate-id " ;
const getOnes = async (): Promise < TodoItem [] | any > => {
return await todoRepository . getOnes ();
};
const getOne = async ( id : string ): Promise < any > => {
return await todoRepository . getOneById ( id );
};
const createOne = async ( dto : CreateTodoDto ): Promise < TodoItem > => {
const newTodo : TodoItem = {
id : generateId (),
... dto ,
isCompleted : false ,
};
return await todoRepository . createOne ( newTodo );
};
const deleteOne = async ( id : string ) => {
if ( ! id ) {
throw new HttpError ( " Todo item id is not provided " , 400 );
}
const existingTodo = await todoRepository . getOneById ( id );
if ( ! existingTodo ) {
throw new HttpError ( `TodoItem(id= ${ id } ) is not found.` , 404 );
}
todoRepository . removeOneById ( existingTodo . id );
};
const updateOne = async ( dto : UpdateTodoDto , id : string ) => {
if ( ! id ) {
throw new HttpError ( " Todo item id is not provided " , 400 );
}
const existingTodo = await todoRepository . getOneById ( id );
if ( ! existingTodo ) {
throw new HttpError ( `TodoItem(id= ${ id } ) is not found.` , 404 );
}
if ( typeof dto . title !== " undefined " ) {
existingTodo . title = dto . title ;
}
if ( typeof dto . isCompleted !== " undefined " ) {
existingTodo . isCompleted = dto . isCompleted ;
}
await todoRepository . update ( existingTodo );
};
export const todoService = Object . freeze ({
getOnes ,
getOne ,
createOne ,
deleteOne ,
updateOne ,
});
存储库
lib/repositories/todo-repository.ts
import { initDB } from " db/db-config " ;
const db = initDB ();
const getOnes = async () => {
const { container } = await db ;
const { resources } = await container . items . readAll (). fetchAll ();
return resources ;
};
const getOneById = async ( id : string ) => {
const { container } = await db ;
const { resource } = await container . item ( id , id ). read ();
return resource ;
};
const createOne = async ( todo : any ) => {
const { container } = await db ;
const { resource } = await container . items . create ( todo );
return resource ;
};
const removeOneById = async ( id : string ) => {
const { container } = await db ;
await container . item ( id , id ). delete ();
};
const update = async ( todo : any ) => {
const { container } = await db ;
await container . item ( todo . id , todo . id ). replace ( todo );
};
export const todoRepository = Object . freeze ({
getOnes ,
getOneById ,
createOne ,
removeOneById ,
update ,
});
数据库设置
lib/db/db-config.ts
import { CosmosClient } from " @azure/cosmos " ;
const cosmosConfig = {
endpoint : process . env . COSMOSDB_URI ,
primaryKey : process . env . COSMOSDB_PRIMARY_KEY ,
database : process . env . COSMOSDB_DATABASE ,
container : process . env . COSMOSDB_CONTAINER ,
partitionKey : {
paths : [ " /id " ],
},
};
export const initDB = async () => {
const cosmosClient = new CosmosClient ({
endpoint : cosmosConfig . endpoint ,
key : cosmosConfig . primaryKey ,
});
const { database } = await cosmosClient . databases . createIfNotExists ({
id : cosmosConfig . database ,
});
const { container } = await database . containers . createIfNotExists ({
id : cosmosConfig . container ,
partitionKey : cosmosConfig . partitionKey ,
});
return {
cosmosClient ,
database ,
container ,
};
};
自定义错误
lib/errors/HttpError.ts
export class HttpError extends Error {
statusCode ;
constructor ( message , statusCode ) {
super ( message );
this . statusCode = statusCode ;
}
get StatusCode () {
return this . statusCode ;
}
}
效用
lib/utils/generate-id.ts
import * as crypto from " crypto " ; export const generateId = () => crypto . randomUUID ();
其他配置文件
主机.json
{
"version" : "2.0" ,
"logging" : {
"applicationInsights" : {
"samplingSettings" : {
"isEnabled" : true ,
"excludedTypes" : "Request"
}
}
},
"extensionBundle" : {
"id" : "Microsoft.Azure.Functions.ExtensionBundle" ,
"version" : "[3.*, 4.0.0)"
}
您可以使用 local.settings.json 设置环境变量。添加 COSMOSDB_URI 和 COSMOSDB_PRIMARY_KEY 作为环境变量以连接到CosmosDB。为 COSMOSDB_DATABASE 和 COSMOSDB_CONTAINER 指定任意值。
local.settings.json
{
"IsEncrypted" : false ,
"Values" : {
"AzureWebJobsStorage" : "" ,
"FUNCTIONS_WORKER_RUNTIME" : "node" ,
"COSMOSDB_URI" : "https://<COSMOSDB_RESOURCE_NAME>.documents.azure测试数据:443/" ,
"COSMOSDB_PRIMARY_KEY" : "<COSMOSDB_PRIMARY_KEY>" ,
"COSMOSDB_DATABASE" : "node_azure_functions_db" ,
"COSMOSDB_CONTAINER" : "todos"
}
}
COSMOSDB_URI 和 COSMOSDB_PRIMARY_KEY 的值可以在 azure 门户中的“CosmosDB 资源 => Settgins => Keys”中查看。
或者您可以使用以下命令获取它。
CosmosDB URI
az cosmosdb show
--resource-group ${ AZ_RESOURCE_GROUP }
--name ${ AZ_COSMOS_DB }
-o tsv
--query "documentEndpoint"
CosmosDB 主键
az cosmosdb keys list
--resource-group ${ AZ_RESOURCE_GROUP }
--name ${ AZ_COSMOS_DB }
--type "keys"
-o tsv
--query "primaryMasterKey"
调用 REST API
npm run start
? npm run start
> az-function-rest-api@1.0.0 prestart
> npm run build
> az-function-rest-api@1.0.0 build
> tsc
> az-function-rest-api@1.0.0 start
> func start
Azure Functions Core Tools
Core Tools Version: 4.0.4829 Commit hash : N/A ( 64-bit )
Function Runtime Version: 4.11.2.19273
[ 2022-10-26T02:25:55.113Z] Worker process started and initialized.
Functions:
delete-todos: [ DELETE] http://localhost:7071/api/todos/ { id }
get-todos: [ GET] http://localhost:7071/api/todos/ { id ? }
patch-todos: [ PATCH] http://localhost:7071/api/todos/ { id }
post-todos: [ POST] http://localhost:7071/api/todos
实际上,点击以下命令来测试 API。
curl -i -X POST -d '{"title":"todo 1"}' http://localhost:7071/api/todos
curl -i -X GET http://localhost:7071/api/todos
curl -i -X GET http://localhost:7071/api/todos/ { id }
curl -i -X PATCH -d '{"title":"todo 1 updated"}' http://localhost:7071/api/todos/ { id }
curl -i -X DELETE http://localhost:7071/api/todos/ { id }
添加待办事项
? curl -i -X POST -d '{"title":"todo 1"}' http://localhost:7071/api/todos
HTTP/1.1 201 Created
Content-Type: text/plain ; charset = utf-8
Date: Wed, 26 Oct 2022 02:53:40 GMT
Server: Kestrel
Transfer-Encoding: chunked
{
"id" : "10cb9c6f-6161-4798-8de5-e213a5e2be1d" ,
"title" : "todo 1" ,
"isCompleted" : false ,
"_rid" : "8T0VAJe9jpsDAAAAAAAAAA==" ,
"_self" : "dbs/8T0VAA==/colls/8T0VAJe9jps=/docs/8T0VAJe9jpsDAAAAAAAAAA==/" ,
"_etag" : " " c001744e-0000-2300-0000-6358a1350000 " " ,
"_attachments" : "attachments/" ,
"_ts" : 1666752821
} %
获取待办事项
? curl -i -X GET http://localhost:7071/api/todos
HTTP/1.1 200 OK
Content-Type: text/plain ; charset = utf-8
Date: Wed, 26 Oct 2022 02:55:41 GMT
Server: Kestrel
Transfer-Encoding: chunked
[
{
"id" : "10cb9c6f-6161-4798-8de5-e213a5e2be1d" ,
"title" : "todo 1" ,
"isCompleted" : false ,
"_rid" : "8T0VAJe9jpsDAAAAAAAAAA==" ,
"_self" : "dbs/8T0VAA==/colls/8T0VAJe9jps=/docs/8T0VAJe9jpsDAAAAAAAAAA==/" ,
"_etag" : " " c001744e-0000-2300-0000-6358a1350000 " " ,
"_attachments" : "attachments/" ,
"_ts" : 1666752821
}
] %
编辑待办事项
? curl -i -X PATCH -d '{"title":"todo 1 updated"}' http://localhost:7071/api/todos/10cb9c6f-6161-4798-8de5-e213a5e2be1d
HTTP/1.1 204 No Content
Content-Type: text/plain ; charset = utf-8
Date: Wed, 26 Oct 2022 02:56:31 GMT
Server: Kestrel
通过 ID 获取待办事项
? curl -i -X GET http://localhost:7071/api/todos/10cb9c6f-6161-4798-8de5-e213a5e2be1d
HTTP/1.1 200 OK
Content-Type: text/plain ; charset = utf-8
Date: Wed, 26 Oct 2022 02:57:34 GMT
Server: Kestrel
Transfer-Encoding: chunked
{
"id" : "10cb9c6f-6161-4798-8de5-e213a5e2be1d" ,
"title" : "todo 1 updated" ,
"isCompleted" : false ,
"_rid" : "8T0VAJe9jpsDAAAAAAAAAA==" ,
"_self" : "dbs/8T0VAA==/colls/8T0VAJe9jps=/docs/8T0VAJe9jpsDAAAAAAAAAA==/" ,
"_etag" : " " c0013f9b-0000-2300-0000-6358a1e00000 " " ,
"_attachments" : "attachments/" ,
"_ts" : 1666752992
} %
确认数据已添加到 Azure 门户端的 CosmosDB。
创建更改提要侦听器函数
npx func new --name change-feed-listener --template 'Azure Cosmos DB trigger' --language typescript
. ├── change-feed-listener # <= 追加 │ ├── function .json │ └── index.ts ├── delete-todos │ ├── function .json │ └── index.ts ├── get-todos │ ├── function .json │ └── index.ts ├── host.json ├── lib │ ├── db │ │ └── db-config.ts │ ├── dtos │ │ ├── CreateTodoDto.ts │ │ └── UpdateTodoDto.ts │ ├── errors │ │ └── HttpError.ts │ ├── models │ │ └── TodoItem.ts │ ├── repositories │ │ └── todo-repository.ts │ ├── services │ │ └── todo-service.ts │ └── utils │ └── generate-id.ts ├── local.settings.json ├── package-lock.json ├── package.json ├── patch-todos │ ├── function .json │ └── index.ts ├── post-todos │ ├── function .json │ └── index.ts └── tsconfig.json
cosmosDBTrigger 在连接的 CosmosDB 中添加或编辑数据时将执行该函数,并传递该记录的数据。擦除数据时不执行 cosmosDBTrigger 。
更改提要监听器/index.ts
import { AzureFunction , Context } from " @azure/functions "
const cosmosDBTrigger : AzureFunction = async function ( context : Context , documents : any []): Promise < void > {
if ( !! documents && documents . length > 0 ) {
context . log ( ' Document: ' , documents );
}
}
export default cosmosDBTrigger ;
如果将 connectionStringSetting 的值直接替换为连接字符串的值,则会发生错误。将实际值写入 local.settings.json ,并将 connectionStringSetting 的值保留为 COSMOSDB_CONNECTION_STRING_DOCUMENTDB 。
更改馈送侦听器/function.json
{
"bindings" : [
{
"type" : "cosmosDBTrigger" ,
"name" : "documents" ,
"direction" : "in" ,
"leaseCollectionName" : "leases" ,
"connectionStringSetting" : "COSMOSDB_CONNECTION_STRING_DOCUMENTDB" , # <= local.settings.jsonの値を参照。
"databaseName" : "node_azure_functions_db" ,
"collectionName" : "todos" ,
"createLeaseCollectionIfNotExists" : true
}
],
"scriptFile" : "dist/src/change-feed-listener/index.js"
}
编辑 local.settings.json 并添加 COSMOSDB_CONNECTION_STRING_DOCUMENTDB 。 COSMOSDB_CONNECTION_STRING_DOCUMENTDB 的值是 COSMOSDB_URI 和 COSMOSDB_PRIMARY_KEY 的组合。请务必在键名末尾添加 _DOCUMENTDB (需要引用来自 function.json 的值)。
local.settings.json
{
"IsEncrypted" : false ,
"Values" : {
"AzureWebJobsStorage" : "" ,
"FUNCTIONS_WORKER_RUNTIME" : "node" ,
"COSMOSDB_URI" : "https://<COSMOSDB_NAME>.documents.azure测试数据:443/" ,
"COSMOSDB_PRIMARY_KEY" : "<COSMOSDB_PRIMARY_KEY>" ,
"COSMOSDB_DATABASE" : "node_azure_functions_db" ,
"COSMOSDB_CONTAINER" : "todos"
# 追加
"COSMOSDB_CONNECTION_STRING_DOCUMENTDB" : "AccountEndpoint=https://<COSMOSDB_NAME>.documents.azure测试数据:443/;AccountKey=<COSMOSDB_PRIMARY_KEY>;"
}
}
或者您可以从 azure 门户中的“CosmosDB 资源 => Settgins => Keys”进行检查。
npm run start
添加了一个新的 change-feed-listener: cosmosDBTrigger 作为函数。
? npm run start
> az-function-rest-api@1.0.0 prestart
> npm run build
> az-function-rest-api@1.0.0 build
> tsc
> az-function-rest-api@1.0.0 start
> func start
Azure Functions Core Tools
Core Tools Version: 4.0.4829 Commit hash : N/A ( 64-bit )
Function Runtime Version: 4.11.2.19273
[ 2022-10-26T05:15:06.386Z] Worker process started and initialized.
Functions:
delete-todos: [ DELETE] http://localhost:7071/api/todos/ { id }
get-todos: [ GET] http://localhost:7071/api/todos/ { id ? }
patch-todos: [ PATCH] http://localhost:7071/api/todos/ { id }
post-todos: [ POST] http://localhost:7071/api/todos
change-feed-listener: cosmosDBTrigger # <= 追加
当我实际添加数据时。
? curl -i -X POST -d '{"title":"todo 1"}' http://localhost:7071/api/todos
HTTP/1.1 201 Created
Content-Type: text/plain ; charset = utf-8
Date: Wed, 26 Oct 2022 05:40:11 GMT
Server: Kestrel
Transfer-Encoding: chunked
{
"id" : "29c84398-6b37-4dfd-ad8b-45403a62f4d7" ,
"title" : "todo 1" ,
"isCompleted" : false ,
"_rid" : "8T0VAJe9jpsEAAAAAAAAAA==" ,
"_self" : "dbs/8T0VAA==/colls/8T0VAJe9jps=/docs/8T0VAJe9jpsEAAAAAAAAAA==/" ,
"_etag" : " " cf01af89-0000-2300-0000-6358c83b0000 " " ,
"_attachments" : "attachments/" ,
"_ts" : 1666762811
} %
change-feed-listener 日志显示从更改源接收到的数据,确认 change-feed-listener 函数已成功执行。
[ 2022-10-26T05:40:16.500Z] Executing 'Functions.change-feed-listener' ( Reason = 'New changes on collection todos at 2022-10-26T05:40:16.4919090Z' , Id = 50591fbf-9ef8-49cc-b34a-abbdca3631d1 )
[ 2022-10-26T05:40:16.519Z] Document: [
[ 2022-10-26T05:40:16.519Z] {
[ 2022-10-26T05:40:16.519Z] id : '29c84398-6b37-4dfd-ad8b-45403a62f4d7' ,
[ 2022-10-26T05:40:16.519Z] _rid: '8T0VAJe9jpsEAAAAAAAAAA==' ,
[ 2022-10-26T05:40:16.519Z] _self: 'dbs/8T0VAA==/colls/8T0VAJe9jps=/docs/8T0VAJe9jpsEAAAAAAAAAA==/' ,
[ 2022-10-26T05:40:16.519Z] _ts: 1666762811,
[ 2022-10-26T05:40:16.519Z] _etag: '"cf01af89-0000-2300-0000-6358c83b0000"' ,
[ 2022-10-26T05:40:16.519Z] title: 'todo 1' ,
[ 2022-10-26T05:40:16.519Z] isCompleted: false ,
[ 2022-10-26T05:40:16.519Z] _lsn: 11
[ 2022-10-26T05:40:16.519Z] }
[ 2022-10-26T05:40:16.519Z] ]
功能部署
只需在项目根目录执行以下命令即可轻松完成部署。
npx func azure functionapp publish ${ AZ_FUNCTION }
? npx func azure functionapp publish myTestFunction231917910
Setting Functions site property 'netFrameworkVersion' to 'v6.0'
Getting site publishing info...
Creating archive for current directory...
Uploading 1.61 MB [ #########################################################]
Upload completed successfully.
Deployment completed successfully.
Syncing triggers...
Functions in myTestFunction231917910:
change-feed-listener - [ cosmosDBTrigger]
delete-todos - [ httpTrigger]
Invoke url: https://mytestfunction231917910.azurewebsites.net/api/todos/ { id }
get-todos - [ httpTrigger]
Invoke url: https://mytestfunction231917910.azurewebsites.net/api/todos/ { id ? }
patch-todos - [ httpTrigger]
Invoke url: https://mytestfunction231917910.azurewebsites.net/api/todos/ { id }
post-todos - [ httpTrigger]
Invoke url: https://mytestfunction231917910.azurewebsites.net/api/todos
您可以使用以下命令查看已部署函数的日志。
npx func azure functionapp logstream ${ AZ_FUNCTION }
结尾
我能够使用 Node.js (TypeScript) 和 Azure Functions 创建一个无服务器 REST API 应用程序。如果使用 azure 函数,则可以高速开发应用程序。部署也很容易。您可以轻松地接收来自 CosmosDB 的更改源,因此您可以轻松地添加以 azure 函数为中心的微服务应用程序。
原创声明:本文系作者授权爱码网发表,未经许可,不得转载;
原文地址:https://HdhCmsTestlikecs测试数据/show-308631507.html
查看更多关于使用 Node.js (TypeScript) 和 Azure Functions 创建无服务器 R的详细内容...