Dusted Codes

Programming adventures

Announcing Giraffe 1.0.0

I am pleased to announce the release of Giraffe 1.0.0, a functional ASP.NET Core web framework for F# developers. After more than a year of building, improving and testing the foundations of Giraffe it makes me extremely happy to hit this important milestone today. With the help of 32 independent contributors, more than a hundred closed GitHub issues and an astonishing 79 merged pull requests (and counting) it is fair to say that Giraffe has gone through many small and big changes which made it what I believe one of the best functional web frameworks available today.

The release of Giraffe 1.0.0 continues with this trend and also brings some new features and improvements along the way:

Streaming support

Giraffe 1.0.0 offers a new streaming API which can be used to stream (large) files and other content directly to a client.

A lot of work has been put into making this feature properly work like supporting conditional HTTP headers and range processing capabilities. On top of that I was even able to help iron out a few bugs in ASP.NET Core MVC's implementation as well (loving the fact that ASP.NET Core is all open source).

Conditional HTTP Headers

In addition to the new streaming API the validation of conditional HTTP headers has been exposed as a separate feature too. The ValidatePreconditions function is available as a HttpContext extension method which can be used to validate If-{...} HTTP headers from within any http handler in Giraffe. The function will self determine the context in which it is called (e.g. GET POST, PUT, etc.) and return a correct result denoting whether a request should be further processed or not.

Configuration of serializers

A much desired and important improvement was the ability to change the default implementation of data serializers and content negotiation. Giraffe 1.0.0 allows an application to configure the default JSON or XML serializer via ASP.NET Core's services container.

Detailed XML documentation

For the first time Giraffe has detailed XML documentation for all public facing functions available:

giraffe-xml-docs

Even though this is not a feature itself, it aims at improving the general development experience by providing better IntelliSense and more detailed information when working with Giraffe.

Giraffe.Tasks deprecated

When Giraffe introduced the task {} CE for the first time it was a copy of the single file project TaskBuilder.fs written by Robert Peele. However, maintaining our own copy of the task CE is resource expensive and not exactly my personal field of expertise. Besides that, since the initial release Robert has made great improvements to TaskBuilder.fs whereas Giraffe's version has been lacking behind. When TaksBuilder.fs has been published to NuGet it felt like a good idea to deprecate Giraffe.Tasks and resort back to the original.

This allows me and other Giraffe contributors to focus more on the web part of Giraffe and let Robert do his excellent work on the async/task side of things. Otherwise nothing has changed and Giraffe will continue to build on top of Task and Task<'T>. If you use Giraffe.Tasks outside of a Giraffe web application then you can continue doing so by referencing TaskBuilder.fs instead.

Giraffe also continues to use exclusively the context insensitive version of the task CE (meaning all task objects are awaited with ConfigureAwait(false)). If you encouter type inference issues after the upgrade to Giraffe 1.0.0 then you might have to add an extra open statement to your F# file:

open FSharp.Control.Tasks.ContextInsensitive

This is normally not required though unless you have do! bindings in your code.

If you like the task {} computation expression then please go to the official GitHub repository and hit the star button to show some support!

TokenRouter as NuGet package

TokenRouter is a popular alternative to Giraffe's default routing API aimed at providing maximum performance. Given the complexity of TokenRouter and the fact that Giraffe already ships a default version of the routing API it made only sense to decouple the TokenRouter into its own repository.

This change will allow TokenRouter to become more independent and evolve at its own pace. TokenRouter can also benefit from having its own release cycle and be much bolder in introducing new features and breaking changes without affecting Giraffe.

If your project is using the TokenRouter API then you will need to add a new dependency to the Giraffe.TokenRouter NuGet package now. The rest remains unchanged.

Improved documentation

At last I have worked on improving the official Giraffe documentation by completely restructuring the document, providing a wealth of new information and focusing on popular topics by demand.

The documentation has also been broken out of the README, but remains as a Markdown file in the git repository for reasons which I hope to blog about in a separate blog post soon.

The complete list of changes and new features can be found in the official release notes.

