Technical Blog

Converting Markdown Into HTML Web Pages Within ASP.NET MVC

How to convert Markdown documents into HTML web pages using Microsoft ASP.NET MVC architecture.

Carl Mills

Published

How To Convert Markdown Documents Into HTML Web Pages Using Microsoft ASP.NET MVC Architecture

Over at Aireforge®, we outlined some fundamental aims we wanted for the Aireforge Studio software documentation:

 

  • The documents should be hosted on site rather than outsourcing.
  • Documentation should be included within a git repository so we have version control.
  • The content needs to be written within a low code solution, so that technical knowledge isn’t required to easily comprehend and edit the documents.
  • The documents need to be presented in a logical, simple & aesthetic manner so users can navigate the content with ease.

We decided that Markdown was the file format of choice; it ticked all the boxes. This post will outline the techniques I used to achieve this in a .NET MVC web application.

Project Dependencies

There are a few packages for converting Markdown to HTML, but we went with the Markdig NuGet package as it is widely used, provides extensive functionality and is still being updated regularly.

Markdig also provides a Visual Studio Markdown Editor Extension, which has a ton of useful features including drag and drop image coding and a live preview window.

Conversion Code

First I created a global private variable for my Markdig pipeline in my DocsController class:

var _pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().UseBootstrap().Build();

I then created a method within my DocsController class which takes a Markdown filepath string as an input, extracts the document content as a string and then uses the ToHTML method from the static Markdown class provided by the Markdig library to generate the Markdown content as a string of HTML content:

private string RenderHTMLFromMD(string mdFilepath)
{
	string Document = System.IO.File.ReadAllText(AppDomain.CurrentDomain.BaseDirectory + mdFilepath);

	var result = Markdown.ToHtml(Document, _pipeline);

	return result;
}

My RenderHTMLFromMD method is then called by two action methods, the first being an Index action method setup within the DocsController:

public ActionResult Index()
{
	string MarkdownHtml = RenderHTMLFromMD(@"\Content\MarkdownFiles-Doc\Introduction.md");

	ViewBag.MarkdownHTML = MarkdownHtml;
	ViewBag.Title = "Aireforge Docs | Tools for SQL Server";
	ViewBag.MetaDescription = "Find out more about the team behind Aireforge Studio. We have over 30 years’ experience in SQL Server.";

	return View();
}

The Index action method passes the index Markdown filepath into RenderHTMLFromMD, returning the string of HTML content, which I then add to a new ViewBag variable entitled MarkdownHTML.

This now allows us to pass the rendered HTML into a view, via the MarkdownHTML variable.

With this setup, I created a new Index.cshtml file within the Views > Docs subdirectory. This file will only contain the following line:

@Html.Raw(@ViewBag.MarkdownHTML)

This is because most of our HTML body content is generated from the Markdown file, with the rest of the HTML document being provided by the shared layout file _Layout.cshtml.

If you have a compatible Markdown file within the index controller filepath defined, start debugging in VS and you should see the Markdown documented loaded as HTML, embedded within the parent _Layout.cshtml file at localhost:12345/Docs/

Markdown-Example

Markdown file created in Visual Studio

Markdown-HTML-Render

Markdown file rendered as HTML in browser through the localhost environment

Routing Markdown Views to a New Layout.cshtml

We needed to use a different layout file from the rest of our site for the following reasons:

  • The new layout needs to focus on positioning the HTML from our new ViewBag.MarkdownHTML variable.
  • We wanted to simplify the UI for the documentation, which meant stripping out certain elements present in the original layout file.
  • We want to add a sidebar for the documents, which is a feature required across all document pages but not for the rest of the site.

To achieve this:

  • Create a new Layout.cshtml file in the Views > Shared folder. I wanted to keep a lot of the aspects from the original _Layout file, so I made a copy of this and renamed to _DocsLayout.html. If you do this you need to ensure you carefully trawl the new file and removing all scripts, stylesheets and HTML that are no longer required for the documentation pages.
  • Create a new _ViewStart.cshtml page like the one found in the Views directory, and put this in your Views > Docs folder. Change the code in this _ViewStart.cshtml to:
@{
	Layout = "~/Views/Shared/_DocsLayout.cshtml";
}
  • This will route all views within the Docs directory to the _DocsLayout.cshtml layout rather than the universal _Layout.cshtml.

Route All Docs Views To Unified Action Method

So we’ve got the Markdown to HTML process working for one file, but you may have noticed if we add a new controller to render the HTML for every single document this would result in a LOT of code.

Every Markdown file could use the same Index view by just passing the relevant HTML, so I figured we just need a method within the DocsController that:

  • Accepts a Markdown file name
  • Uses the file name to source metadata (Title, MetaDescription) from a JSON file
  • Renders the HTML using the filepath
  • Passes the data to the Index View

To set this up I opened RouteConfig.cs within the App_Start folder and added a new MapRoute extension method call:

routes.MapRoute(
	"DynamicDocs",  // Route name
	"Docs/{element}",  // URL with parameters
	new { controller = "Docs", action = "ProcessCommand" }  // Parameter defaults
);

This tells the web application to route any URL where Docs is the controller and an action is specified to a new action method called ProcessCommand. The action name in the URL is defined as a parameter entitled element here, which we’ll use within this method.

Next go back to the DocController and add the ProcessCommand method:

public ActionResult ProcessCommand(string element)
{
	try
	{
		string MarkdownHtml = RenderHTMLFromMD(@"\Content\MarkdownFiles-Doc\" + element + ".md");
		
		return View("Index");
	}
	catch(Exception ex)
	{
		Logger.Log(ex);
		return RedirectToAction("NotFound", "Shared");
	}
}

From here you can debug to ensure any URL with a controller and action in the format of Docs/documentname hits the ProcessCommand method and the element parameter holds the documentname value. This means if we have a file entitled documentname.md within the directory specified, the HTML is rendered as a string, and passed to the Index view.

In my next blog posts, I’ll be covering:

  • How we can use a JSON config file to generate page metadata and control CSS classes for sidebar highlighting and sub-menu activation.
  • How to add custom CSS styling within your Markdown files.
  • How we can use Markdig markup extensions to add CSS styling to keywords in different programming languages.

Get Started With

Start Now
icon_bluestone98