This example demonstrates the Configuration APIs in Dapr. It demonstrates the following APIs:
- configuration: Get configuration from statestore
- configuration: Subscribe Configuration
Note: Make sure to use the latest proto bindings
This example shows the usage of two different Configuration APIs. The GetConfiguration call and the SubscribeConfiguration call. Both of these calls can be handled in two different ways.
Both the Get and Subscribe APIs can be called directly through the DaprClient. The Get response
can be read directly. The Subscribe API provides an IAsyncEnumerable
which should be handled using
the appropriate async procedures such as await foreach
.
The Subscribe response will also provide an Id after receiving a streamed update. This Id has to be used to Unsubscribe and close the streaming connection.
var configItems = await client.GetConfiguration(configStore, new List<string>() { queryKey });
foreach (var item in configItems.Items)
{
logger.LogInformation($"Got configuration item:\nKey: {item.Key}\nValue: {item.Value.Value}\nVersion: {item.Value.Version}");
}
var subscribeConfigurationResponse = await client.SubscribeConfiguration(configStore, new List<string>() { queryKey });
logger.LogInformation("Watching configuration for 1 minute.");
var cts = new CancellationTokenSource(TimeSpan.FromMinutes(1));
var data = new Dictionary<string, string>(Data);
try
{
// This loop listens for a minute and puts any results into the data dictionary.
await foreach (var items in subscribeConfigurationResponse.Source.WithCancellation(cts.Token))
{
foreach (var item in items)
{
id = subscribeConfigurationResponse.Id;
data[item.Key] = item.Value.Value;
}
}
}
catch (TaskCanceledException)
{
// If the connection didn't close before the Task was cancelled, try and unsubscribe.
if (!string.IsNullOrEmpty(subscribeConfigurationResponse.Id))
{
await client.UnsubscribeConfiguration(configStore, source.Id);
}
}
The SDK also provides an extension that allows the Configuration API to be included with the existing Application Configuration paradigm. To utilize them this way, simply include them in your host construction. Note that the values are first fetched without streaming. This is because the streaming values are only included in the configuration after the first update. This simply fetches the initial values and loads them into the configuration before subscribing to them.
return Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(config =>
{
// Get the initial value and continue to watch it for changes.
config.AddDaprConfigurationStore("redisconfig", new List<string>() { "withdrawVersion" }, client, TimeSpan.FromSeconds(20));
config.AddStreamingDaprConfigurationStore("redisconfig", new List<string>() { "withdrawVersion", "source" }, client, TimeSpan.FromSeconds(20));
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
And then reference the IConfiguration
object in whatever classes it is needed.
private ILogger<ConfigurationController> logger;
private IConfiguration configuration;
private DaprClient client;
public ConfigurationController(ILogger<ConfigurationController> logger, IConfiguration configuration, [FromServices] DaprClient client)
{
this.logger = logger;
this.configuration = configuration;
this.client = client;
}
[HttpGet("extension")]
public Task SubscribeAndWatchConfiguration()
{
logger.LogInformation($"Getting values from Configuration Extension, watched values ['withdrawVersion', 'source'].");
logger.LogInformation($"'withdrawVersion' from extension: {configuration["withdrawVersion"]}");
logger.LogInformation($"'source' from extension: {configuration["source"]}");
return Task.CompletedTask;
}
This example relies on the ControllerSample
. All functionality won't be available without it running.
Note: This must be executed before starting the application.
GetConfiguration
treats missing configuration as an exceptional case.
docker exec dapr_redis redis-cli SET withdrawVersion "v1||1"
The above command creates an object with a key 'withdrawVersion' and a value of 'v1' in your redis store. The ||1
denotes the version of the key and is not necessary but is included for fullness.
Change directory to this folder:
cd examples/AspNetCore/ControllerSample
To run the ControllerSample
, execute the following command:
dapr run --app-id controller --app-port 5000 -- dotnet run
Change directory to this folder:
cd examples/Client/ConfigurationApi
To run the ConfigurationExample
, execute the following command:
dapr run --app-id configexample --components-path ./Components -- dotnet run
Using curl or the Dapr CLI, call the Get Configuration API.
curl 'http://localhost:5010/configuration/get/redisconfig/withdrawVersion'
You should see the following output from the application:
== APP == info: ConfigurationApi.Controllers.ConfigurationController[0]
== APP == Querying Configuration with key: withdrawVersion
== APP == info: ConfigurationApi.Controllers.ConfigurationController[0]
== APP == Got configuration item:
== APP == Key: withdrawVersion
== APP == Value: v1
== APP == Version: 1
Note: This step requires multiple terminals.
This step will utilize the ControllerSample
to show how a subscribed configuration can be used as a dynamic feature flag. The ControllerSample
vends
several APIs, but the one we're interested in is the withdraw
API which also has a withdraw.v2
version.
First, we need to setup an account with some money in it. We'll do this with the deposit
API. Run the following command:
curl -X POST http://127.0.0.1:5000/deposit -H "Content-Type: application/json" -d "{ \"id\": \"1\", \"amount\": 100000 }"
Now, we can start making requests. Without changing anything in the configuration, run the following command:
curl -X POST http://127.0.0.1:5010/configuration/withdraw -H "Content-Type: application/json" -d "{ \"id\": \"1\", \"amount\": 10 }"
You should the following output in your configuration app:
== APP == info: ConfigurationApi.Controllers.ConfigurationController[0]
== APP == Calling V1 Withdraw API: ControllerSample.Transaction
Now, let's move our application to use the v2 API. Do this by running the following command:
docker exec dapr_redis redis-cli SET withdrawVersion "v2||1"
Run the withdraw command again and you'll see an output like this from your app:
== APP == info: ConfigurationApi.Controllers.ConfigurationController[0]
== APP == Calling V2 Withdraw API - Id: 1 Amount: 10 Channel: local
We use the 'local' channel because nothing has been set in your configuration. Change the 'source' configuration item to 'mobile' and increase the withdraw to 100000. Doing this, we can see a new feature in the V2 API which is denying requests that are too large from mobile sources.
docker exec dapr_redis redis-cli SET source "mobile||1"
curl -X POST http://127.0.0.1:5010/configuration/withdraw -H "Content-Type: application/json" -d "{ \"id\": \"1\", \"amount\": 100000 }"
After the withdraw command, you should see an error in your application:
== APP == fail: ConfigurationApi.Controllers.ConfigurationController[0]
== APP == Error executing withdrawal: An exception occurred while invoking method: 'withdraw.v2' on app-id: 'controller'
Using curl or the Dapr CLI, call the endpoint that prints configuration from the extension.
curl 'http://localhost:5010/configuration/extension'
You should see an output similar to what is below. If you've changed the value of 'withdrawVersion' or 'source', you could see a different value:
== APP == Getting values from Configuration Extension, watched values ['withdrawVersion', 'source'].
== APP == info: ConfigurationApi.Controllers.ConfigurationController[0]
== APP == 'withdrawVersion' from extension: v2
== APP == info: ConfigurationApi.Controllers.ConfigurationController[0]
== APP == 'source' from extension: mobile