Thank you for reading this blog and using Giraffe (and if you don't, then give it a try ;))!

Comments

Extending the Giraffe template with different view engine options

This is going to be a quick but hopefully very useful tutorial on how to create a more complex template for the dotnet new command line tool.

If you have ever build a Giraffe web application then you've probably started off by installing the giraffe-template into your .NET CLI and then created a new boilerplate application by running the dotnet new command:

dotnet new giraffe

(There is currently a bug with the .NET CLI which forces you to specify the -lang parameter.)

Previously this would have created a new Giraffe web application which would have had the Giraffe.Razor NuGet package included and a default project structure with MVC's famous Razor view engine.

Since today (after you've updated the giraffe-template to the latest version) you can choose between three different options:

The dotnet new giraffe command optionally supports a new parameter called --ViewEngine or short -V now:

dotnet new giraffe -ViewEngine razor

If you are unsure which options are available you can always request help by running:

dotnet new giraffe --help

The output of the command line will display all available options and supported values as well:

Giraffe Web App (F#)

Author: Dustin Moris Gorski, David Sinclair and contributors

Options:
  -V|--ViewEngine
        giraffe    - Default GiraffeViewEngine
        razor      - MVC Razor views
        dotliquid  - DotLiquid template engine

        Default: giraffe

If you do not specify a view engine then the dotnet new giraffe command will automatically create a new Giraffe web application with the default GiraffeViewEngine engine.

Creating multiple project templates as part of one dotnet new template

There are many ways of how I could have programmed these options into the Giraffe template, but none of them are very obviously documented in one place. The documentation of the dotnet templating engine1 is fairly scattered across multiple resources and hard to understand if you have never worked with it before. Part of today's blog post I thought I'll quickly sum up one of the options which I believed was the cleanest and most straight forward one.

Each view engine has a significant impact on the entire project structure, such as NuGet package dependencies, folder structure, code organisation and files which need to be included. I didn't want to hack around with #if - #else switches and introduce complex add-, modify- or delete rules and consequently decided that the easiest and least error prone way would be to create a complete independent template for each individual view engine first:

src
+-- giraffe-template.nuspec
|
+-- content
    |
    +-- .template.config
    |   +-- template.json
    |
    +-- DotLiquid.Template
    |   +-- Views
    |   +-- WebRoot
    |   +-- Program.fs
    |   +-- AppNamePlaceholder.fsproj
    |
    +-- Razor.Template
    |   +-- Views
    |   +-- WebRoot
    |   +-- Program.fs
    |   +-- AppNamePlaceholder.fsproj
    |
    +-- Giraffe.Template
        +-- WebRoot
        +-- Program.fs
        +-- AppNamePlaceholder.fsproj

I split the content of the Giraffe template into three distinctive sub templates:

As you can see from the diagram there's still only one .template.config\template.json file at the root of the content folder and only one giraffe-template.nuspec file.

The benefit of this structure is very simple. There is a clear separation of each template and each template is completely independent of the other templates which makes maintenance very straight forward. I can work on each template as if they were small projects with full Intellisense and IDE support and being able to build, run and test each application.

The next step was to create the --ViewEngine parameter inside the template.json file:

"symbols": {
    "ViewEngine": {
      "type": "parameter",
      "dataType": "choice",
      "defaultValue": "giraffe",
      "choices": [
        {
          "choice": "giraffe",
          "description": "Default GiraffeViewEngine"
        },
        {
          "choice": "razor",
          "description": "MVC Razor views"
        },
        {
          "choice": "dotliquid",
          "description": "DotLiquid template engine"
        }
      ]
    }
  }

All I had to do was to define a new symbol called ViewEngine of type parameter and data type choice. Then I specified all supported options via the choice array and set the giraffe option as the default value.

Now that the ViewEngine parameter has been created I was able to use it from elsewhere in the specification. The sources section of a template.json file denotes what source code should be installed during the dotnet new command. In Giraffe's case this was very easy. If the giraffe option has been selected, then the source code shall come from the Giraffe.Template folder and the destination/target folder should be the root folder of where the dotnet new command is being executed from. The same logic applies to all the other options as well:

"sources": [
    {
      "source": "./Giraffe.Template/",
      "target": "./",
      "condition": "(ViewEngine == \"giraffe\")"
    },
    {
      "source": "./Razor.Template/",
      "target": "./",
      "condition": "(ViewEngine == \"razor\")"
    },
    {
      "source": "./DotLiquid.Template/",
      "target": "./",
      "condition": "(ViewEngine == \"dotliquid\")"
    }
  ]

With this in place I was able to create a new giraffe-template NuGet package and deploy everything to the official NuGet server again.

This is literally how easy it is to support distinct project templates from a single dotnet new template.

Different templates with same groupIdentifier

Another very similar, but in my opinion less elegant way would have been to create three different template.json files and use the groupIdentifier setting in connection with the tags array to support three different templates as part of one. Unfortunately this option doesn't seem to be very well supported from the .NET CLI. Even though it works, the .NET CLI doesn't display any useful error message when a user makes a mistake or when someone types dotnet new giraffe --help into the terminal. It also doesn't allow a default value to be set which made it less attractive overall. I would only recommend to go with this option if you need to provide different templates based on the selected .NET language, in which case it works really well again.

If you have any further questions or you would like to know more about the details of the Giraffe template then you can visit the giraffe-template GitHub repository for further reference.

This blog post is part of the F# Advent Calendar in English 2017 blog series which has been kindly organised by Sergey Tihon. Hope you all enjoyed this short tutorial and wish you a very Merry Christmas!

1) Templating engine documentation

Various documentation for the dotnet new templating engine can be found across the following resources:

Comments

Older Posts Newer Posts