Created
April 19, 2018 11:26
-
-
Save rhwy/febd56f13dcb4b241da0a4d4748beaf7 to your computer and use it in GitHub Desktop.
Nancy 2 RazorLight view engine wrapper for RazorLight templating engine
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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<RazorLightViewEngine.IViewContextShares>( | |
/// 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<Type> ViewEngines | |
/// => 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("<","<").Replace(">",">"))); | |
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