-
Notifications
You must be signed in to change notification settings - Fork 832
Description
Background and motivation
HTTP requests to downstream services often require consistent classification and metadata handling for telemetry, routing, and policy application. The HttpDependencyMetadataResolver
provides a highly optimized solution using trie-based lookups to efficiently map HTTP requests to their corresponding metadata.
By exposing this as a public abstract class, we allow applications to:
- Leverage the core functionality without re-implementing complex matching logic
- Extend or customize the implementation for specific environments
- Integrate with existing service discovery or configuration systems
Making this an abstract class rather than an interface or sealed class provides the optimal balance between extensibility and encapsulation. Organizations with performance-critical systems and high-volume API traffic can utilize our efficient request classification without rebuilding the optimized trie-based lookup. Teams leveraging service mesh technologies can integrate with their service registry systems through extension points. The abstract approach also benefits multi-cloud deployments that need adaptive dependency resolution based on environment, while providing simplified mocking capabilities for integration testing without exposing sensitive implementation details. This design preserves performance while enabling the flexibility needed for diverse enterprise scenarios.
API Proposal
namespace Microsoft.Extensions.Http.Diagnostics;
/// <summary>
/// Interface to manage dependency metadata.
/// </summary>
public abstract class HttpDependencyMetadataResolver
{
/// <summary>
/// Get metadata for the specified HTTP request message.
/// </summary>
/// <param name="requestMessage">The HTTP request.</param>
/// <returns>The resolved <see cref="RequestMetadata"/> if found; otherwise, <see langword="false" />.</returns>
public virtual RequestMetadata? GetRequestMetadata(HttpRequestMessage requestMessage);
/// <summary>
/// Gets request metadata for the specified HTTP web request.
/// </summary>
/// <param name="requestMessage">The HTTP web request.</param>
/// <returns>The resolved <see cref="RequestMetadata"/> if found; otherwise, null.</returns>
public virtual RequestMetadata? GetRequestMetadata(HttpWebRequest requestMessage);
}
API Usage
The example below demonstrates a comprehensive HTTP request processing flow that showcases how the proposed abstract class enables flexible dependency resolution. The flow illustrates how the HttpDependencyMetadataResolver
abstract class would serve as a core building block in an HTTP processing pipeline that needs to:
- Resolve service dependency information from both static configuration and dynamic service discovery
- Apply appropriate telemetry enrichment based on identified dependencies
- Configure request-specific policies based on identified operation characteristics
This represents the primary use case for the abstract class API - allowing users to extend our optimized trie-based lookup algorithm while providing flexibility to integrate with their service discovery mechanisms and apply custom handling based on resolved metadata.
// 1. Create a custom implementation of the abstract class
public class CloudProviderDependencyResolver : HttpDependencyMetadataResolver
{
private readonly ICloudServiceDiscovery _serviceDiscovery;
public CloudProviderDependencyResolver (
ICloudServiceDiscovery serviceDiscovery,
IEnumerable<IDownstreamDependencyMetadata> staticMetadata)
: base(staticMetadata)
{
_serviceDiscovery = serviceDiscovery;
}
protected override RequestMetadata? GetRequestMetadataCore(string method, string host, string path)
{
// First try the base implementation using static metadata
var metadata = base.GetRequestMetadataCore(method, host, path);
if (metadata != null)
{
return metadata;
}
// Fall back to dynamic service discovery
return _serviceDiscovery.GetMetadataForRequest(method, host, path);
}
}
// 2. Register with dependency injection
public void ConfigureServices(IServiceCollection services)
{
// Register static metadata
services.AddSingleton<IDownstreamDependencyMetadata, PaymentServiceMetadata>();
services.AddSingleton<IDownstreamDependencyMetadata, InventoryServiceMetadata>();
// Register cloud service discovery
services.AddSingleton<ICloudServiceDiscovery, AzureServiceDiscovery>();
// Register custom dependency resolver
services.AddSingleton<HttpDependencyMetadataResolver, CloudProviderDependencyResolver>();
// Add HTTP client with appropriate middleware
services.AddHttpClient("api-client")
.AddHttpMessageHandler<DependencyMetadataHandler>();
}
// 3. Use in HTTP message handler
public class DependencyMetadataHandler : DelegatingHandler
{
private readonly HttpDependencyMetadataResolver _metadataResolver;
private readonly ITelemetry _telemetry;
public DependencyMetadataHandler(
HttpDependencyMetadataResolver metadataResolver,
ITelemetry telemetry)
{
_metadataResolver= metadataResolver;
_telemetry = telemetry;
}
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
var metadata = _metadataManager.GetRequestMetadata(request);
if (metadata != null)
{
// Add telemetry information
_telemetry.AddProperty("DependencyName", metadata.DependencyName);
_telemetry.AddProperty("RequestName", metadata.RequestName);
// Apply policy based on metadata
switch (metadata.RequestName)
{
case "CriticalPaymentOperation":
request.SetTimeout(TimeSpan.FromSeconds(10));
break;
case "BulkOperation":
request.SetTimeout(TimeSpan.FromMinutes(2));
break;
}
}
return await base.SendAsync(request, cancellationToken);
}
}
Alternative Designs
No response
Risks
No response