Add extension methods for adding direct forwarding to the ASP.NET Core request pipeline (#1972) (#1979)

This commit is contained in:
Tomasz Pęczek 2023-01-04 20:40:14 +01:00 коммит произвёл GitHub
Родитель f26d0f1d32
Коммит b9b2bb4dbe
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 110 добавлений и 23 удалений

Просмотреть файл

@ -98,6 +98,23 @@ private class CustomTransformer : HttpTransformer
}
```
There are also [extension methods](xref:Microsoft.AspNetCore.Builder.DirectForwardingIEndpointRouteBuilderExtensions) available that simplify the mapping of IHttpForwarder to endpoints.
```C#
...
public void Configure(IApplicationBuilder app, IHttpForwarder forwarder)
{
...
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapForwarder("/{**catch-all}", "https://localhost:10000/", requestConfig, transformer, httpClient);
});
}
```
### The HTTP Client
The http client may be customized, but the above example is recommended for common proxy scenarios.

Просмотреть файл

@ -48,6 +48,9 @@ namespace Yarp.Sample
var requestOptions = 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.UseEndpoints(endpoints =>
{
endpoints.Map("/test/{**catch-all}", async httpContext =>
@ -78,18 +81,8 @@ namespace Yarp.Sample
});
// 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.
endpoints.Map("/{**catch-all}", async httpContext =>
{
var error = await forwarder.SendAsync(httpContext, "https://example.com", httpClient, requestOptions, transformer);
// Check if the proxy operation was successful
if (error != ForwarderError.None)
{
var errorFeature = httpContext.Features.Get<IForwarderErrorFeature>();
var exception = errorFeature.Exception;
}
});
// When using extension methods for registering IHttpForwarder providing configuration, transforms, and HttpMessageInvoker is optional (defaults will be used).
endpoints.MapForwarder("/{**catch-all}", "https://example.com", requestOptions, transformer, httpClient);
});
}

Просмотреть файл

@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Net.Http;
using Yarp.ReverseProxy.Configuration;
namespace Yarp.ReverseProxy.Forwarder;
internal sealed class DirectForwardingHttpClientProvider
{
public HttpMessageInvoker HttpClient { get; }
public DirectForwardingHttpClientProvider() : this(new ForwarderHttpClientFactory()) { }
public DirectForwardingHttpClientProvider(IForwarderHttpClientFactory factory)
{
HttpClient = factory.CreateClient(new ForwarderHttpClientContext
{
NewConfig = HttpClientConfig.Empty
});
}
}

Просмотреть файл

@ -31,6 +31,9 @@ public static class ReverseProxyServiceCollectionExtensions
services.TryAddSingleton<IClock, Clock>();
services.TryAddSingleton<IHttpForwarder, HttpForwarder>();
services.TryAddSingleton<ITransformBuilder, TransformBuilder>();
services.AddSingleton<DirectForwardingHttpClientProvider>();
return services;
}

Просмотреть файл

@ -0,0 +1,61 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Net.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Yarp.ReverseProxy.Forwarder;
namespace Microsoft.AspNetCore.Builder;
/// <summary>
/// Extension methods for <see cref="IEndpointRouteBuilder"/> used to add direct forwarding to the ASP.NET Core request pipeline.
/// </summary>
public static class DirectForwardingIEndpointRouteBuilderExtensions
{
/// <summary>
/// Adds direct forwarding of HTTP requests that match the specified pattern to a specific destination using default configuration for the outgoing request, default transforms, and default HTTP client.
/// </summary>
public static IEndpointConventionBuilder MapForwarder(this IEndpointRouteBuilder endpoints, string pattern, string destinationPrefix)
{
return endpoints.MapForwarder(pattern, destinationPrefix, ForwarderRequestConfig.Empty);
}
/// <summary>
/// Adds direct forwarding of HTTP requests that match the specified pattern to a specific destination using customized configuration for the outgoing request, default transforms, and default HTTP client.
/// </summary>
public static IEndpointConventionBuilder MapForwarder(this IEndpointRouteBuilder endpoints, string pattern, string destinationPrefix, ForwarderRequestConfig requestConfig)
{
return endpoints.MapForwarder(pattern, destinationPrefix, requestConfig, HttpTransformer.Default);
}
/// <summary>
/// Adds direct forwarding of HTTP requests that match the specified pattern to a specific destination using customized configuration for the outgoing request, customized transforms, and default HTTP client.
/// </summary>
public static IEndpointConventionBuilder MapForwarder(this IEndpointRouteBuilder endpoints, string pattern, string destinationPrefix, ForwarderRequestConfig requestConfig, HttpTransformer transformer)
{
var httpClientProvider = endpoints.ServiceProvider.GetRequiredService<DirectForwardingHttpClientProvider>();
return endpoints.MapForwarder(pattern, destinationPrefix, requestConfig, transformer, httpClientProvider.HttpClient);
}
/// <summary>
/// Adds direct forwarding of HTTP requests that match the specified pattern to a specific destination using customized configuration for the outgoing request, customized transforms, and customized HTTP client.
/// </summary>
public static IEndpointConventionBuilder MapForwarder(this IEndpointRouteBuilder endpoints, string pattern, string destinationPrefix, ForwarderRequestConfig requestConfig, HttpTransformer transformer, HttpMessageInvoker httpClient)
{
ArgumentNullException.ThrowIfNull(endpoints);
ArgumentNullException.ThrowIfNull(destinationPrefix);
ArgumentNullException.ThrowIfNull(httpClient);
ArgumentNullException.ThrowIfNull(requestConfig);
ArgumentNullException.ThrowIfNull(transformer);
var forwarder = endpoints.ServiceProvider.GetRequiredService<IHttpForwarder>();
return endpoints.Map(pattern, async httpContext =>
{
await forwarder.SendAsync(httpContext, destinationPrefix, httpClient, requestConfig, transformer);
});
}
}

Просмотреть файл

@ -30,7 +30,7 @@ public class Startup
/// <summary>
/// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
/// </summary>
public void Configure(IApplicationBuilder app, IHttpForwarder httpProxy)
public void Configure(IApplicationBuilder app)
{
var httpClient = new HttpMessageInvoker(new SocketsHttpHandler()
{
@ -56,16 +56,7 @@ public class Startup
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.Map("/{**catch-all}", async httpContext =>
{
await httpProxy.SendAsync(httpContext, "https://example.com", httpClient, requestConfig, transformer);
var errorFeature = httpContext.GetForwarderErrorFeature();
if (errorFeature is not null)
{
var error = errorFeature.Error;
var exception = errorFeature.Exception;
}
});
endpoints.MapForwarder("/{**catch-all}", "https://example.com", requestConfig, transformer, httpClient);
});
}