以 C# 編寫的 Lambda 函數處理常式 - AWS Lambda

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

以 C# 編寫的 Lambda 函數處理常式

Lambda 函數處理常式是您的函數程式碼中處理事件的方法。當有人呼叫您的函數時,Lambda 會執行處理常式方法。函數會執行,直到處理常式傳回回應、結束或逾時為止。

當您的函數遭調用,且 Lambda 執行了函數的處理常式方法時,系統會將兩個引數傳遞給函數。第一個引數是 event 物件。當另一個 AWS 服務 調用您的函數時,該event對象包含有關導致您的函數被調用的事件的數據。例如,來自 API Gateway 的 event 物件中,包含路徑、HTTP 方法和 HTTP 標頭的相關資訊。確切的事件結構根據 AWS 服務 調用函數而有所不同。如需個別服務事件格式的詳細資訊,請參閱:AWS Lambda 搭配其他服務使用

Lambda 也會傳遞 context 物件給函數。此物件包含有關調用、函數以及執行環境的資訊。如需詳細資訊,請參閱 C# 中的 AWS Lambda 內容物件

所有 Lambda 事件的原生格式,都是代表 JSON 格式事件的位元組串流。除非您的函數輸入與輸出參數為 System.IO.Stream 類型,否則必須將其序列化。透過設定 LambdaSerializer 組件屬性來指定要使用的序列化程式。如需詳細資訊,請參閱 Lambda 函數中的序列化

適用於 Lambda 的 .NET 執行模型

在 .NET 中執行 Lambda 函數有兩種不同的執行模型:類別庫可執行組件做法。

若使用類別庫做法,您可以為 Lambda 提供一個字串,指出要調用之函數的 AssemblyNameClassName、和 Method。如需此字串格式的詳細資訊,請參閱:類別庫處理常式。在函數的初始化階段,系統會初始化函數的類別,並執行建構函數中的任何程式碼。

若使用可執行組件做法,您可以使用 C# 9 的頂層陳述式功能。此方法會產生一個可執行組件,每當 Lambda 收到函數的調用命令時,就會執行該組件。您只需提供要執行的可執行組件名稱給 Lambda。

以下各節提供這兩種做法的範例函數程式碼。

類別庫處理常式

下列 Lambda 函數程式碼顯示使用類別庫做法之 Lambda 函數的處理常式方法 (FunctionHandler) 範例。在此範例函數中,Lambda 收到調用函數的 API Gateway 傳來的事件。函數會從資料庫讀取記錄,並在 API Gateway 回應中傳回該記錄。

[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] namespace GetProductHandler; public class Function { private readonly IDatabaseRepository _repo; public Function() { this._repo = new DatabaseRepository(); } public async Task<APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyRequest request) { var id = request.PathParameters["id"]; var databaseRecord = await this._repo.GetById(id); return new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.OK, Body = JsonSerializer.Serialize(databaseRecord) }; } }

建立 Lambda 函數時,您需要以處理常式字串的形式向 Lambda 提供函數處理常式的相關資訊。目的是指示 Lambda 在調用函數時要在程式碼中執行哪種方法。若使用 C#,使用類別庫做法時處理常式字串的格式如下:

ASSEMBLY::TYPE::METHOD,其中:

  • ASSEMBLY 是應用程式的 .NET 組件名稱。如果您使用 Amazon.Lambda.Tools CLI 建置應用程式,而沒有使用 .csproj 檔案中的 AssemblyName 屬性來設定組件名稱,則 ASSEMBLY 單純為 .csproj 檔案的名稱。

  • TYPE 是處理常式類型的完整名稱,包含 NamespaceClassName

  • METHOD 是程式碼中函數處理常式方法的名稱。

在顯示的範例程式碼中,如果組件名為 GetProductHandler,則處理常式字串會是 GetProductHandler::GetProductHandler.Function::FunctionHandler

可執行組件處理常式

