asp.net-coref#saturn-frameworksafe-stack

How to access and use user secrets in a saturn application


Edit: here are some adaptions to the answer by Koenig Lear,in case someone else stumbles upon this question:

to add user secrets to the application i used this code, where the ID used by config.AddUserSecrets is the secrets id that gets added to the Server.fsproj by using dotnet user-secrets init.

app
    .ConfigureAppConfiguration(
        System.Action<Microsoft.Extensions.Hosting.HostBuilderContext,IConfigurationBuilder> ( fun ctx config ->
            config.AddUserSecrets("6de80bdf-2a05-4cf7-a1a8-d08581dfa887") |> ignore
        )
)
|> run

The secret can then be consumed in the router via:

 forward "/api" (fun next ctx ->

        let settings = ctx.GetService<IConfiguration>()
        let cString = settings.["Some:ConnectionString"]

        webApp cString next ctx

    )

Original Question:

I'm trying to wrap my head around how to access user secrets, but i cant quite translate the example provided by microsoft.

lets say i have Saturn running in a SAFE stack environment (most likely irrelevant in this case). I have a webApp, which takes a connection string and then handles requests using the provided connection string for DB access:

let router connectionString = forward "/api" (webApp connectionString)

and a user secret that contains the connection string that i initiated using dotnet user-secrets set :

{
  "Some:ConnectionString": "Data Source=..."
}

and an application that uses the router:

let app = application {
    url ("https://0.0.0.0:" + port.ToString() + "/")
    use_router (router )
}

What i am trying to do is somehow extract the user secret and pass it to the router. What i dont seem to grasp is where and how to perform the dependency injection that ASP.NetCore usually does (see where the user secret is configured via the Configuration API in the MS Docs example.).

What comes very close to what i am trying to do is this section from the example :


public class Startup
{
    private string _connection = null;

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        var builder = new SqlConnectionStringBuilder(
            Configuration.GetConnectionString("Movies"));
        builder.Password = Configuration["DbPassword"];
        _connection = builder.ConnectionString;
    }

    public void Configure(IApplicationBuilder app)
    {
        app.Run(async (context) =>
        {
            await context.Response.WriteAsync($"DB Connection: {_connection}");
        });
    }
}

The first problem is that i cant find how to get the Configuration object that contains the GetConnectionString method. But even if i could do that, i would configure this section via the service_config operation in the application monad right? But then I can't use the obtained connection string for the router, because the use_router operation is separated from it.

I am using Saturn 0.14.1 on netcoreapp/asp.netcore 3.1 .

Any kind of help would be greatly appreciated!


Solution

  • You should be able to use the ConfigureAppConfiguration method:

    open Microsoft.Extensions.Configuration
    open Microsoft.Extensions.Configuration.UserSecrets
    
    let configureAppConfiguration  (context: WebHostBuilderContext) (config:IConfigurationBuilder) =  
        config
            .AddUserSecrets()
    
    app
       .ConfigureAppConfiguration(configureAppConfiguration)
       .run
    

    Inside your router you could use it like this:

    let webApp = router {
        get "/" (fun next ctx ->
            task {
                let settings = ctx.GetService<IConfiguration>()
                let s = settings.["Some:ConnectionString"]
                ...
            })
    }