Use appsettings in a Giraffe web app

During the learning of F # the inevitable question arises on how to use the language for your day to day tasks. You can create a web site using F# using the Giraffe framework. The framework can be found on github. The most amazing part that I found is their documentation. A lot can be found to get you started. A missing piece in tutorials is how to use an appsettings.json file with the Giraffe framework, and that's what this post is about.

Installing the .net core Giraffe template

We'll start of by installing the Giraffe template:
You can verify if the template is already installed by opening a command prompt and execute

dotnet new  

This will give you a list of all installed .net core templates.

If you don't have the template yet, you can install it via:

dotnet new -i "giraffe-template::*"  

Quick start to create a solution

dotnet new giraffe -V razor -lang F# --name DevProtocol.Giraffe.SettingsDemo.Web  
cd src  
dotnet new sln --name DevProtocol.Giraffe.SettingsDemo  
dotnet sln add DevProtocol.Giraffe.SettingsDemo.Web/DevProtocol.Giraffe.SettingsDemo.Web.fsproj  

Organize your project

The template will get you up and running with Giraffe, but I like to organize the things a bit. By default all application logic can be found in the Program.fs file. I like to separate the routing and the http handling.
Remove the indexHandler and webApp method from your Program.fs file.

  • Add a file Routing.fs above Program.fs
  • Add HttpHandler.fs above Routing.fs

Note: If you're using Visual Studio 2017 you can move the files up and down via ALT+arrow

Add routes

Add a default route in your Routing.fs file

module DevProtocol.Giraffe.SettingsDemo.Web.Routing

open Giraffe  
open DevProtocol.Giraffe.SettingsDemo.Web.HttpHandlers

let routes: HttpFunc -> HttpFunc =  
    choose [
        GET >=>
            choose [
                route "/" >=> indexHandler
            ]
        setStatusCode 404 >=> text "Not Found" ]

The configuration of the routes is nearly identical to the route configuration of Suave (another popular F# web framework).

Add a httpHandler

In the HttpHandler.fs file we need to define the indexHandler:

module DevProtocol.Giraffe.SettingsDemo.Web.HttpHandlers

open Microsoft.AspNetCore.Http  
open Microsoft.Extensions.Configuration  
open Giraffe  
open Giraffe.Razor  
open DevProtocol.Giraffe.SettingsDemo.Web.Models

let indexHandler =  
    fun (next: HttpFunc) (ctx: HttpContext) ->
        let greetings = sprintf "Hello from Giraffe!" 
        let model     = { Text = greetings }
        razorHtmlView "Index" model next ctx

Adding the appsettings.json

Add a appsettings.json file to your project containing

{
  "Greeting": "Hello from appsettings"
}

Make sure the file is copied to the output directory
In visual studio change the properties of the file to copy to output if newer. Or in VSCode open the fsproj file and verify the ItemGroup:

 <ItemGroup>
    <Content Include="appsettings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
  </ItemGroup>

Add the NuGet package

Microsoft.Extensions.Configuration.Json  

So if you use VSCode add the following to your fsproj file:

<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.1" /  

In your Program.fs file you'll find the main method. You'll notice that the WebHostBuilder is configured in that method. We'll add the ConfigureAppConfiguration there:

[<EntryPoint>]
let main _ =  
    let contentRoot = Directory.GetCurrentDirectory()
    let webRoot     = Path.Combine(contentRoot, "WebRoot")
    WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(contentRoot)
        .UseIISIntegration()
        .UseWebRoot(webRoot)
// Add the ConfigureAppConfiguration method
        .ConfigureAppConfiguration(configureAppConfiguration)
        .Configure(Action<IApplicationBuilder> configureApp)
        .ConfigureServices(configureServices)
        .ConfigureLogging(configureLogging)
        .Build()
        .Run()
    0

Now we need to configure what this ConfigureAppConfiguration does:

let configureAppConfiguration  (context: WebHostBuilderContext) (config: IConfigurationBuilder) =  
    config
        .AddJsonFile("appsettings.json",false,true)
        .AddJsonFile(sprintf "appsettings.%s.json" context.HostingEnvironment.EnvironmentName ,true)
        .AddEnvironmentVariables() |> ignore

The first AddJsonFile method says that the appsettings.json file is not optional (so mandatory). The second AddJsonFile method says that the appsettings.[environment].json file is optional (so it will be used if available). The result is that we now get access to the IConfiguration via the HttpContext

And finally add an open statement:

open Microsoft.Extensions.Configuration  

Using the appsettings

Now we can use our appsettings in our httpHandlers. Our indexHandler functions has access to the HttpContext. So all we need to do is get an IConfiguration from it and request our Greeting setting:

let indexHandler =  
    fun (next: HttpFunc) (ctx: HttpContext) ->
        let settings = ctx.GetService<IConfiguration>()
        let model     = { Text = "Giraffe demo: " + settings.["Greeting"] }
        razorHtmlView "Index" model next ctx

You should now be able to run the app and seeing the content of the Greeting

The transformations of the appsettings files will work as expected. Add another appsettings file "appsettings.Development.json", make sure the file is copied to the output directory. Change the Greetings value:

{
  "Greeting": "hello from DEV appsettings"
}

Download

Download the code from my github

References