在下列範例中,Lambda 函數定義為可執行的組件。此程式碼中的處理常式方法名為 Handler。使用可執行組件時,必須引導 Lambda 執行期。若要執行此作業,請使用 LambdaBootstrapBuilder.Create 方法。此方法的輸入是函數當成處理常式使用的方法,以及要使用的 Lambda 序列化程式。

如需有關使用頂層陳述式的詳細資訊,請參閱 AWS 運算部落格 AWS Lambda上的 .NET 6 執行階段簡介

namespace GetProductHandler; IDatabaseRepository repo = new DatabaseRepository(); await LambdaBootstrapBuilder.Create<APIGatewayProxyRequest>(Handler, new DefaultLambdaJsonSerializer()) .Build() .RunAsync(); async Task<APIGatewayProxyResponse> Handler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context) { var id = input.PathParameters["id"]; var databaseRecord = await this.repo.GetById(id); return new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.OK, Body = JsonSerializer.Serialize(databaseRecord) }; };

使用可執行組件時,指示 Lambda 如何執行程式碼的處理常式字串就是組件的名稱。在這個範例中,即為 GetProductHandler

Lambda 函數中的序列化

若您的 Lambda 函數使用輸入或輸出類型而非 Stream 物件,您必須將序列化程式庫新增至您的應用程式。若要實作序列化,您可以使用 System.Text.JsonNewtonsoft.Json 提供的標準反射式序列化,或是使用原始碼產生的序列化

使用原始碼產生的序列化

源代碼生成的序列化是 .NET 版本 6 及更高版本的功能,允許在編譯時生成序列化代碼。此方式不需要使用反射,且可提高函數的效能。若要在函數中使用原始碼產生的序列化,請執行以下操作:

  • 建立一個繼承自 JsonSerializerContext 的新部分分類,為需要序列化或還原序列化的所有類型新增 JsonSerializable 屬性。

  • 設定 LambdaSerializer 以使用 SourceGeneratorLambdaJsonSerializer<T>

  • 更新應用程式程式碼中的任何手動序列化或還原序列化,以使用新建立的類別。

以下程式碼顯示的範例函數,是使用原始碼產生的序列化。

[assembly: LambdaSerializer(typeof(SourceGeneratorLambdaJsonSerializer<CustomSerializer>))] public class Function { private readonly IDatabaseRepository _repo; public Function() { this._repo = new DatabaseRepository(); } public async Task<APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyRequest request) { var id = request.PathParameters["id"]; var databaseRecord = await this._repo.GetById(id); return new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.OK, Body = JsonSerializer.Serialize(databaseRecord, CustomSerializer.Default.Product) }; } } [JsonSerializable(typeof(APIGatewayProxyRequest))] [JsonSerializable(typeof(APIGatewayProxyResponse))] [JsonSerializable(typeof(Product))] public partial class CustomSerializer : JsonSerializerContext { }
注意

如果您想透過 Lambda 使用原生預先編譯 (AOT),必須使用原始碼產生的序列化。

使用反射式序列化

AWS 提供預先建置的程式庫,讓您快速將序列化新增至應用程式。您可以使用Amazon.Lambda.Serialization.SystemTextJsonAmazon.Lambda.Serialization.Json NuGet 套件來設定此項目。Amazon.Lambda.Serialization.SystemTextJson 會在背景使用 System.Text.Json 來執行序列化任務,而 Amazon.Lambda.Serialization.Json 會使用 Newtonsoft.Json 套件。

您也可以實作 ILambdaSerializer 界面 (隨 Amazon.Lambda.Core 程式庫提供) 來建立自己的序列化程式庫。此界面定義了兩種方法:

  • T Deserialize<T>(Stream requestStream);

    若實作此方法,會將請求承載從 Invoke API 還原序列化至傳遞到 Lambda 函數處理常式的物件。

  • T Serialize<T>(T response, Stream responseStream);

    若實作此方法,會將從 Lambda 函數處理常式傳回的結果,序列化至 Invoke API 作業傳回的回應承載中。

