Skip to content

Instantly share code, notes, and snippets.

@lski
Last active August 29, 2015 14:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lski/cee6863b627dd818ea80 to your computer and use it in GitHub Desktop.
Save lski/cee6863b627dd818ea80 to your computer and use it in GitHub Desktop.
An IHttpActionResult that returns cached views with models requires RazorEngine
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