YARP Direct Forwarding (Chuyển tiếp trực tiếp)
Nguồn: YARP Direct Forwarding
Một số ứng dụng chỉ cần khả năng nhận một request cụ thể và chuyển tiếp nó đến một destination cụ thể. Các ứng dụng này không cần, hoặc đã giải quyết bằng cách khác, các tính năng khác của proxy như configuration discovery (khám phá cấu hình), routing (định tuyến), load balancing (cân bằng tải), v.v.
IHttpForwarder
IHttpForwarder đóng vai trò là bộ điều hợp proxy cốt lõi giữa các request ASP.NET Core đến và các request System.Net.Http đi. Nó xử lý cơ chế tạo một HttpRequestMessage từ HttpContext, gửi nó, và chuyển tiếp lại response.
IHttpForwarder hỗ trợ:
- Chọn destination động, bạn chỉ định destination cho mỗi request
- Tùy chỉnh HTTP client, bạn cung cấp
HttpMessageInvoker - Tùy chỉnh request và response (ngoại trừ body)
- Các giao thức streaming như gRPC và WebSocket
- Xử lý lỗi
Nó không bao gồm:
- Routing (Định tuyến)
- Load balancing (Cân bằng tải)
- Affinity (Ái lực)
- Retry (Thử lại)
Ví dụ
Xem ReverseProxy.Direct.Sample làm mẫu dựng sẵn, hoặc sử dụng các bước dưới đây.
Thêm NuGet package YARP Reverse Proxy
Thêm Yarp.ReverseProxy NuGet package vào project.
Cập nhật Program.cs
Trong ví dụ này, IHttpForwarder được đăng ký trong DI (Dependency Injection - tiêm phụ thuộc), được inject vào phương thức endpoint, và được sử dụng để chuyển tiếp request từ một route cụ thể đến https://localhost:10000/prefix/.
Các transform tùy chọn cho thấy cách sao chép tất cả request headers ngoại trừ Host, thường thì destination yêu cầu Host của chính nó từ URL.
using System;
using System.Diagnostics;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Threading;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Yarp.ReverseProxy.Forwarder;
using Yarp.ReverseProxy.Transforms;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpForwarder();
var app = builder.Build();
// Configure our own HttpMessageInvoker for outbound calls for proxy operations
var httpClient = new HttpMessageInvoker(new SocketsHttpHandler
{
UseProxy = false,
AllowAutoRedirect = false,
AutomaticDecompression = DecompressionMethods.None,
UseCookies = false,
EnableMultipleHttp2Connections = true,
ActivityHeadersPropagator = new ReverseProxyPropagator(DistributedContextPropagator.Current),
ConnectTimeout = TimeSpan.FromSeconds(15),
});
// Setup our own request transform class
var transformer = new CustomTransformer(); // or HttpTransformer.Default;
var requestConfig = new ForwarderRequestConfig { ActivityTimeout = TimeSpan.FromSeconds(100) };
app.UseRouting();
// When using IHttpForwarder for direct forwarding you are responsible for routing, destination discovery, load balancing, affinity, etc..
// For an alternate example that includes those features see BasicYarpSample.
app.Map("/test/{**catch-all}", async (HttpContext httpContext, IHttpForwarder forwarder) =>
{
var error = await forwarder.SendAsync(httpContext, "https://localhost:10000/",
httpClient, requestConfig, transformer);
// Check if the operation was successful
if (error != ForwarderError.None)
{
var errorFeature = httpContext.GetForwarderErrorFeature();
var exception = errorFeature.Exception;
}
});
app.Run();
/// <summary>
/// Custom request transformation
/// </summary>
internal class CustomTransformer : HttpTransformer
{
/// <summary>
/// A callback that is invoked prior to sending the proxied request. All HttpRequestMessage
/// fields are initialized except RequestUri, which will be initialized after the
/// callback if no value is provided. The string parameter represents the destination
/// URI prefix that should be used when constructing the RequestUri. The headers
/// are copied by the base implementation, excluding some protocol headers like HTTP/2
/// pseudo headers (":authority").
/// </summary>
/// <param name="httpContext">The incoming request.</param>
/// <param name="proxyRequest">The outgoing proxy request.</param>
/// <param name="destinationPrefix">The uri prefix for the selected destination server which can be used to create
/// the RequestUri.</param>
public override async ValueTask TransformRequestAsync(HttpContext httpContext, HttpRequestMessage proxyRequest, string destinationPrefix, CancellationToken cancellationToken)
{
// Copy all request headers
await base.TransformRequestAsync(httpContext, proxyRequest, destinationPrefix, cancellationToken);
// Customize the query string:
var queryContext = new QueryTransformContext(httpContext.Request);
queryContext.Collection.Remove("param1");
queryContext.Collection["area"] = "xx2";
// Assign the custom uri. Be careful about extra slashes when concatenating here. RequestUtilities.MakeDestinationAddress is a safe default.
proxyRequest.RequestUri = RequestUtilities.MakeDestinationAddress("https://example.com", httpContext.Request.Path, queryContext.QueryString);
// Suppress the original request header, use the one from the destination Uri.
proxyRequest.Headers.Host = null;
}
}Cũng có các extension methods (phương thức mở rộng) giúp đơn giản hóa việc ánh xạ IHttpForwarder đến các endpoint.
app.MapForwarder("/{**catch-all}", "https://localhost:10000/", requestConfig, transformer, httpClient);HTTP Client
HTTP client có thể được tùy chỉnh, nhưng ví dụ trên được khuyến nghị cho các kịch bản proxy thông thường.
Luôn sử dụng HttpMessageInvoker thay vì HttpClient, HttpClient buffer (đệm) response theo mặc định. Buffering phá vỡ các kịch bản streaming và tăng mức sử dụng bộ nhớ và độ trễ.
Việc tái sử dụng một client cho các request đến cùng một destination được khuyến nghị vì lý do hiệu suất, vì nó cho phép tái sử dụng các pooled connection. Một client cũng có thể được tái sử dụng cho các request đến các destination khác nhau nếu cấu hình giống nhau.
Transforms (Biến đổi)
Request và response có thể được sửa đổi bằng cách cung cấp một HttpTransformer kế thừa làm tham số cho phương thức SendAsync.
Xử lý lỗi
IHttpForwarder bắt các ngoại lệ và timeout từ HTTP client, ghi log chúng, và chuyển đổi chúng thành mã trạng thái 5xx hoặc hủy bỏ response. Một mã lỗi được trả về từ SendAsync, và chi tiết lỗi có thể được truy cập từ IForwarderErrorFeature như được thể hiện ở trên.