Lambda Annotations Framework for .NET が GA となったのでプレビューからの変更点を確認してみた

2023.07.21

いわさです。

.NET Lambda で利用出来るフレームワークに Lambda Annotaions Framework というものがあります。

2022 年 4 月に AWS 公式ブログで紹介されたもので、.NET を Lambda で使用するとどうしてもインターフェースなどに冗長な記述が発生してしまうのですが、C# のソースコードジェネレータの仕組みを使ってそのあたりをうまく隠蔽してくれるフレームワークです。

これまでずっとプレビューという位置づけだったのですが、つい先日 GA になったとアップデートアナウンスがありました。

Lambda Annotations Framework の概要

アノテーションというのは、C# の文脈でいうとクラスやメソッドに付与出来るメタデータ・属性を指しています。
色々な用途で使われるのですが、ビルド時やあるいは実行時に動的に何かカスタム処理を行う際に利用されることが多いです。

Lambda Annotations Framework を使わない場合、例えば API Gateway と Lambda を統合する場合であれば次のような実装が従来必要です。

Functions.cs

public class Functions
{
    public APIGatewayProxyResponse LambdaMathPlus(APIGatewayProxyRequest request, ILambdaContext context)
    {
        if (!request.PathParameters.TryGetValue("x", out var xs))
        {
            return new APIGatewayProxyResponse
            {
                StatusCode = (int)HttpStatusCode.BadRequest
            };
        }
        if (!request.PathParameters.TryGetValue("y", out var ys))
        {
            return new APIGatewayProxyResponse
            {
                StatusCode = (int)HttpStatusCode.BadRequest
            };
        }

        var x = int.Parse(xs);
        var y = int.Parse(ys);

        return new APIGatewayProxyResponse
        {
            StatusCode = (int)HttpStatusCode.OK,
            Body = (x + y).ToString(),
            Headers = new Dictionary<string, string> { { "Content-Type", "text/plain" } }
        };
    } 
}

ビジネスロジックと関係ないコード多すぎ問題が発生します。
これを次のように、今回 GA となったフレームワークのアノテーションを使うことでとてもシンプルに実装出来るようになります。

public class Functions
{
    [LambdaFunction]
    [RestApi("/plus/{x}/{y}")]
    public int Plus(int x, int y)
    {
        return x + y;
    }
}

実際にはコンパイル時に上記メタデータを検出して、本来必要だった冗長なコードを自動生成してくれるような仕組みとなっています。
NuGet パッケージマネージャーから導入することも出来ますし、Visual Studio や .NET CLI のテンプレートとしても提供されているので簡単に使い始めることが出来ます。

変更点を確認

導入や使用方法は 2022 年 12 月に次の記事で紹介していますのでこちらを見てもらえると。
フレームワークの概念自体はそのころと全く変わっていないです。

アップデート方法

既にプレビュー版を導入済みだった方はアップデートが必要です。
Amazon.Lambda.ToolsAmazon.Lambda.Templatesをアップデートしましょう。

% dotnet tool update -g Amazon.Lambda.Tools
Tool 'amazon.lambda.tools' was successfully updated from version '5.6.3' to version '5.7.2'.

% dotnet new install Amazon.Lambda.Templates::6.14.0
The following template packages will be installed:
   Amazon.Lambda.Templates::6.14.0

