Xây dựng và Gỡ lỗi ứng dụng .NET Lambda với .NET Aspire (Phần 1)

Tác giả: [Norm Johanson]
Ngày phát hành: 03 THÁNG 3, 2025
Chuyên mục: .NET, AWS Lambda

Trong một bài viết gần đây, chúng tôi đã cung cấp một số thông tin nền tảng về .NET Aspire và giới thiệu các tích hợp AWS với .NET Aspire, giúp đưa AWS vào vòng lặp phát triển nội bộ (dev inner loop) của .NET để xây dựng ứng dụng. Các tích hợp này bao gồm cách cung cấp tài nguyên ứng dụng với AWS CloudFormation hoặc AWS Cloud Development Kit (AWS CDK) và sử dụng Amazon DynamoDB local cho việc phát triển tại chỗ. Những tính năng này giúp các nhà phát triển .NET duy trì năng suất trong môi trường phát triển quen thuộc của họ.

AWS Lambda có mô hình lập trình riêng để phản hồi các sự kiện, và chúng tôi thường nhận được câu hỏi về cách chạy và gỡ lỗi (debug) các hàm Lambda tại môi trường local; về cơ bản, đó là làm thế nào để “F5 debug” một tập hợp các hàm Lambda tạo nên một ứng dụng serverless. Hôm nay, chúng tôi đã phát hành một bản xem trước cho nhà phát triển (developer preview) hỗ trợ phát triển Lambda tại local, cung cấp tính năng này với trải nghiệm quen thuộc của .NET.

Với sự hỗ trợ mới cho Lambda, bạn có thể sử dụng app host của .NET Aspire để điều phối các hàm Lambda tạo nên ứng dụng serverless của mình. Khi app host được khởi chạy từ Visual Studio, tất cả các hàm Lambda đều được kết nối với trình gỡ lỗi (debugger). Ngoài việc kết nối các hàm Lambda với app host, các hàm này còn có thể được điều phối thông qua một trình giả lập API Gateway để cung cấp trải nghiệm đầy đủ về việc gọi các hàm Lambda thông qua một REST API.

Developer Preview

Các tính năng Lambda của chúng tôi hiện đang trong giai đoạn developer preview. Để biểu thị trạng thái xem trước, các API Lambda trong gói Aspire.Hosting.AWS của chúng tôi đã được đánh dấu bằng thuộc tính .NET RequiresPreviewFeatures. Để sử dụng API Lambda, bạn cần chọn tham gia các tính năng xem trước; nếu không, một lỗi biên dịch sẽ xảy ra.

Bạn có thể chọn tham gia các tính năng xem trước của Lambda bằng cách thêm dòng sau vào đầu tệp Program.cs của app host.

#pragma warning disable CA2252 // Opt in to preview features

Một vấn đề đã biết với bản developer preview là IDE Rider từ JetBrains không thể khởi động các hàm Lambda được định nghĩa trong một class library. Các hàm Lambda được viết dưới dạng một tệp thực thi, còn được gọi là sử dụng top level statements, sẽ hoạt động không có vấn đề gì trong Rider. Đội ngũ Rider đã có một bản sửa lỗi giải quyết vấn đề với class library của Lambda, và sẽ có sẵn trong một bản phát hành sắp tới.

Bắt đầu

Để bắt đầu sử dụng .NET Aspire với Lambda, hãy cùng xem qua cách bạn có thể kích hoạt .NET Aspire cho một dự án .NET Lambda hiện có. Trong kịch bản này, giả sử tôi có một dự án .NET Lambda tên là “LambdaWebCalculator”, là một tập hợp các hàm Lambda được phơi bày dưới dạng một REST API thông qua Amazon API Gateway. Các bước được sử dụng để kích hoạt .NET Aspire cho hàm này có thể được áp dụng cho bất kỳ dự án .NET Lambda nào, nhưng để có ngữ cảnh, đây là một đoạn mã mẫu trong dự án .NET Lambda đã đề cập.

using Amazon.Lambda.APIGatewayEvents;
using Amazon.Lambda.Core;

