File: ConfigurationBuilder\YarpRoute.cs
Web Access
Project: src\src\Aspire.Hosting.Yarp\Aspire.Hosting.Yarp.csproj (Aspire.Hosting.Yarp)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Yarp.ReverseProxy.Configuration;
 
namespace Aspire.Hosting.Yarp;
 
/// <summary>
/// Represents a route for YARP
/// </summary>
public class YarpRoute
{
    // Testing only
    internal YarpRoute(RouteConfig routeConfig)
    {
        RouteConfig = routeConfig;
    }
 
    internal YarpRoute(YarpCluster cluster, string routeId)
    {
        RouteConfig = new RouteConfig
        {
            RouteId = routeId,
            ClusterId = cluster.ClusterConfig.ClusterId,
            Match = new RouteMatch(),
        };
    }
 
    internal RouteConfig RouteConfig { get; private set; }
 
    internal void Configure(Func<RouteConfig, RouteConfig> configure)
    {
        RouteConfig = configure(RouteConfig);
    }
}
 
/// <summary>
/// Provides extension methods for configuring a YARP destination
/// </summary>
public static class YarpRouteExtensions
{
    /// <summary>
    /// Set the parameters used to match requests.
    /// </summary>
    public static YarpRoute WithMatch(this YarpRoute route, RouteMatch match)
    {
        route.Configure(r => r with { Match = match });
        return route;
    }
 
    #region RouteMatch helpers
 
    private static YarpRoute ConfigureMatch(this YarpRoute route, Func<RouteMatch, RouteMatch> match)
    {
        route.Configure(r => r with { Match = match(r.Match) });
        return route;
    }
 
    /// <summary>
    /// Only match requests with the given Path pattern.
    /// </summary>
    public static YarpRoute WithMatchPath(this YarpRoute route, string path)
    {
        route.ConfigureMatch(match => match with { Path = path });
        return route;
    }
 
    /// <summary>
    /// Only match requests that use these optional HTTP methods. E.g. GET, POST.
    /// </summary>
    public static YarpRoute WithMatchMethods(this YarpRoute route, params string[] methods)
    {
        route.ConfigureMatch(match => match with { Methods = methods });
        return route;
    }
 
    /// <summary>
    /// Only match requests that contain all of these headers.
    /// </summary>
    public static YarpRoute WithMatchHeaders(this YarpRoute route, params RouteHeader[] headers)
    {
        route.ConfigureMatch(match => match with { Headers = headers.ToList() });
        return route;
    }
 
    /// <summary>
    ///  Only match requests with the given Host header. Supports wildcards and ports.
    ///  For unicode host names, do not use punycode.
    /// </summary>
    public static YarpRoute WithMatchHosts(this YarpRoute route, params string[] hosts)
    {
        route.ConfigureMatch(match => match with { Hosts = hosts.ToList() });
        return route;
    }
 
    /// <summary>
    ///  Only match requests that contain all of these query parameters.
    /// </summary>
    public static YarpRoute WithMatchRouteQueryParameter(this YarpRoute route, params RouteQueryParameter[] queryParameters)
    {
        route.ConfigureMatch(match => match with { QueryParameters = queryParameters.ToList() });
        return route;
    }
 
    #endregion
 
    /// <summary>
    /// Set the order for the destination
    /// </summary>
    public static YarpRoute WithOrder(this YarpRoute route, int? order)
    {
        route.Configure(r => r with { Order = order });
        return route;
    }
 
    /// <summary>
    /// Set the MaxRequestBodySize for the destination
    /// </summary>
    public static YarpRoute WithMaxRequestBodySize(this YarpRoute route, long maxRequestBodySize)
    {
        route.Configure(r => r with { MaxRequestBodySize = maxRequestBodySize });
        return route;
    }
 
    /// <summary>
    /// Set the Metadata of the destination
    /// </summary>
    public static YarpRoute WithMetadata(this YarpRoute route, IReadOnlyDictionary<string, string>? metadata)
    {
        route.Configure(r => r with { Metadata = metadata });
        return route;
    }
 
    /// <summary>
    /// Set the Transforms of the destination
    /// </summary>
    public static YarpRoute WithTransforms(this YarpRoute route, IReadOnlyList<IReadOnlyDictionary<string, string>>? transforms)
    {
        route.Configure(r => r with { Transforms = transforms });
        return route;
    }
 
    /// <summary>
    /// Add a new transform to the destination
    /// </summary>
    public static YarpRoute WithTransform(this YarpRoute route, Action<IDictionary<string, string>> createTransform)
    {
        ArgumentNullException.ThrowIfNull(createTransform);
 
        route.Configure(r =>
        {
            List<IReadOnlyDictionary<string, string>> transforms;
            if (r.Transforms is null)
            {
                transforms = new List<IReadOnlyDictionary<string, string>>();
            }
            else
            {
                transforms = new List<IReadOnlyDictionary<string, string>>(r.Transforms.Count + 1);
                transforms.AddRange(r.Transforms);
            }
 
            var transform = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
            createTransform(transform);
            transforms.Add(transform);
 
            return r with { Transforms = transforms };
        });
 
        return route;
    }
}