microsoft / reverse-proxy

A toolkit for developing high-performance HTTP reverse proxy applications.
https://microsoft.github.io/reverse-proxy
MIT License
8.43k stars 828 forks source link

Feature request: Configuration-driven proxy filtering #2165

Open bradygaster opened 1 year ago

bradygaster commented 1 year ago

In most of the scenarios where I've used YARP, I've found it useful to copy in a class I borrowed from another sample created during the Project Tye era that would read in values from IConfiguration to be pumped into the route and cluster endpoint values. In an example by Shayne Boyer, which I've mimicked in the CustomConfigFilter class I created for the Build 2023 cloud native .NET demo code, a configuration-driven filter would give developers a conventional way of mapping environment variables to placeholders in their YARP configuration.

Take the appsettings.json code below, from the same Build 2023 demo repo:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "System.Net.Http.HttpClient": "Warning",
      "Yarp": "Warning"
    }
  },
  "ORDERS_API": "http://localhost:5004",
  "STORE_UI": "http://localhost:5176",
  "ReverseProxy": {
    "Clusters": {
      "Frontend": {
        "Destinations": {
          "Store": {
            "Address": "{{STORE_UI}}"
          }
        }
      },
      "OrdersApi": {
        "Destinations": {
          "Store": {
            "Address": "{{ORDERS_API}}"
          }
        }
      }
    },
    "Routes": {
      "ClientRoute": {
        "ClusterId": "Frontend",
        "Match": {
          "Path": "{**catch-all}"
        }
      },
      "OrdersApiRoute": {
        "ClusterId": "OrdersApi",
        "Match": {
          "Path": "/api/orders/{**catch-all}"
        },
        "Transforms": [
          { "PathPattern": "/api/orders/{**remainder}" },
          { "PathRemovePrefix": "/api" }
        ]
      }
    }
  }
}

In this code, the ORDERS_API and STORE_UI values are hard-coded in the file as settings, so they'll be read in from here by default or from environment variables. The placeholders {{ORDERS_API}} and {{STORE_UI}} are replaced with the values in these settings or environment variables at runtime, making it easy for developers to use environment variables to route traffic between services. It also gives developers who want to integrate things like Tye with YARP to create new service discovery approaches.

I'll follow this issue up with a pull request to add the filter to the product's code.

Tratcher commented 1 year ago

It seems like you're doing 1:1 mapping of route patterns to destinations without any of the other features like health checks, load balancing, etc. In that case the new MapForwarder API might be more appropriate.

app.MapForwarder("{**catch-all}", configuration["STORE_UI"]);
app.MapForwarder("/api/orders/{**catch-all}", configuration["ORDERS_API"]);
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "System.Net.Http.HttpClient": "Warning",
      "Yarp": "Warning"
    }
  },
  "ORDERS_API": "http://localhost:5004",
  "STORE_UI": "http://localhost:5176"
}

Are you actually using the transforms in that example? They seem misconfigured.

samsp-msft commented 1 month ago

This feels like it should be more part of Aspire and a bigger question of how to do API gateways in Aspire. TestShop and eshop both have instances of YARP that use the service discovery Aspire component.

This is probably better addressed with #2525 and having a Aspire apphost component for YARP. Deploying a YARP to ACA to map URLs seems a bit redundant, but c'est la vie