介绍
在本文中,我们将使用 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.com: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.com: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.com: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://www.likecs.com/show-308631507.html
查看更多关于使用 Node.js (TypeScript) 和 Azure Functions 创建无服务器 R的详细内容...