Markdown Razor

Demis Bellot edited this page Oct 25, 2016 · 12 revisions

This page has moved to

Markdown Razor View Engine

Markdown Razor is the first HTML and Text (i.e. Markdown) view engine built into ServiceStack. The pages are simply plain-text Markdown surrounded by MVC Razor-like syntax to provide its enhanced dynamic functionality.

Extensible with custom base classes and Helpers

Markdown Razor is extensible in much the same way as MVC Razor is with the ability to define and use your own custom base class, Helpers and HtmlHelper extension methods. This allows you to call util methods on your base class or helpers directly from your templates.

You can define a base class for all your markdown pages by implementing MarkdownViewBase and register it in your AppHost with:

SetConfig(new HostConfig {
    //Replace prefix with the Url supplied
    WebHostUrl = "",   
    //Set base class for all Markdown pages
    MarkdownBaseType = typeof(CustomMarkdownPage), 
    //Define global Helpers e.g. at Ext.
    MarkdownGlobalHelpers = new Dictionary<string, Type> { 
        {"Ext", typeof(CustomStaticHelpers)}

If a WebHostUrl is specified, it replaces all ~/ in all static website and Markdown pages with it. The MarkdownGlobalHelpers allow you to define global helper methods available to all your pages. This has the same effect of declaring it in your base class e.g:

public class CustomMarkdownPage : MarkdownViewBase {
    public CustomStaticHelpers Ext = new CustomStaticHelpers();

Which you can access in your pages via @Ext.MyHelper(Model). Declaring instance methods on your custom base class allows you to access them without any prefix.

MarkdownViewBase base class

By default the MarkdownViewBase class provides the following properties and hooks:

public class MarkdownViewBase 
    //Access Config, resolve dependencies, etc.
    public IAppHost AppHost; 
    //This precompiled Markdown page with Metadata
    public MarkdownPage MarkdownPage; 
    //ASP.NET MVC's HtmlHelper
    public HtmlHelper Html; 
    //Flag to on whether you should you generate HTML or Markdown
    public bool RenderHtml; 

      All variables passed to and created by your page. 
      The Response DTO is stored and accessible via the 'Model' variable.

      All variables and outputs created are stored in ScopeArgs which 
      is what's available to your website template. The Generated page 
      is stored in the 'Body' variable.
    public Dictionary<string,object> ScopeArgs;

    //Called before page is executed
    public virtual void InitHelpers(){} 

    //Called after page is executed before it's merged with website template if any
    public virtual void OnLoad(){}      

See this websites CustomMarkdownPage.cs base class for an example on how to effectively use the base class to Resolve dependencies, inspect generated variables, generate PagesMenu and other dynamic variables for output in the static website template.

Compared with ASP.NET MVC Razor Syntax

For the best way to illustrate the similarities with ASP.NET MVC Razor syntax I will show examples of the Razor examples in ScottGu's introductory Introducing "Razor" - a new view engine for ASP.NET

Note: more context and the output for each snippet and example displayed is contained in the Introductory Example and Introductory Layout Unit tests. For reference most features of Mardown Razor view engine are captured in the Core Template Unit Tests

Hello World Sample with Razor

The following basic page:

Hello World Output

Can be generated in MVC Razor with:

Hello World MVC Razor

And Markdown Razor with:

# Razor Example

###  Hello @name, the year is @DateTime.Now.Year

Checkout [this product](/Product/Details/@productId)

Loops and Nested HTML Sample

The simple loop example:

Simple Loop Output

With MVC Razor:

Simple Loop MVC Razor

With Markdown Razor:

@foreach (var p in products) {
  - @p.Name: (@p.Price)


At this point I think it would be a good to introduce some niceties in Markdown Razor of its own. Borrowing a page out of BrendanEich proposal for CoffeeScript's inspired Parens free syntax for JS.Next - you can simply remove the parens from all block statements e.g:

@foreach var p in products {
  - @p.Name: (@p.Price)

Produces the same output, and to go one step further you can remove the redundant var as well :)

@foreach p in products {
  - @p.Name: (@p.Price)

Which makes the Markdown Razor's version a bit more wrist-friendly then its MVCs cousin :)

If-Blocks and Multi-line Statements

If statements in MVC Razor:

If Statements

If statements in Markdown Razor:

@if (products.Count == 0) {
Sorry - no products in this category
} else {
We have products for you!

Multi-line and Multi-token statements

MVC Razor Multi-line statements

Markdown Razor doesn't support multi-line or multi-token statements, instead you are directed to take advantage for variable syntax declarations, e.g:

Markdown replacement for Multi-line Statements

@var number = 1
@var message = ""Number is "" + number

Your Message: @message

Integrating Content and Code

Does it break with email addresses and other usages of in HTML?

With MVC Razor

MVC Razor Content and Code

With Markdown Razor

Send mail to telling him the time: @DateTime.Now.

Both View engines generate the expected output, e.g:

MVC Razor Content and Code Output

Identifying Nested Content

With MVC Razor

MVC Razor Identifying Nested Content

With Markdown Razor

@if (DateTime.Now.Year == 2011) {
If the year is 2011 then print this 
multi-line text block and 
the date: @DateTime.Now

Markdown Razor doesn't need to do anything special with text blocks since all it does is look for the ending brace '}'. This means if you want to output a brace literal '{' then you have to double escape it with '{{' or '}}'.

HTML Encoding

Markdown Razor follows MVC Markdown behaviour where by default content emitted using a @ block is automatically HTML encoded to better protect against XSS attack scenarios.

If you want to avoid HTML Encoding you have the same options as MVC Razor where you can wrap your result in @Html.Raw(htmlString) or if you're using an Extension method simply return a MvcHtmlString instead of a normal string.

Markdown also lets you mix and match HTML in your markdown although any markdown between the tags does not get converted to HTML. To tell Markdown Razor to evaulate the contents inside html <tag>...</tag>'s need to prefixed with ^, e.g. (taken from the /Views/ page):

^<div id="searchresults">

@foreach page in Model.Results {
### @page.Category > [@page.Name](@page.AbsoluteUrl)


If we didn't prefix ^ we would see ### @page.Category ... repeating.

Layout/MasterPage Scenarios - The Basics

Markdown Razor actually deviates a bit from MVC Razor's handling of master layout pages and website templates (we believe for the better :).

Simple Layout Example

MVC Razor's example of a simple website template

MVC Razor simple website template

Rather then using a magic method like @RenderBody() we treat the output Body of View as just another variable storing the output a in a variable called 'Body'. This way we use the same mechanism to embed the body like any other variable i.e. following the place holder convention of <--@VarName--> so to embed the View page output in the above master template you would do:

<!DOCTYPE html>
        <title>Simple Site</title>

        <div id=""header"">
            <a href=""/"">Home</a>
            <a href=""/About"">About</a>
        <div id=""body"">


By default we use convention to select the appropriate website template for the selected view where it uses the nearest default.shtml static template it finds, looking first in the current directory than up parent directories.

Your View page names must be unique but can live anywhere in your /View directory so you are free to structure your website templates and view pages accordingly. If for whatever reason you need more granularity in selecting website templates than we provide similar options to MVC for selecting a custom template:

Select Custom Template with MVC Razor

MVC Razor Custom Layout Page

With Markdown Razor

@Layout ~/websiteTemplate

# About this Site

This is some content that will make up the ""about"" 
page of our web-site. We'll use this in conjunction
with a layout template. The content you are seeing here
comes from ^^^websiteTemplate.

And obviously I can have code in here too. Here is the
current date/year: @DateTime.Now.Year

Note: In addition to @Layout we also support the more appropriate alias of @template.

Layout/MasterPage Scenarios - Adding Section Overrides

MVC Razor allows you to define sections in your view pages which you can embed in your Master Template:

With MVC Razor:

MVC Razor Sections in Views

And you use in your website template like so:

MVC Razor use Sections

With Markdown Razor:

Markdown Razor supports the same @section construct but allows you to embed it in your template via the standard variable substitution convention, e.g:

@Layout ~/websiteTemplate

# About this Site

This is some content that will make up the ""about"" 
page of our web-site. We'll use this in conjunction
with a layout template. The content you are seeing here
comes from ^^^websiteTemplate.

And obviously I can have code in here too. Here is the
current date/year: @DateTime.Now.Year

@section Menu {
  - About Item 1
  - About Item 2

@section Footer {
This is my custom footer for Home

And these sections and body can be used in the website template like:

<!DOCTYPE html>
        <title>Simple Site</title>

        <div id="header">
            <a href="/">Home</a>
            <a href="/About">About</a>
        <div id="left-menu">
        <div id="body">
        <div id="footer">


Encapsulation and Re-Use with HTML Helpers

In order to encapsulate and better be able to re-use HTML Helper utils MVC Razor includes a few different ways to componentize and re-use code with HTMLHelper extension methods and declarative helpers.

Code Based HTML Helpers

HtmlHelper extension methods with MVC Razor:

MVC Razor HtmlHelper extension methods

Since we've ported MVC's HtmlHelper and its Label, TextBox extensions we can do something similar although to make this work we need to inherit from the MarkdownViewBase<TModel> generic base class so we know what Model to provide the strong-typed extensions for. You can do this using the @model directive specifying the full type name:

@model ServiceStack.ServiceHost.Tests.Formats.Product
    <legend>Edit Product</legend>

        @Html.LabelFor(m => m.ProductID)
        @Html.TextBoxFor(m => m.ProductID)

Whilst we ported most of MVC HtmlHelper extension methods as-is, we did rip out all the validation logic which appeared to be unnecessarily complex and too coupled with MVC's code-base.

Note: Just as it is in MVC the @model directive is a shorthand (which Markdown Razor also supports) for:

@inherits Namespace.BaseType<Namespace.ModelType>

Whilst we don't support MVC Razors quasi C# quasi-html approach of defining declarative helpers, we do allow you to on a per instance basis (or globally) import helpers in custom Fields using the @helper syntax:

@helper Prod: MyHelpers.ExternalProductHelper

    <legend>All Products</legend>

You can register Global helpers and a custom base class using the MarkdownGlobalHelpers and MarkdownBaseType AppHost Config options as shown at the top of this article.


Well that's it for the comparison between MVC Razor and Markdown Razor as you can see the knowledge is quite transferable with a lot of cases the syntax is exactly the same.

As good as MVC Razor is with its wrist-friendly and expressive syntax, we believe Razor Markdown is even better! Where thanks to Markdown you can even dispense with most of HTML's boiler plage angle brackets :) We think it makes an ideal solution for content heavy websites like this one.

Unlike ASP.NET's MVC Razor, Markdown Razor like all of ServiceStack is completely Open Source and as such we welcome the contribution from the community via new features, Unit and regression tests, etc.

Community Resources

  1. Getting Started

    1. Creating your first project
    2. Create Service from scratch
    3. Your first webservice explained
    4. Example Projects Overview
    5. Learning Resources
  2. Designing APIs

    1. ServiceStack API Design
    2. Designing a REST-ful service with ServiceStack
    3. Simple Customer REST Example
    4. How to design a Message-Based API
    5. Software complexity and role of DTOs
  3. Reference

    1. Order of Operations
    2. The IoC container
    3. Configuration and AppSettings
    4. Metadata page
    5. Rest, SOAP & default endpoints
    6. SOAP support
    7. Routing
    8. Service return types
    9. Customize HTTP Responses
    10. Customize JSON Responses
    11. Plugins
    12. Validation
    13. Error Handling
    14. Security
    15. Debugging
    16. JavaScript Client Library (ss-utils.js)
  4. Clients

    1. Overview
    2. C#/.NET client
      1. .NET Core Clients
    3. Add ServiceStack Reference
      1. C# Add Reference
      2. F# Add Reference
      3. VB.NET Add Reference
      4. Swift Add Reference
      5. Java Add Reference
    4. Silverlight client
    5. JavaScript client
      1. Add TypeScript Reference
    6. Dart Client
    7. MQ Clients
  5. Formats

    1. Overview
    2. JSON/JSV and XML
    3. HTML5 Report Format
    4. CSV Format
    5. MessagePack Format
    6. ProtoBuf Format
  6. View Engines 4. Razor & Markdown Razor

    1. Markdown Razor
  7. Hosts

    1. IIS
    2. Self-hosting
    3. Messaging
    4. Mono
  8. Security

    1. Authentication
    2. Sessions
    3. Restricting Services
    4. Encrypted Messaging
  9. Advanced

    1. Configuration options
    2. Access HTTP specific features in services
    3. Logging
    4. Serialization/deserialization
    5. Request/response filters
    6. Filter attributes
    7. Concurrency Model
    8. Built-in profiling
    9. Form Hijacking Prevention
    10. Auto-Mapping
    11. HTTP Utils
    12. Dump Utils
    13. Virtual File System
    14. Config API
    15. Physical Project Structure
    16. Modularizing Services
    17. MVC Integration
    18. ServiceStack Integration
    19. Embedded Native Desktop Apps
    20. Auto Batched Requests
    21. Versioning
    22. Multitenancy
  10. Caching

  11. Caching Providers

  12. HTTP Caching

  13. CacheResponse Attribute

  14. Cache Aware Clients

  15. Auto Query

  16. Overview

  17. Why Not OData

  18. AutoQuery RDBMS

  19. AutoQuery Data

  20. AutoQuery Memory

  21. AutoQuery Service

  22. AutoQuery DynamoDB

  23. Server Events

    1. Overview
    2. JavaScript Client
    3. C# Server Events Client
    4. Redis Server Events
  24. Service Gateway

    1. Overview
    2. Service Discovery
  25. Encrypted Messaging

    1. Overview
    2. Encrypted Client
  26. Plugins

    1. Auto Query
    2. Server Sent Events
    3. Swagger API
    4. Postman
    5. Request logger
    6. Sitemaps
    7. Cancellable Requests
    8. CorsFeature
  27. Tests

    1. Testing
    2. HowTo write unit/integration tests
  28. ServiceStackVS

    1. Install ServiceStackVS
    2. Add ServiceStack Reference
    3. TypeScript React Template
    4. React, Redux Chat App
    5. AngularJS App Template
    6. React Desktop Apps
  29. Other Languages

    1. FSharp
      1. Add ServiceStack Reference
    2. VB.NET
      1. Add ServiceStack Reference
    3. Swift
    4. Swift Add Reference
    5. Java
      1. Add ServiceStack Reference
      2. Android Studio & IntelliJ
      3. Eclipse
  30. Amazon Web Services

  31. ServiceStack.Aws

  32. PocoDynamo

  33. AWS Live Demos

  34. Getting Started with AWS

  35. Deployment

    1. Deploy Multiple Sites to single AWS Instance
      1. Simple Deployments to AWS with WebDeploy
    2. Advanced Deployments with OctopusDeploy
  36. Install 3rd Party Products

    1. Redis on Windows
    2. RabbitMQ on Windows
  37. Use Cases

    1. Single Page Apps
    2. HTML, CSS and JS Minifiers
    3. Azure
    4. Connecting to Azure Redis via SSL
    5. Logging
    6. Bundling and Minification
    7. NHibernate
  38. Performance

    1. Real world performance
  39. Other Products

    1. ServiceStack.Redis
    2. ServiceStack.OrmLite
    3. ServiceStack.Text
  40. Future

    1. Roadmap
Clone this wiki locally
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.