Amazon.Lambda.Templates (version 6.10.0) is already installed, it will be replaced with version 6.14.0.
Amazon.Lambda.Templates::6.10.0 was successfully uninstalled.
Success: Amazon.Lambda.Templates::6.14.0 installed the following templates:
Template Name                                                                      Short Name                                    Language  Tags                            
---------------------------------------------------------------------------------  --------------------------------------------  --------  --------------------------------
Empty Top-level Function                                                           lambda.EmptyTopLevelFunction                  [C#]      AWS/Lambda/Serverless           
Lambda Annotations Framework Sample                                                serverless.Annotations                        [C#]      AWS/Lambda/Serverless           
Lambda ASP.NET Core Minimal API                                                    serverless.AspNetCoreMinimalAPI               [C#]      AWS/Lambda/Serverless           
Lambda ASP.NET Core Web API                                                        serverless.AspNetCoreWebAPI                   [C#],F#   AWS/Lambda/Serverless           
Lambda ASP.NET Core Web API (.NET 6 Container Image)                               serverless.image.AspNetCoreWebAPI             [C#],F#   AWS/Lambda/Serverless           
Lambda ASP.NET Core Web Application with Razor Pages                               serverless.AspNetCoreWebApp                   [C#]      AWS/Lambda/Serverless           
Lambda Custom Runtime Function (.NET 7)                                            lambda.CustomRuntimeFunction                  [C#],F#   AWS/Lambda/Function             
Lambda Detect Image Labels                                                         lambda.DetectImageLabels                      [C#],F#   AWS/Lambda/Function             
Lambda Empty Function                                                              lambda.EmptyFunction                          [C#],F#   AWS/Lambda/Function             
Lambda Empty Function (.NET 7 Container Image)                                     lambda.image.EmptyFunction                    [C#],F#   AWS/Lambda/Function             
Lambda Empty Serverless                                                            serverless.EmptyServerless                    [C#],F#   AWS/Lambda/Serverless           
:

テンプレートからもPreviewの表記がなくなりました。

アップデート内容

前回の記事が0.10.0-preview時点のもので、それから毎月定期的にアップデートされているようです。
更新内容をサマリしてみました。

IHttpResult と HttpResults タイプを使ったステータスコードとヘッダーのレスポンスカスタマイズをサポート

次のようにメソッドの返却値に IHttpResult が使えるようになっていますね。

[LambdaFunction(PackageType = LambdaPackageType.Image)]
[HttpApi(LambdaHttpMethod.Get, "/resource/{id}")]
public IHttpResult NotFoundResponseWithHeaderV2(int id, ILambdaContext context)
{
    return HttpResults.NotFound($"Resource with id {id} could not be found")
                        .AddHeader("Custom-Header1", "Value1");
}

aws-lambda-dotnet/Libraries/src/Amazon.Lambda.Annotations at master · aws/aws-lambda-dotnet より

また、上記が実装された際には 5xx エラーを返却する方法がなく、結局 APIGatewayProxyResponse を使わざるを得ないケースがあったのですが、HttpResults.InternalServerErrorが使用出来るようになったので、サーバーエラーを直感的に処理出来るようにもアップデートされています。

[LambdaFunction(PackageType = LambdaPackageType.Image)]
[HttpApi(LambdaHttpMethod.Get, "/resource/{id}")]
public IHttpResult NotFoundResponseWithHeaderV2(int id, ILambdaContext context)
{
    try
    {
        //hoge process
        return HttpResults.NotFound($"Resource with id {id} could not be found")
                            .AddHeader("Custom-Header1", "Value1");
    }
    catch (Exception e)
    {
        return HttpResults.InternalServerError("hoge error");
    }
}

また、IHttpResultからステータスコードを取得出来るようにも修正されているので関数をテストするコードでステータスを参照することも出来るようになっています。

public class HtttpResultsStatusCodeUsage
{
    [Fact]
    public void UsageOfIHttpResultStatusCode()
    {
        var sut = new Functions();
        var result = sut.NotFoundResponseWithHeaderV2(1, null);
        Assert.Equal(HttpStatusCode.OK, result.StatusCode);
    }
}

バグの修正や安定性の向上

コードジェネレータによる生成失敗時にエラー内容が正しく報告されない問題などがありましたが修正されています。
また、診断メッセージ自体にもフォーマット崩れなどの問題がありましたが、それも解消しています。

診断メッセージ関係の小さなアップデートが多く、トラブルシューティングしやすくなっていそうです。

静的チェック機能の強化

コンパイルにあたっていくつかの前提条件がありますが、コンパイルエラーで検出出来るように強化されています。
例えばアセンブリ属性にLambdaSerailizerAttributeが指定されていない場合や、API Gateway 使用時にAmazon.Lambda.APIGatewayEventsが参照されていない場合などはコンパイルエラーとなります。

さいごに

本日は Lambda Annotations Framework for .NET が GA となったのでプレビューからの変更点を確認してみました。

まず、GA ということでプロダクトでも採用しやすくなったと思います。
プレビュー時には破壊的なアップデートがあったりしましたが、これからはより安定した形で導入しやすくなったのではないでしょうか。

前回紹介した時からの変更点としては各種安定性が向上した他に、IHttpResultをつかって ASP.NET に近い形で HTTP レスポンスが表現出来るようになった点でしょうか。