Building a lightweight, controller-less, Markdown-only website in ASP.NET Core

In this blog post let’s have a look at building a lightweight site in ASP.NET Core.

In “classic” ASP.NET we had the WebPages framework – which allowed us to build sites composed only of views. This was perfect for lightweight projects, where we didn’t need the entire model-controller infrastructure.

At the moment, ASP.NET Core doesn’t have an equivalent yet (though it’s being worked on), but we have already provided a similar type of experience via the WebApiContrib project (you can read more about the project here). With the help of some of the libraries from there, we can build controller-less sites for ASP.NET Core already.

In addition to that, we can combine it with using Markdown tag helpers for content delivery – and it will result in a very cool experience – being able to author ASP.NET Core sites, without controllers, in Markdown. With Razor sprinkled on top of it, to provide dynamic data.

Let’s have a look – more after the jump.

Getting started

To get started, let’s create a new empty ASP.NET Core project and add references to the following WebAPiContrib.Core packages:

Here is my full project.json file. It seems quite large but it’s a typical ASP.NET Core app, created from the default template.

What’s worth mentioning, is that I also added Microsoft.AspNetCore.StaticFiles middleware, so that we can serve up some CSS. The rest is – again – pretty standard stuff from the default web application template, so Kestrel, IIS integration, as well as the default build and publish options.

Configuring WebApiContrib.Core.WebPages

In order to configure our web pages, we need to add the following to the Startup class:

The pattern is the same as in adding/configuring i.e. fully fledged MVC – first we are “adding” our framework, and then we are “using” it.

Next step is to actually add some views – the views will serve as our pages, and there names will also define the routes – since we have no controllers in place, everything happens by convention. We’ll get back to this in a moment.

If you look again at the snippet above, we also configured the view called Index.cshtml to act as the root of the site (so our “homepage”) – it is the view that will be served when navigating to the root of the domain where the site will be deployed.

Adding layout

Now, let’s imagine that we are building a simple blog – this could be a good example for us.

The Index.cshtml view will be our root, listing all of the posts. Then each of the posts will be a separate view (separate physical file in the Views folder) and it will be authored as a Razor/Markdown combination.

However, before we get on to that, let’s first add all of the remaining bootstrapping that we need.

It’s quite typical that we’d want to have a shared layout for our site – this in Razor is normally represented by a _Layout.cshtml file. So let’s add that.

The responsibility of our _Layout.cshtml will be to provide the page title and load up the necessary CSS. This is shown below.

Our layout file is using the classic Razor ViewBag to read some data from the views it will wrap around – each view will be able to pass a Title and a flag whether a link back to home page should be rendered or not.

Note: I am using a nice and elegant Markdown CSS file from here. This also means that the referenced CSS file exists in my wwwroot/css folder.

Configuring Markdown tag helper

Since we want to use Markdown to render our pages, and we already pulled the package for it, we just need to make it visible to our Razor views.

To do that, we need to add a _ViewImports.cshtml file, with the following content:

This will import the Markdown tag helpers (the package actually has two of them, we will be using just one though, the “basic” one).

The WebApiContrib.Core.TagHelpers.Markdown tag helpers (you can read more about it here) allows us to use an <md />tag and write Markdown directly in our Razor views, and it will get auto-converted to HTML. The nice think about it is that we get a cool authoring experience – as writing Markdown files for content driven sites is usually more efficient than writing pure HTML.

Also, once a Razor view is rendered, it will be cached, so the fact that we will convert to Markdown on the fly doesn’t matter that much, as only the first hit will be slower.

Adding views

At this point we can start adding our views. Since we already established that Index.cshtml will be our root, let’s add it. It will act as our table of contents.

Notice that we set the layout file and interact with the ViewBag identically how you’d do it in a typical, fully-fledged MVC application. Since this is our root page, we can hide the “back to home” link and we do it via a relevant flag (we just added that logic to _Layout.cshtml moments ago).

The content of the page itself is a a simple Markdown header and a list with some links. We already mentioned in the beginning, that the names of the views will correspond to the links/routes that are available in our lightweight application. So – as a consequence – the above structure implies that we need to have WebApiContrib.cshtml and FormatFilter.cshtml in our Views folder.

So let’s add both of them. I am not gonna show their entire structure here – cause for demo purposes I used my old blog posts here (I just grabbed the last two: this one and this one) and they are fairly long (btw – I write the posts in Markdown, obviously). Instead in the snippets below, I will abbreviate their “content” part to save up on space.

But they are similar to our Index.cshtml – some Razor bootrsrapping on top, and then Markdown content.



One interesting note is that we can use the typical Razor mechanisms when authoring our posts. For example in the above snippets we defined the title as a local variable and used it both to set the title of the page, and to set the H1 of the Markdown content.

Similarly, you can make your life easier by injecting other dynamic content, leveraging loops or even accessing external services to pull some data.

You could even use the @inject directive to inject services into the views.

Source code & demo

So this is everything!

We now have a fully functional, controller-less, Markdown-driven, lightweight, ASP.NET Core site. We just have views, and all the content is written almost entirely in pure Markdown.

Of course this is a very basic implementation, but I hope it nudges you in a useful direction or inspires you to do cool things with ASP.NET Core.

The links for this post:

Be Sociable, Share!

  • Dan Martin

    Awesome, thanks for posting this. I had looked into doing something similar in MVC a few years back and couldn’t get much going. Nice to see how you got it working and thanks for sharing the details.

  • Paul Wheeler

    Cool idea!
    FWIW: I always cringe when I see dynamic properties treated as booleans in razor views (especially layouts) like “if (!ViewBag.HideBackLink) …” since they will throw exceptions if the property HideBackLink is null (like when the page forgets to set it). I always recommend the style “if (ViewBag.HideBackLink == true) … ” which doesn’t throw exceptions on null.

  • Manuel Wolfgram

    Just out of curiosity, what would the benefits be going with this approach vs. say using a static site generator like Wyam ( that can parse and render Razor templates?