[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace LambdaWebCalculator;

public class Functions
{
    public APIGatewayHttpApiV2ProxyResponse AddFunctionHandler(APIGatewayHttpApiV2ProxyRequest request, ILambdaContext context)
    {
        context.Logger.LogDebug("Starting adding operation");    
        var x = (int)Convert.ChangeType(request.PathParameters["x"], typeof(int));
        var y = (int)Convert.ChangeType(request.PathParameters["y"], typeof(int));
        var sum = x + y;
        context.Logger.LogInformation("Adding {x} with {y} is {sum}", x, y, sum);
        var response = new APIGatewayHttpApiV2ProxyResponse
        {
            StatusCode = 200,
            Headers = new Dictionary&t;string, string>
                    {
                        {"Content-Type", "application/json" }
                    },
            Body = sum.ToString()
        };

        return response;
    }

... // Similar functions for minus, multiply, and divide

}

Thiết lập dự án app host .NET Aspire

Để bắt đầu sử dụng .NET Aspire để chạy và gỡ lỗi các hàm Lambda của chúng ta, chúng ta cần tạo một dự án app host .NET Aspire. Dự án này đại diện cho việc điều phối cục bộ để chạy các thành phần của ứng dụng phân tán. Trong kịch bản Lambda của chúng ta, app host điều phối việc chạy tất cả các hàm Lambda.

Sử dụng các bước sau để tạo một app host cho Lambda.

  1. Trong solution, thêm một dự án mới sử dụng mẫu dự án .NET Aspire App Host. Đối với phiên bản .NET Aspire, bạn nên chọn phiên bản mới nhất. Tại thời điểm viết bài này, phiên bản .NET Aspire mới nhất là 9.1. Quy ước chung là đặt tên dự án app host là <solution-name>.AppHost.
Tạo dự án .NET Aspire App Host
  1. Trên AppHost, thêm một tham chiếu NuGet đến Aspire.Hosting.AWS phiên bản 9.1.4 trở lên.
  2. Trên dự án AppHost, thêm một tham chiếu dự án đến dự án Lambda.
Thêm tham chiếu dự án đến dự án Lambda
  1. Trong tệp Program.cs của AppHost, chúng ta cần thêm #pragma để cho phép sử dụng các API Lambda xem trước từ Aspire.Hosting.AWS. Sau đó, sử dụng phương thức AddAWSLambdaFunction để đăng ký tất cả các hàm Lambda chúng ta muốn chạy và gỡ lỗi tại local. Trong ví dụ của tôi, tôi có bốn hàm Lambda cho các tính năng Cộng, Trừ, Nhân và Chia của máy tính REST API của mình.
#pragma warning disable CA2252

var builder = DistributedApplication.CreateBuilder(args);

builder.AddAWSLambdaFunction<Projects.LambdaWebCalculator>("AddFunction",
        lambdaHandler: "LambdaWebCalculator::LambdaWebCalculator.Functions::AddFunctionHandler");
builder.AddAWSLambdaFunction<Projects.LambdaWebCalculator>("MinusFunction",
        lambdaHandler: "LambdaWebCalculator::LambdaWebCalculator.Functions::MinusFunctionHandler");
builder.AddAWSLambdaFunction<Projects.LambdaWebCalculator>("MultipyFunction",
        lambdaHandler: "LambdaWebCalculator::LambdaWebCalculator.Functions::MultiplyFunctionHandler");
builder.AddAWSLambdaFunction<Projects.LambdaWebCalculator>("DivideFunction",
        lambdaHandler: "LambdaWebCalculator::LambdaWebCalculator.Functions::DivideFunctionHandler");

builder.Build().Run();
  1. Đặt dự án AppHost làm dự án khởi động (startup project) và nhấn F5 để khởi chạy trình gỡ lỗi của Visual Studio.
  2. Từ bảng điều khiển Aspire, các hàm Lambda được thêm vào như một tài nguyên cùng với trình giả lập dịch vụ Lambda. Để gỡ lỗi một hàm Lambda, nhấp vào biểu tượng gỡ lỗi trong cột Actions cho dự án Lambda.
Bảng điều khiển Aspire hiển thị các hàm Lambda
  1. Biểu tượng gỡ lỗi sẽ khởi chạy trang kiểm thử Lambda nơi các sự kiện Lambda có thể được gọi. Các điểm ngắt (breakpoint) có thể được đặt trong hàm Lambda để gỡ lỗi.
Giao diện công cụ kiểm thử Lambda

Giả lập API Gateway

Trong dự án .NET Lambda hiện có của tôi, tất cả các hàm Lambda đều được thiết kế để được gọi từ một endpoint của Amazon API Gateway. Điều này có nghĩa là tôi muốn máy tính web của mình được gọi như một REST API thông qua các HTTP client như trình duyệt web. Với tích hợp Lambda của chúng tôi, chúng tôi đã thêm khả năng điều phối các hàm Lambda của bạn trong app host như thể chúng đang chạy trong API Gateway.

Để kích hoạt trình giả lập API Gateway, hãy sử dụng phương thức AddAWSAPIGatewayEmulator, và bao gồm các hàm Lambda với các route bằng cách sử dụng phương thức WithReference. Lưu ý rằng nếu route của bạn sử dụng một đường dẫn ký tự đại diện (wildcard path), hãy sử dụng biến đường dẫn {proxy+}, tương tự như cách bạn định nghĩa các route ký tự đại diện trong mẫu CloudFormation.

#pragma warning disable CA2252

var builder = DistributedApplication.CreateBuilder(args);

var addFunction = builder.AddAWSLambdaFunction<Projects.LambdaWebCalculator>("AddFunction",
        lambdaHandler: "LambdaWebCalculator::LambdaWebCalculator.Functions::AddFunctionHandler");
var minusFunction = builder.AddAWSLambdaFunction<Projects.LambdaWebCalculator>("MinusFunction",
        lambdaHandler: "LambdaWebCalculator::LambdaWebCalculator.Functions::MinusFunctionHandler");
var multiplyFunction = builder.AddAWSLambdaFunction<Projects.LambdaWebCalculator>("MultipyFunction",
        lambdaHandler: "LambdaWebCalculator::LambdaWebCalculator.Functions::MultiplyFunctionHandler");
var divideFunction = builder.AddAWSLambdaFunction<Projects.LambdaWebCalculator>("DivideFunction",
        lambdaHandler: "LambdaWebCalculator::LambdaWebCalculator.Functions::DivideFunctionHandler");

builder.AddAWSAPIGatewayEmulator("APIGatewayEmulator", Aspire.Hosting.AWS.Lambda.APIGatewayType.HttpV2)
        .WithReference(addFunction, Aspire.Hosting.AWS.Lambda.Method.Get, "/add/{x}/{y}")
        .WithReference(minusFunction, Aspire.Hosting.AWS.Lambda.Method.Get, "/minus/{x}/{y}")
        .WithReference(multiplyFunction, Aspire.Hosting.AWS.Lambda.Method.Get, "/multiply/{x}/{y}")
        .WithReference(divideFunction, Aspire.Hosting.AWS.Lambda.Method.Get, "/divide/{x}/{y}");

builder.Build().Run();

Bây giờ khi app host được khởi chạy, tài nguyên giả lập API Gateway được bao gồm trong danh sách tài nguyên. Endpoint được hiển thị cho trình giả lập API Gateway có thể được sử dụng để thực hiện các yêu cầu REST gọi các hàm Lambda.

Bảng điều khiển Aspire với trình giả lập API Gateway

Trong ví dụ của tôi, tôi đã đăng ký hàm Lambda addFunction với động từ HTTP GET và đường dẫn tài nguyên /add/{x}/{y}. Vì vậy, nếu tôi thực hiện một yêu cầu đến http://localhost:<apigateway-emulator-port>/add/1/2, hàm Lambda cộng của tôi sẽ được gọi trong trình gỡ lỗi, nơi các điểm ngắt sẽ được tôn trọng và tôi sẽ nhận lại phản hồi.

Kết quả API từ trình duyệt

Truy cập Log

Các log được tạo ra từ hàm Lambda có thể được xem trong bảng điều khiển .NET Aspire bằng cách nhấp vào tab Console và chọn hàm Lambda trong danh sách thả xuống Resources.

Xem log trong bảng điều khiển Aspire

Theo mặc định, hàm Lambda sẽ ghi log ở cấp độ INFO ở định dạng văn bản, khớp với hành vi mặc định của các hàm được triển khai lên Lambda. Bằng cách sử dụng lớp LambdaFunctionOptions với phương thức AddAWSLambdaFunction, định dạng và cấp độ log có thể được điều chỉnh. Đoạn mã sau đặt cấp độ log thành debug và định dạng thành JSON.

var addFunction = builder.AddAWSLambdaFunction<Projects.LambdaWebCalculator>("AddFunction",
        lambdaHandler: "LambdaWebCalculator::LambdaWebCalculator.Functions::AddFunctionHandler",
        options: new LambdaFunctionOptions
        {
            ApplicationLogLevel = ApplicationLogLevel.DEBUG,
            LogFormat = LogFormat.JSON
        });

Chế độ xem Console Logs bây giờ sẽ hiển thị các câu lệnh debug từ hàm Lambda và định dạng các thông báo dưới dạng tài liệu JSON.

Log có cấu trúc ở định dạng JSON

Kết nối đến AWS

Như đã đề cập trong bài đăng blog trước đó về .NET Aspire cho AWS, cấu hình mà AWS SDK for .NET nên sử dụng để kết nối với AWS có thể được tạo bằng phương thức AddAWSSDKConfig. Cấu hình có thể được gán cho các dự án bằng cách gọi phương thức WithReference trên dự án. Một dự án .NET Lambda cũng là một dự án, và mẫu tương tự có thể được sử dụng cho các dự án .NET Lambda.

var builder = DistributedApplication.CreateBuilder(args);

var awsConfig = builder.AddAWSSDKConfig()
                        .WithRegion(RegionEndpoint.USWest2);

builder.AddAWSLambdaFunction<Projects.ReadDynamoDBExecutable>("ReadDynamoDBFunction", lambdaHandler: "ReadDynamoDBExecutable")
        .WithReference(awsConfig);

Kết luận

Với việc tích hợp Lambda với .NET Aspire, chúng tôi hy vọng sẽ cung cấp một trải nghiệm xây dựng các hàm .NET Lambda quen thuộc và dễ dàng để bắt đầu. Một khi app host được thiết lập, tất cả những gì các thành viên trong nhóm của một dự án cần làm là sao chép (clone) hoặc lấy (pull) những thay đổi mới nhất từ kho lưu trữ và bắt đầu gỡ lỗi ngay lập tức. Tích hợp Lambda có thể được sử dụng kết hợp với các tích hợp trước đây của chúng tôi để cung cấp tài nguyên với AWS CloudFormation và có các hàm Lambda sử dụng Amazon DynamoDB local cho backend.

Trong bài đăng blog này, là Phần 1, bạn đã học cách sử dụng .NET Aspire để chạy và gỡ lỗi các hàm .NET Lambda tại local. Trong bài đăng blog tiếp theo, là Phần 2, bạn sẽ học cách kết hợp mô hình lập trình của .NET Aspire để kết nối các thành phần bổ sung và chia sẻ các phương pháp hay nhất trên các hàm Lambda.

Việc phát triển tính năng này đang diễn ra trong kho lưu trữ aws/integrations-on-dotnet-aspire-for-aws của chúng tôi. Vấn đề GitHub sau đây đang được sử dụng làm vấn đề theo dõi chính cho việc tích hợp Lambda: https://github.com/aws/integrations-on-dotnet-aspire-for-aws/issues/17. Chúng tôi mong muốn các nhà phát triển .NET đang xây dựng các hàm Lambda hãy thử bản xem trước này và cho chúng tôi biết trên kho lưu trữ của chúng tôi về những câu chuyện thành công của bạn và bất kỳ vấn đề nào với việc tích hợp Lambda của chúng tôi.

Về tác giả

Norm Johanson

Norm Johanson

Norm Johanson đã là một nhà phát triển phần mềm hơn 25 năm, phát triển tất cả các loại ứng dụng. Từ năm 2010, ông đã làm việc cho AWS, tập trung vào trải nghiệm của nhà phát triển .NET tại AWS. Bạn có thể tìm thấy ông trên Twitter @socketnorm và GitHub @normj.