-
Notifications
You must be signed in to change notification settings - Fork 122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[New article]: Configuring, Referencing, and Resolving Endpoints in .NET Aspire #2340
Comments
This is a brain dump: PrerequisiteThis document assumes familiarity with endpoint definitions and the basics of networking in Aspire. Refer to the Networking Overview for an introduction to these concepts. Understanding Endpoint PrimitivesThe EndpointReference is the fundamental type used to interact with another resource's endpoint. It provides properties such as:
These properties are dynamically resolved during the application’s startup sequence. Accessing them before the endpoints are allocated results in an exception. IResourceWithEndpointsResources supporting endpoints should implement IResourceWithEndpoints, enabling the use of GetEndpoint(name) to retrieve an EndpointReference. This is implemented on the built-in ProjectResource, ContainerResource and ExecutableResource. It allows endpoints to be programmatically accessed and passed between resources. Key Example: Endpoint Access and Resolution var builder = DistributedApplication.CreateBuilder(args);
var redis = builder.AddContainer("redis", "redis")
.WithEndpoint(name: "tcp", targetPort: 6379);
// Get a reference to the "tcp" endpoint by name
var endpoint = redis.GetEndpoint("tcp");
builder.Build().Run(); Understanding Endpoint Allocation and ResolutionWhat Does "Allocated" Mean?An endpoint is allocated when Aspire resolves its runtime values (e.g., In publish mode, endpoints are not allocated with concrete values. Instead, their values are represented as manifest expressions (e.g., Comparison: Run Mode vs. Publish Mode
Use the Accessing Allocated Endpoints SafelyEndpoint resolution happens during the startup sequence of the DistributedApplication. To safely access endpoint values (e.g., Url, Host, Port), you must wait until endpoints are allocated. Aspire provides eventing APIs, such as Example: Checking Allocation and Using Eventingvar builder = DistributedApplication.CreateBuilder(args);
// Add a Redis container with a TCP endpoint
var redis = builder.AddContainer("redis", "redis")
.WithEndpoint(name: "tcp", targetPort: 6379);
// Retrieve the EndpointReference
var endpoint = redis.GetEndpoint("tcp");
// Check allocation status and access Url
Console.WriteLine($"IsAllocated: {endpoint.IsAllocated}");
try
{
Console.WriteLine($"Url: {endpoint.Url}");
}
catch (Exception ex)
{
Console.WriteLine($"Error accessing Url: {ex.Message}");
}
// Subscribe to AfterEndpointsAllocatedEvent for resolved properties
builder.Eventing.Subscribe<AfterEndpointsAllocatedEvent>(
static (@event, cancellationToken) =>
{
Console.WriteLine($"Endpoint allocated: {endpoint.IsAllocated}");
Console.WriteLine($"Resolved Url: {endpoint.Url}");
return Task.CompletedTask;
});
// Start the application
builder.Build().Run(); Output
NOTE: The overloads of WithEnvironent that take a callback run after endpoints have been allocated. Referencing Endpoints from Other ResourcesUsing WithReferenceThe WithReference API allows you to pass an endpoint reference directly to a target resource. var builder = DistributedApplication.CreateBuilder(args);
var redis = builder.AddContainer("redis", "redis")
.WithEndpoint(name: "tcp", targetPort: 6379);
builder.AddProject<Projects.Worker>("worker")
.WithReference(redis.GetEndpoint("tcp"));
builder.Build().Run();
Using WithEnvironmentThe WithEnvironment API exposes endpoint details as environment variables, enabling runtime configuration. Example: Passing Redis Endpoint as Environment Variable var builder = DistributedApplication.CreateBuilder(args);
var redis = builder.AddContainer("redis", "redis")
.WithEndpoint(name: "tcp", targetPort: 6379);
builder.AddProject<Worker>("worker")
.WithEnvironment("RedisUrl", redis.GetEndpoint("tcp"));
builder.Build().Run(); WithEnvironment gives full control over the configuration names injected into the target resource. Core Primitives:
|
Source | Target | Resolution | Example URL |
---|---|---|---|
Container | Container | Container network (resource name:port ). |
redis:6379 |
Executable/Project | Container | Host network (localhost:port ). |
localhost:6379 |
Container | Executable/Project | Host network (host.docker.internal:port ). |
host.docker.internal:5000 |
Advanced Scenario: Dynamic Endpoint Resolution Across Contexts
Aspire resolves endpoints differently based on the execution context (e.g., run mode vs. publish mode, container vs. executable). Sometimes you want to override that resolution behavior.
Scenario
Below example shows a project that is going to setup up grafana and keycloak. We need to give the project the address for container-to-container communication between grafana and keycloak even though the target resource is a project. The project isn’t directly talking to keycloak or grafana, it's a mediator that is just setting URLs in the appropriate configuration of each container.
Example: Cross-Context Communication
Code Example
var builder = DistributedApplication.CreateBuilder(args);
var api = builder.AddProject<Projects.Api>("api")
.WithEnvironment(ctx =>
{
var keyCloakEndpoint = keycloak.GetEndpoint("http");
var grafanaEndpoint = grafana.GetEndpoint("http");
ctx.EnvironmentVariables["Grafana__Url"] = grafanaEndpoint;
if (ctx.ExecutionContext.IsRunMode)
{
// The project needs to get the URL for keycloak in the context of the container network,
// but since this is a project, it'll resolve the url in the context of the host network.
// We get the runtime url and change the host and port to match the container network pattern (host = resource name, port = target port ?? port)
var keycloakUrl = new UriBuilder(keyCloakEndpoint.Url)
{
Host = keycloak.Resource.Name,
Port = keyCloakEndpoint.TargetPort ?? keyCloakEndpoint.Port,
};
ctx.EnvironmentVariables["Keycloak__AuthServerUrl"] = keycloakUrl.ToString();
}
else
{
// In publish mode let the endpoint resolver handle the URL
ctx.EnvironmentVariables["Keycloak__AuthServerUrl"] = keyCloakEndpoint;
}
});
builder.Build().Run();
Great article! I did not understand the last example. Why do we need to handle run mode differently? Why not let the aspire handle like in publish mode? |
It’s missing some context. This is an edge case where the default resolution rules don’t work because you’re passing around an endpoint that you want to be resolve in a different context. The above example shows an api project that is going to setup up grafana and keycloak, we need to give it the address for container to container communication even though the target resource is a project. The project isn’t directly talking to keycloak, it’s just setting URLs in the grafana configuration. |
@davidebbo Can you take a look at the above to see if I missed anything? |
@davidfowl I will try to read through in the next few days. Having it as an issue comment makes it a bit hard to do inline commenting. Ideally, if you had it as a PR, it would be easier. |
Agree will do |
I've already shared this draft with a few colleagues and found it helpful. Would love to see this published to the live docs.. |
Proposed topic or title
Configuring, Referencing, and Resolving Endpoints in .NET Aspire
Location in table of contents.
App Host (Orchestration) → Networking Overview
Reason for the article
This article will provide an essential guide for developers to understand how Aspire endpoints are configured, referenced, and resolved. It will demystify APIs such as WithEnvironment and explain how endpoints behave in both run and publish modes. This understanding is crucial for developers to integrate and automate resources within Aspire seamlessly. Without this knowledge, users might face challenges connecting services or leveraging the full potential of the Aspire app model.
Article abstract
This article will explore the resource endpoints, covering configuration, reference mechanisms, and resolution rules of those endpoints. It will illustrate how to use APIs like WithEnvironment to ensure consistent connectivity between resources and how endpoints are resolved between run and publish modes. Examples will guide developers through typical workflows and edge cases, ensuring robust and reliable service integrations.
Relevant searches
"Aspire endpoint resolution"
"WithEnvironment API .NET Aspire"
"Configuring endpoints in Aspire"
"Aspire run mode vs publish mode endpoints"
The text was updated successfully, but these errors were encountered: