Skip to content

Instantly share code, notes, and snippets.

@rhwy
Created April 19, 2018 11:26
Show Gist options
  • Save rhwy/febd56f13dcb4b241da0a4d4748beaf7 to your computer and use it in GitHub Desktop.
Save rhwy/febd56f13dcb4b241da0a4d4748beaf7 to your computer and use it in GitHub Desktop.
Nancy 2 RazorLight view engine wrapper for RazorLight templating engine
namespace Nancy.ViewEngines.NancyRazorLight
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Nancy.Responses;
using RazorLight;
using System.Linq;
/// <summary>
/// Simple implementation of a Nancy View engine for RazorLight templating engine
/// To make it work you need 2 simple steps of configuration in your own Nancy Bootstrapper:
///
/// 1. Setup a context that will carry the root folder for views in your container
///
/// add in
/// container.Register&lt;RazorLightViewEngine.IViewContextShares&gt;(
/// new RazorLightViewEngine.ViewContextShares(env.ContentRootPath,"views"));
///
/// (where env is your IHostingEnvironment or anything else that carries the root folder of your app
///
/// 2. set your engine as the default engine for your app by overriding ViewEngines in your Nancy Bootstrapper:
///
/// protected override IEnumerable&lt;Type&gt; ViewEngines
/// =&gt; new[] { typeof(Nancy.ViewEngines.RazorLight.RazorLightViewEngine) };
/// }
/// </summary>
public class RazorLightViewEngine : IViewEngine
{
public interface IViewContextShares
{
string ViewsRootPath { get; }
bool IsDebug { get; }
}
public class ViewContextShares : IViewContextShares
{
public ViewContextShares(string rootPath, string relativeViewPath, bool isDebug)
{
ViewsRootPath = Path.Combine(rootPath, relativeViewPath);
IsDebug = isDebug;
}
public string ViewsRootPath { get; }
public bool IsDebug { get; }
}
private ViewEngineStartupContext viewEngineStartupContext;
private RazorLightEngine engine;
private string viewsRootPath;
private bool isDebug;
public RazorLightViewEngine(IViewContextShares shares)
{
viewsRootPath = shares.ViewsRootPath;
isDebug = true;
engine = new RazorLightEngineBuilder()
.UseMemoryCachingProvider()
.UseFilesystemProject(shares.ViewsRootPath)
.Build();
}
/// <summary>
/// Initialize is called by Nancy bootstrapper at configuration start
/// </summary>
/// <param name="viewEngineStartupContext"></param>
public void Initialize(ViewEngineStartupContext viewEngineStartupContext)
{
this.viewEngineStartupContext = viewEngineStartupContext;
}
/// <summary>
/// Called by nancy when a matching view is found. It reads the content of the found view and passed it
/// to the RazorLight template renderer.
/// Please note that we'll check if we're in dev mode or not (in dev mode we don't used cache)
/// </summary>
/// <param name="viewLocationResult"></param>
/// <param name="model"></param>
/// <param name="renderContext"></param>
/// <returns></returns>
public Response RenderView(ViewLocationResult viewLocationResult, dynamic model, IRenderContext renderContext)
{
var reader = viewLocationResult.Contents;
var template = reader().ReadToEnd();
var response = new HtmlResponse
{
ContentType = "text/html; charset=utf-8",
Contents = s =>
{
var writer = new StreamWriter(s, Encoding.UTF8);
string result = RenderTemplate(template, viewLocationResult.Name, model);
writer.Write(result);
writer.Flush();
}
};
return response;
}
/// <summary>
/// Rendering the template through provided template and model
/// If in debug mode, we recreate new engine each time to avoid caching features and get the changes in
/// templates live.
/// </summary>
/// <param name="template"></param>
/// <param name="name"></param>
/// <param name="data"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
private string RenderTemplate(string template,string name, dynamic data)
{
try
{
if (isDebug)
{
engine = new RazorLightEngineBuilder()
.UseMemoryCachingProvider()
.UseFilesystemProject(viewsRootPath)
.Build();
}
var result = engine.CompileRenderAsync(name, template, (object)data).GetAwaiter().GetResult();
if (result == null) throw new ArgumentNullException(nameof(result));
return result;
}
catch (Exception e)
{
Console.WriteLine(e);
if (isDebug)
{
var lines = template.Split(Environment.NewLine);
var sourceWithLines = string.Join(Environment.NewLine,
lines
.Select((line, i) => $"{i}. {line}")
.Select(line => line.Replace("<","&lt;").Replace(">","&gt;")));
return $@"<h1>Error in view [{name}]</h1>
<p>Message :
<pre><code>{e.Message}</code></pre></p>
<hr>
<p>Stack :
<pre><code style=""background-color:#EEE;font-family:Courier"">{e.StackTrace}</code></pre>
</p>
<hr>
<p>Source :
<pre style=""background-color:#EEE;font-family:Courier""><code>{sourceWithLines}</code></pre>
</p>
";
}
return "";
}
}
public IEnumerable<string> Extensions => new[] {"cshtml"};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment