ASP.NET Core: Khắc phục sự cố gRPC trên .NET


Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên

Trong bài viết này

  1. Không khớp giữa cấu hình SSL/TLS của máy khách và dịch vụ
  2. Gọi dịch vụ gRPC bằng chứng chỉ không đáng tin cậy/không hợp lệ
  3. Gọi các dịch vụ gRPC không an toàn bằng máy khách .NET Core
  4. Không thể khởi động ứng dụng gRPC ASP.NET Core trên macOS
  5. Nội dung gRPC C# không phải là mã được tạo từ các file .proto
  6. Các dự án WPF không thể tạo nội dung gRPC C# từ các file .proto
  7. Gọi các dịch vụ gRPC được lưu trữ trong thư mục con
  8. Định cấu hình máy khách gRPC để sử dụng HTTP/3
  9. Xây dựng gRPC trên Alpine Linux

Ghi chú

Đây không phải là phiên bản mới nhất của bài viết này. Để biết bản phát hành hiện tại, hãy xem phiên bản .NET 8 của bài viết này.

Tài liệu này thảo luận về các vấn đề thường gặp khi phát triển ứng dụng gRPC trên .NET.

Không khớp giữa cấu hình SSL/TLS của máy khách và dịch vụ

Các sample và mẫu gRPC sử dụng Bảo mật lớp truyền tải (TLS) để bảo mật các dịch vụ gRPC theo mặc định. Máy khách gRPC cần sử dụng kết nối an toàn để gọi thành công các dịch vụ gRPC được bảo mật.

Bạn có thể xác minh dịch vụ gRPC ASP.NET Core đang sử dụng TLS trong nhật ký được ghi khi khởi động ứng dụng. Dịch vụ sẽ lắng nghe trên điểm cuối HTTPS:

info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development

Máy khách .NET Core phải sử dụng httpsđịa chỉ máy chủ để thực hiện cuộc gọi với kết nối an toàn:

static async Task Main(string[] args)
{
    // The port number(5001) must match the port of the gRPC server.
    var channel = GrpcChannel.ForAddress("https://localhost:5001");
    var client = new Greet.GreeterClient(channel);
}

Tất cả việc triển khai ứng dụng khách gRPC đều hỗ trợ TLS. Máy khách gRPC từ các ngôn ngữ khác thường yêu cầu kênh được định cấu hình bằng SslCredentialsSslCredentials chỉ định chứng chỉ mà máy khách sẽ sử dụng và nó phải được sử dụng thay vì thông tin xác thực không an toàn. Để biết ví dụ về cách định cấu hình triển khai máy khách gRPC khác nhau để sử dụng TLS, hãy xem Xác thực gRPC.

Gọi dịch vụ gRPC bằng chứng chỉ không đáng tin cậy/không hợp lệ

Máy khách .NET gRPC yêu cầu dịch vụ phải có chứng chỉ tin cậy. Thông báo lỗi sau được trả về khi gọi dịch vụ gRPC mà không có chứng chỉ tin cậy:

Unhandled exception. System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception. ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.

Bạn có thể gặp lỗi này nếu bạn đang kiểm tra ứng dụng của mình cục bộ và chứng chỉ phát triển ASP.NET Core HTTPS không đáng tin cậy. Để biết hướng dẫn khắc phục sự cố này, hãy xem Tin cậy chứng chỉ phát triển ASP.NET Core HTTPS trên Windows và macOS.

Nếu bạn đang gọi dịch vụ gRPC trên một máy khác và không thể tin cậy vào chứng chỉ thì máy khách gRPC có thể được định cấu hình để bỏ qua chứng chỉ không hợp lệ. Đoạn mã sau sử dụng HttpClientHandler.ServerCertificateCustomValidationCallback để cho phép các lời gọi không có chứng chỉ tin cậy:

var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpHandler = handler });
var client = new Greet.GreeterClient(channel);

Factory máy khách gRPC cho phép các lời gọi mà không cần chứng chỉ đáng tin cậy. Sử dụng phương thức tiện ích mở rộng configurePrimaryHttpMessageHandler để định cấu hình trình xử lý trên máy khách:

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        var handler = new HttpClientHandler();
        handler.ServerCertificateCustomValidationCallback = 
            HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

        return handler;
    });

Cảnh báo

Chỉ nên sử dụng chứng chỉ không đáng tin cậy trong quá trình phát triển ứng dụng. Ứng dụng production phải luôn sử dụng chứng chỉ hợp lệ.

Gọi các dịch vụ gRPC không an toàn bằng máy khách .NET Core

Máy khách .NET gRPC có thể gọi các dịch vụ gRPC không an toàn bằng cách chỉ định http trong địa chỉ máy chủ. Ví dụ, GrpcChannel.ForAddress("http://localhost:5000").

Có một số yêu cầu bổ sung để gọi các dịch vụ gRPC không an toàn tùy thuộc vào phiên bản .NET mà ứng dụng đang sử dụng:

  • .NET 5 trở lên yêu cầu Grpc.Net.Client phiên bản 2.32.0 trở lên.

  • .NET Core 3.x yêu cầu cấu hình bổ sung. Ứng dụng phải đặt System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport chuyển thành true:

    // This switch must be set before creating the GrpcChannel/HttpClient.
    AppContext.SetSwitch(
        "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
    
    // The port number(5000) must match the port of the gRPC server.
    var channel = GrpcChannel.ForAddress("http://localhost:5000");
    var client = new Greet.GreeterClient(channel);
    

Việc System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport chuyển đổi chỉ được yêu cầu đối với .NET Core 3.x. Nó không làm gì trong .NET 5 và không bắt buộc.

Quan trọng

Các dịch vụ gRPC không an toàn phải được lưu trữ trên cổng chỉ HTTP/2. Để biết thêm thông tin, hãy xem Đàm phán giao thức ASP.NET Core.

Không thể khởi động ứng dụng gRPC ASP.NET Core trên macOS

Kestrel không hỗ trợ HTTP/2 với TLS trên macOS trước .NET 8. Các sample và mẫu ASP.NET Core gRPC sử dụng TLS theo mặc định. Bạn sẽ thấy thông báo lỗi sau khi cố gắng khởi động máy chủ gRPC:

Không thể liên kết với https://localhost:5001 trên giao diện loopback IPv4: 'HTTP/2 qua TLS không được hỗ trợ trên macOS do thiếu hỗ trợ ALPN.'.

Để khắc phục sự cố này trong .NET 7 trở về trước, hãy đặt cấu hình Kestrel và máy khách gRPC để sử dụng HTTP/2 mà không cần TLS. Bạn chỉ nên làm điều này trong quá trình phát triển. Không sử dụng TLS sẽ dẫn đến việc gửi tin nhắn gRPC mà không được mã hóa.

Kestrel phải định cấu hình điểm cuối HTTP/2 không có TLS trong Program.cs:

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(options =>
{
    // Setup a HTTP/2 endpoint without TLS.
    options.ListenLocalhost(<5287>, o => o.Protocols =
        HttpProtocols.Http2);
});
  • Trong đoạn code trên, thay thế số cổng localhost 5287 bằng số cổng HTTP (không phải HTTPS) được chỉ định trong Properties/launchSettings.json với dự án dịch vụ gRPC.

Khi điểm cuối HTTP/2 được định cấu hình không có TLS, thì ListenOptions.Protocols của điểm cuối phải được đặt thành HttpProtocols.Http2HttpProtocols.Http1AndHttp2 sẽ không thể sử dụng được vì TLS được yêu cầu để đàm phán HTTP/2. Nếu không có TLS, tất cả các kết nối đến điểm cuối mặc định là HTTP/1.1 và lệnh gọi gRPC đều không thành công.

Máy khách gRPC cũng phải được cấu hình để không sử dụng TLS. Để biết thêm thông tin, hãy xem phần Gọi các dịch vụ gRPC không an toàn bằng ứng dụng khách .NET Core.

Cảnh báo

HTTP/2 không có TLS chỉ nên được sử dụng trong quá trình phát triển ứng dụng. Các ứng dụng production phải luôn sử dụng bảo mật truyền tải. Để biết thêm thông tin, hãy xem Những cân nhắc về bảo mật trong gRPC dành cho ASP.NET Core.

Nội dung gRPC C# không phải là mã được tạo từ các file .proto

Việc tạo mã gRPC của các máy khách cụ thể và các lớp cơ sở dịch vụ yêu cầu các tệp và công cụ protobuf phải được tham chiếu từ một dự án. Bạn phải bao gồm:

  • Các file.proto bạn muốn sử dụng trong nhóm item <Protobuf>Các file .proto đã import phải được tham chiếu bởi dự án.
  • Tham chiếu gói đến gói công cụ gRPC Grpc.Tools.

Để biết thêm thông tin về cách tạo nội dung gRPC C#, hãy xem các dịch vụ gRPC với C#.

Một ứng dụng web ASP.NET Core lưu trữ các dịch vụ gRPC chỉ cần tạo lớp cơ sở dịch vụ:

<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
</ItemGroup>

Máy khách gRPC thực hiện lệnh gọi gRPC chỉ cần tạo ứng dụng khách cụ thể:

<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>

Các dự án WPF không thể tạo nội dung gRPC C# từ các file .proto

Các dự án WPF có một sự cố đã biết khiến việc tạo mã gRPC không hoạt động bình thường. Bất kỳ loại gRPC nào được tạo trong dự án WPF bằng cách tham chiếu các file Grpc.Tools và .proto sẽ tạo ra lỗi biên dịch khi sử dụng:

error CS0246: The type or namespace name 'MyGrpcServices' could not be found (are you missing a using directive or an assembly reference?)

Bạn có thể giải quyết vấn đề này bằng cách:

  1. Tạo một dự án thư viện lớp .NET Core mới.
  2. Trong dự án mới, hãy thêm các tham chiếu để cho phép tạo mã C# từ các file .proto:
  3. Trong ứng dụng WPF, thêm tham chiếu đến dự án mới.

Ứng dụng WPF có thể sử dụng các loại gRPC được tạo từ dự án thư viện lớp mới.

Gọi các dịch vụ gRPC được lưu trữ trong thư mục con

Cảnh báo

Nhiều công cụ gRPC của bên thứ ba không hỗ trợ các dịch vụ được lưu trữ trong thư mục con. Hãy cân nhắc việc tìm cách lưu trữ gRPC làm thư mục gốc.

Thành phần đường dẫn của địa chỉ kênh gRPC bị bỏ qua khi thực hiện lệnh gọi gRPC. Ví dụ: GrpcChannel.ForAddress("https://localhost:5001/ignored_path") sẽ không sử dụng ignored_path khi định tuyến các lời gọi gRPC cho dịch vụ.

Đường dẫn địa chỉ bị bỏ qua vì gRPC có cấu trúc địa chỉ quy định, được chuẩn hóa. Địa chỉ gRPC kết hợp tên gói, dịch vụ và phương thức: https://localhost:5001/PackageName.ServiceName/MethodName.

Có một số trường hợp khi ứng dụng cần bao gồm đường dẫn có lệnh gọi gRPC. Ví dụ: khi ứng dụng ASP.NET Core gRPC được lưu trữ trong thư mục IIS và thư mục đó cần được đưa vào yêu cầu. Khi cần có một đường dẫn, nó có thể được thêm vào lệnh gọi gRPC bằng cách sử dụng tùy chỉnh SubdirectoryHandler được chỉ định như bên dưới:

/// <summary>
/// A delegating handler that adds a subdirectory to the URI of gRPC requests.
/// </summary>
public class SubdirectoryHandler : DelegatingHandler
{
    private readonly string _subdirectory;

    public SubdirectoryHandler(HttpMessageHandler innerHandler, string subdirectory)
        : base(innerHandler)
    {
        _subdirectory = subdirectory;
    }

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var old = request.RequestUri;

        var url = $"{old.Scheme}://{old.Host}:{old.Port}";
        url += $"{_subdirectory}{request.RequestUri.AbsolutePath}";
        request.RequestUri = new Uri(url, UriKind.Absolute);

        return base.SendAsync(request, cancellationToken);
    }
}

SubdirectoryHandler được sử dụng khi kênh gRPC được tạo.

var handler = new SubdirectoryHandler(new HttpClientHandler(), "/MyApp");

var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = handler });
var client = new Greet.GreeterClient(channel);

var reply = await client.SayHelloAsync(new HelloRequest { Name = ".NET" });

Đoạn code trên:

  • Tạo một SubdirectoryHandler với đường dẫn /MyApp.
  • Định cấu hình kênh để sử dụng SubdirectoryHandler.
  • Gọi dịch vụ gRPC bằng SayHelloAsync. Lời gọi gRPC được gửi tới https://localhost:5001/MyApp/greet.Greeter/SayHello.

Ngoài ra, bạn có thể định cấu hình factory máy khách SubdirectoryHandler bằng cách sử dụng AddHttpMessageHandler.

Định cấu hình máy khách gRPC để sử dụng HTTP/3

Máy khách .NET gRPC hỗ trợ HTTP/3 với .NET 6 trở lên. Nếu máy chủ gửi tiêu đề phản hồi alt-svc tới máy khách cho biết máy chủ hỗ trợ HTTP/3 thì máy khách sẽ tự động nâng cấp kết nối của nó lên HTTP/3. Để biết thông tin về cách bật HTTP/3 trên máy chủ, hãy xem Sử dụng HTTP/3 với máy chủ web ASP.NET Core Kestrel.

Hỗ trợ HTTP/3 trong .NET 8 được bật theo mặc định. Hỗ trợ HTTP/3 trong .NET 6 và .NET 7 cần được bật thông qua cờ cấu hình trong tệp dự án:

<ItemGroup>
  <RuntimeHostConfigurationOption Include="System.Net.SocketsHttpHandler.Http3Support" Value="true" />
</ItemGroup>

System.Net.SocketsHttpHandler.Http3Support cũng có thể được đặt bằng AppContext.SetSwitch.

DelegatingHandler có thể được sử dụng để buộc máy khách gRPC sử dụng HTTP/3Việc buộc HTTP/3 sẽ tránh được chi phí nâng cấp yêu cầu. Buộc HTTP/3 với mã tương tự như sau:

/// <summary>
/// A delegating handler that changes the request HTTP version to HTTP/3.
/// </summary>
public class Http3Handler : DelegatingHandler
{
    public Http3Handler() { }
    public Http3Handler(HttpMessageHandler innerHandler) : base(innerHandler) { }

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        request.Version = HttpVersion.Version30;
        request.VersionPolicy = HttpVersionPolicy.RequestVersionExact;

        return base.SendAsync(request, cancellationToken);
    }
}

Http3Handler được sử dụng khi kênh gRPC được tạo. Đoạn mã sau tạo một kênh được định cấu hình để sử dụng Http3Handler.

var handler = new Http3Handler(new HttpClientHandler());

var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = handler });
var client = new Greet.GreeterClient(channel);

var reply = await client.SayHelloAsync(new HelloRequest { Name = ".NET" });

Ngoài ra, bạn có thể định cấu hình factory máy khách Http3Handler bằng cách sử dụng AddHttpMessageHandler.

Xây dựng gRPC trên Alpine Linux

Gói Grpc.Tools tạo ra các loại .NET từ các file .proto bằng cách sử dụng tệp nhị phân gốc đi kèm có tên là protoc. Cần có các bước bổ sung để xây dựng ứng dụng gRPC trên nền tảng không được các tệp nhị phân gốc trong Grpc.Tools, chẳng hạn như Alpine Linux hỗ trợ.

Tạo mã trước thời hạn

Một giải pháp là tạo mã trước thời hạn.

  1. Di chuyển các file .proto và tham chiếu gói Grpc.Tools tới một dự án mới.
  2. Xuất bản dự án dưới dạng gói NuGet và tải nó lên nguồn cấp dữ liệu NuGet.
  3. Cập nhật ứng dụng để tham khảo gói NuGet.

Với các bước ở trên, ứng dụng không còn cần phải yêu cầu Grpc.Tools để build nữa vì mã được tạo trước thời hạn.

Tùy chỉnh các file nhị phân gốc Grpc.Tools

Grpc.Tools hỗ trợ sử dụng các file nhị phân gốc tùy chỉnh. Tính năng này cho phép công cụ gRPC chạy trong môi trường mà các file nhị phân gốc đi kèm của nó không hỗ trợ.

Xây dựng hoặc thu thập các file nhị phân gốc protoc và grpc_csharp_plugin và định cấu hình Grpc.Tools để sử dụng chúng. Định cấu hình các file nhị phân gốc bằng cách đặt các biến môi trường sau:

  • PROTOBUF_PROTOC - Đường dẫn đầy đủ đến trình biên dịch bộ đệm giao thức
  • GRPC_PROTOC_PLUGIN - Đường dẫn đầy đủ tới grpc_csharp_plugin

Đối với Alpine Linux, có các gói do cộng đồng cung cấp cho trình biên dịch bộ đệm giao thức và các plugin gRPC tại https://pkgs.alpinelinux.org/.

# Build or install the binaries for your architecture.

# e.g. for Alpine Linux the grpc-plugins package can be used
#  See https://pkgs.alpinelinux.org/package/edge/community/x86_64/grpc-plugins
apk add grpc-plugins  # Alpine Linux specific package installer

# Set environment variables for the built/installed protoc
# and grpc_csharp_plugin binaries
export PROTOBUF_PROTOC=/usr/bin/protoc
export GRPC_PROTOC_PLUGIN=/usr/bin/grpc_csharp_plugin

# When dotnet build runs, the Grpc.Tools NuGet package
# uses the binaries pointed to by the environment variables.
dotnet build

Để biết thêm thông tin về cách sử dụng Grpc.Tools với kiến ​​trúc không được hỗ trợ, hãy xem tài liệu tích hợp bản dựng gRPC.

Nguồn: learn.microsoft.com
» Tiếp: Bắt đầu với EF Core trong ứng dụng web ASP.NET MVC
« Trước: Các sample
Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên
Copied !!!