OWIN and Razor

Just for fun added a simple support for using Razor together with OWIN. You can read a little bit about OWIN on my previous blog post.

I wanted my example to be similar to the MVC pattern, so I can use a Model, Controller (in this case I use a simple method) and a View. My Model is simple:

    public class Customer
{

        public int Id { get; set; }

        public string Name { get; set; }
}

 

The "Controller" I use will only return the Model, but I can also return a IView and specify which view to be used to render my model. By default convention is used, so the name of the returned Model will be the name of the View to use. Here is my "Controller":

 

    public Customer ListCustomer()
{

       return new Customer() { Id = 1, Name = "John Doe" };
}

 

It just return a new Customer with the name "John Doe".

To configure the use of Razor with OWIN and to setup "routes", I created a RazorConfig class. By using its Get property, I can map a "route" to a "Controller". Here is the RezorConfig class:

 

     public class RazorConfig
{
private readonly Dictionary<string, Func<object>> _get = new Dictionary<string, Func<object>>();

        private IViewLocator _viewLocator;
private IViewParser _viewParser;

        public IDictionary<string, Func<object>> Get
{
get { return _get; }
}

        public IViewLocator ViewLocator
{
get
{
if (_viewLocator != null)
return _viewLocator;

                return new RazorViewLocator();
}
set
{
_viewLocator = value;
}
}

public IViewParser ViewParser
{
get
{
if (_viewParser != null)
return _viewParser;

                return new RazorViewParser();
}
set
{
_viewParser = value;
}
}
}

 

 

The RazorConfig can also be used to specify a custom view locator and a custom view parser. The RazorViewLoctor I have created will look for views in a Views folder. The RazorViewParser will use RazorEngine.

Here is the Startup.cs where I configure the "routes" with the RazorConfiguration:

 

   public class Startup
{
public void Configuration(IAppBuilder app)
{
var razorConfig = new RazorConfig();

            razorConfig.Get["/customer"] = ListCustomer;
razorConfig.Get["/user"] = ListUser;

            app.UseRazor(razorConfig);
}
}

 

The Get property of the RazorConfig will be sued to configure the "controllers" to be used for HTTP Get of a specific "route".

If I do a HTTP GET, e.g. GET http://localhost/customer", the ListCustomer will be invoked. The ListCusomer returns a Customer, so a view with the name Customer.cshtml will be used to render the returned Customer model.

I have created an extension method for the IAppBuilder, UseRazor. UseRazor takes the RazorConfig as a parameter:

 

    public static class RazorExtensions
{
public static void UseRazor(this IAppBuilder builder, RazorConfig razorConfig)
{
if (builder == null)
throw new ArgumentNullException("builder");

            builder.Use(new Func<object, object>(ignored => new Razor(razorConfig)));
}
}


The extension will use the class Razor. The class Razor will handle the HTTP request and make sure the correct view for the request will be written to the HTTP stream:

 

    public class Razor
{
private RazorConfig _config;

        public Razor(RazorConfig config)
{
_config = config;
}

public Task Invoke(IDictionary<string, object> env)
{
switch (((string)env["owin.RequestMethod"]).ToUpper())
{
case "GET":
HttpGetHandler(env);
break;
}

            return Task.FromResult<object>(null);
}

        private void HttpGetHandler(IDictionary<string, object> env)
{
var responseContent = _config.Get[(string)env["owin.RequestPath"]].Invoke();

            var responseHeader = (IDictionary<string, string[]>)env["owin.ResponseHeaders"];
responseHeader.Add("Content-Type", new[] { "text/html" });

            using (var writer = new StreamWriter((Stream)env["owin.ResponseBody"]))
{
var parsedView = ParseView(responseContent.GetType(), responseContent);
writer.Write(parsedView);
}
}

        private string ParseView(Type type, object model)
{
var viewName = GetViewName(model != null ? model.GetType() : type);
var view = model as IView ?? new View(viewName, model, type);

            var viewTemplate = _config.ViewLocator.GetView(null, view);

            return _config.ViewParser.ParseView(view, viewTemplate);
}

        private string GetViewName(Type modelType)
{
var viewAttributes = (ViewAttribute[])modelType.GetCustomAttributes(typeof(ViewAttribute), true);
if (viewAttributes.Length > 0)
return viewAttributes[0].ViewName;

return modelType.Name;
}
}

 

The Razor class can get the name of the view to be used to render a model by convention, or attribute (ViewAttribute on the model).

Here is the Customer.cshtml view I used to see that I managed to get Razor to work:

 

    <!DOCTYPE html>
<html>
<head></head>
<body>
<div id="body">

            <section>                
<div>

                  <hgroup>
<h1>Welcome '@Model.Name' to OWIN Razor Test!</h1>
</hgroup>
</div>

           </section>

         </div>
</body>
</html>

 

The code in this blog post is far from perfect, it just a simple code to render a Razor view for a model. You may notice that I fail to follow the law of delimiters when I Get- and Parse a View ;)

If you are interested in the RazorViewLocator and RazorViewParser code, you can take a look at the code added to the ASP.Net Web API Contrib.

If you want to know when I publish a new blog post, feel free to follow me on twitter: @fredrikn

No Comments