使用 Lambda Annotations 架構簡化函數程式碼

Lambda 註解是 .NET 6 和 .NET 8 的框架,它簡化了使用 C# 編寫 Lambda 函數的過程。透過 Annotations 架構,您可以取代使用一般程式設計模型編寫的大部分 Lambda 函數程式碼。使用此架構編寫的程式碼使用更簡單的表達式,讓您可專注於商業邏輯。

以下範例程式碼示範使用 Annotations 架構可如何簡化編寫 Lambda 函數的程序。第一個範例顯示使用一般 Lambda 程式模型編寫的程式碼,第二個範例顯示使用 Annotations 架構的對等程式碼。

public APIGatewayHttpApiV2ProxyResponse LambdaMathAdd(APIGatewayHttpApiV2ProxyRequest request, ILambdaContext context) { if (!request.PathParameters.TryGetValue("x", out var xs)) { return new APIGatewayHttpApiV2ProxyResponse { StatusCode = (int)HttpStatusCode.BadRequest }; } if (!request.PathParameters.TryGetValue("y", out var ys)) { return new APIGatewayHttpApiV2ProxyResponse { StatusCode = (int)HttpStatusCode.BadRequest }; } var x = int.Parse(xs); var y = int.Parse(ys); return new APIGatewayHttpApiV2ProxyResponse { StatusCode = (int)HttpStatusCode.OK, Body = (x + y).ToString(), Headers = new Dictionary≪string, string> { { "Content-Type", "text/plain" } } }; }
[LambdaFunction] [HttpApi(LambdaHttpMethod.Get, "/add/{x}/{y}")] public int Add(int x, int y) { return x + y; }

如需使用 Lambda 註解如何簡化程式碼的另一個範例,請參閱awsdocs/aws-doc-sdk-examples GitHub 儲存庫中的這個跨服務範例應用程式。PamApiAnnotations 資料夾在主要 function.cs 檔案中使用 Lambda Annotations。為了進行比較,PamApi 資料夾包含使用一般 Lambda 程式設計模型編寫的對等檔案。

Annotations 架構使用原始碼產生器來產生程式碼,會將 Lambda 程式設計模型轉換為第二個範例中呈現的程式碼。

如需如何使用 Lambda Annotations for .NET 的詳細資訊,請參閱下列資源:

使用 Lambda Annotations 架構進行相依性插入

您也可以透過 Lambda Annotations 架構,使用熟悉的語法將相依性插入新增至 Lambda 函數。將 [LambdaStartup] 屬性新增至 Startup.cs 檔案時,Lambda Annotations 架構會在編譯時產生所需的程式碼。

[LambdaStartup] public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddSingleton<IDatabaseRepository, DatabaseRepository>(); } }

Lambda 函數可以使用建構函數插入來插入服務,或使用 [FromServices] 屬性插入個別方法中。

[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] namespace GetProductHandler; public class Function { private readonly IDatabaseRepository _repo; public Function(IDatabaseRepository repo) { this._repo = repo; } [LambdaFunction] [HttpApi(LambdaHttpMethod.Get, "/product/{id}")] public async Task<Product> FunctionHandler([FromServices] IDatabaseRepository repository, string id) { return await this._repo.GetById(id); } }

Lambda 函數處理常式限制

請注意,處理常式簽章有若干限制。

  • 處理常式簽章不能為 unsafe,且不得使用指標類型,但可以在處理函式方法及其依存項目中使用 unsafe 內容。如需詳細資訊,請參閱 Microsoft 文檔網站上的不安全 (C# 參考)

  • 處理常式不會傳送使用 params 關鍵字的參數變數,也不得使用支援參數變數的 ArgIterator 作為輸入或傳回參數。

  • 處理常式不得為通用方法,例如 IList<T> Sort<T>(IList<T> input)。

  • 不支援具有 async void 簽章的非同步處理常式。