Last active
August 29, 2015 14:06
-
-
Save lski/cee6863b627dd818ea80 to your computer and use it in GitHub Desktop.
An IHttpActionResult that returns cached views with models requires RazorEngine
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
using System.Linq; | |
using Newtonsoft.Json; | |
using RazorEngine.Configuration; | |
using RazorEngine.Templating; | |
using System.IO; | |
using System.Net; | |
using System.Net.Http; | |
using System.Net.Http.Headers; | |
using System.Text.RegularExpressions; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using System.Web.Http.Results; | |
namespace System.Web.Http.Results { | |
public class HtmlActionResult : IHttpActionResult { | |
private readonly string _template; | |
private readonly dynamic _model; | |
private readonly string _root; | |
public HtmlActionResult(string name, dynamic model, string root = null) { | |
_template = name; | |
_model = model; | |
_root = root ?? "/"; | |
} | |
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) { | |
var dvb = new DynamicViewBag(); | |
dvb.AddValue("_ApplicationRoot", _root); | |
var parsedContent = RazorEngineWrapper.Parse(_template, _model, dvb); | |
var response = new HttpResponseMessage(HttpStatusCode.OK) { | |
Content = new StringContent(parsedContent) | |
}; | |
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html"); | |
return Task.FromResult(response); | |
} | |
} | |
public class Template<T> : TemplateBase<T> { | |
public string ApplicationRoot { | |
get { return ViewBag._ApplicationRoot; } | |
} | |
public string Json(object obj) { | |
return JsonConvert.SerializeObject(obj); | |
} | |
} | |
/// <summary> | |
/// Created as an easy to use wrapper for the razor engine, the caching is based on this article: http://stackoverflow.com/a/21330077/653458 I have cleaned it up slightly though | |
/// </summary> | |
internal class RazorEngineWrapper { | |
static RazorEngineWrapper() { | |
var templateConfig = new TemplateServiceConfiguration { | |
BaseTemplateType = typeof(Template<>), | |
EncodedStringFactory = new RazorEngine.Text.RawStringFactory() | |
}; | |
RazorEngine.Razor.SetTemplateService(new TemplateService(templateConfig)); | |
} | |
public static string Parse(string name, object model, DynamicViewBag bag = null) { | |
if (RazorEngine.Razor.Resolve(name) == null) { | |
RazorEngine.Razor.Compile(LoadContent(name), name); | |
} | |
return (bag == null) ? RazorEngine.Razor.Run(name, model) : RazorEngine.Razor.Run(name, model, bag); | |
} | |
private static string LoadContent(string name) { | |
if (String.IsNullOrEmpty(name)) { | |
throw new ArgumentException("name"); | |
} | |
// If the template name starts with a tilde or forward slash, the template is app relative, so strip it and add the base directory | |
var filename = name.StartsWith("~") || name.StartsWith("/") | |
? Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Regex.Replace(name, @"^(\~\/|\/)", "")) | |
: name; | |
var pathsToTest = new[] { | |
filename + ".cshtml", | |
filename + ".vbhtml", | |
filename + ".html", | |
filename + ".htm" | |
}; | |
foreach (var path in pathsToTest) { | |
if (File.Exists(path)) { | |
return File.ReadAllText(path); | |
} | |
} | |
throw new FileNotFoundException(filename); | |
} | |
} | |
} | |
namespace System.Web.Http { | |
public static class ApiControllerHtmlExt { | |
private const string ViewDirectory = @"/views"; | |
/// <summary> | |
/// Returns a result to the user made up of parsed cshtml, if the view name is not supplied then it will default to finding a view of the same name as the action, in a folder the | |
/// same name as the controller and under the views folder. | |
/// | |
/// If the name is supplied it should be an application root relative one. Do not include '.cshtml' as that is included automatically | |
/// </summary> | |
/// <returns></returns> | |
public static HtmlActionResult Html(this ApiController controller) { | |
var root = GetRoot(controller); | |
var view = GetView(controller); | |
return new HtmlActionResult(view, null, root); | |
} | |
/// <summary> | |
/// Returns a result to the user made up of parsed cshtml, if the view name is not supplied then it will default to finding a view of the same name as the action, in a folder the | |
/// same name as the controller and under the views folder. | |
/// | |
/// If the name is supplied it should be an application root relative one. Do not include '.cshtml' as that is included automatically | |
/// </summary> | |
/// <returns></returns> | |
public static HtmlActionResult Html(this ApiController controller, string view) { | |
var root = GetRoot(controller); | |
return new HtmlActionResult(view, null, root); | |
} | |
/// <summary> | |
/// Returns a result to the user made up of parsed cshtml, if the view name is not supplied then it will default to finding a view of the same name as the action, in a folder the | |
/// same name as the controller and under the views folder. | |
/// | |
/// If the name is supplied it should be an application root relative one. Do not include '.cshtml' as that is included automatically | |
/// </summary> | |
/// <returns></returns> | |
public static HtmlActionResult Html(this ApiController controller, dynamic obj) { | |
var root = GetRoot(controller); | |
var view = GetView(controller); | |
return new HtmlActionResult(view, obj, root); | |
} | |
/// <summary> | |
/// Returns a result to the user made up of parsed cshtml, if the view name is not supplied then it will default to finding a view of the same name as the action, in a folder the | |
/// same name as the controller and under the views folder. | |
/// | |
/// If the name is supplied it should be an application root relative one. Do not include '.cshtml' as that is included automatically | |
/// </summary> | |
/// <returns></returns> | |
public static HtmlActionResult Html(this ApiController controller, string view, dynamic obj) { | |
var root = GetRoot(controller); | |
return new HtmlActionResult(view, obj, root); | |
} | |
private static string GetRoot(ApiController controller) { | |
// Gets the root by getting the absoluteUri of the application and removing the root | |
return controller.Url.Content("~/").Substring(controller.Request.RequestUri.GetLeftPart(UriPartial.Authority).Length); | |
} | |
private static string GetView(ApiController controller) { | |
return Path.Combine(ViewDirectory, controller.ControllerContext.ControllerDescriptor.ControllerName, controller.ActionContext.ActionDescriptor.ActionName); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment