From b918cb8170f127fa92876127fc00faf322a30fd1 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Wed, 15 Jan 2014 18:02:59 -0800 Subject: [PATCH] Adding view engines --- Microsoft.AspNet.Mvc.Razor/HtmlString.cs | 18 +++ .../IVirtualPathFactory.cs | 10 ++ .../MetadataVirtualPathProvider.cs | 49 +++++++++ .../Microsoft.AspNet.Mvc.Razor.csproj | 79 +++++++++++++ .../Properties/AssemblyInfo.cs | 36 ++++++ Microsoft.AspNet.Mvc.Razor/RazorView.cs | 104 ++++++++++++++++++ Microsoft.AspNet.Mvc.Razor/RazorViewEngine.cs | 59 ++++++++++ .../VirtualPathAttribute.cs | 15 +++ Microsoft.AspNet.Mvc.Razor/packages.config | 5 + Microsoft.AspNet.Mvc.sln | 6 + Microsoft.AspNet.Mvc/ActionResultHelper.cs | 16 ++- .../Microsoft.AspNet.Mvc.csproj | 6 + Microsoft.AspNet.Mvc/MvcHandler.cs | 5 + Microsoft.AspNet.Mvc/MvcServices.cs | 3 +- .../View/CompositeViewEngine.cs | 56 ++++++++++ Microsoft.AspNet.Mvc/View/IView.cs | 10 ++ Microsoft.AspNet.Mvc/View/IViewEngine.cs | 9 ++ Microsoft.AspNet.Mvc/View/ViewContext.cs | 19 ++++ Microsoft.AspNet.Mvc/View/ViewEngineResult.cs | 47 ++++++++ Microsoft.AspNet.Mvc/View/ViewResult.cs | 61 ++++++++++ MvcSample/HomeController.cs | 5 + MvcSample/MvcSample.csproj | 10 ++ MvcSample/Startup.cs | 10 +- MvcSample/ViewMetadata.cs | 23 ++++ MvcSample/Views/Layout.cs | 20 ++++ MvcSample/Views/MyView.cs | 17 +++ 26 files changed, 694 insertions(+), 4 deletions(-) create mode 100644 Microsoft.AspNet.Mvc.Razor/HtmlString.cs create mode 100644 Microsoft.AspNet.Mvc.Razor/IVirtualPathFactory.cs create mode 100644 Microsoft.AspNet.Mvc.Razor/MetadataVirtualPathProvider.cs create mode 100644 Microsoft.AspNet.Mvc.Razor/Microsoft.AspNet.Mvc.Razor.csproj create mode 100644 Microsoft.AspNet.Mvc.Razor/Properties/AssemblyInfo.cs create mode 100644 Microsoft.AspNet.Mvc.Razor/RazorView.cs create mode 100644 Microsoft.AspNet.Mvc.Razor/RazorViewEngine.cs create mode 100644 Microsoft.AspNet.Mvc.Razor/VirtualPathAttribute.cs create mode 100644 Microsoft.AspNet.Mvc.Razor/packages.config create mode 100644 Microsoft.AspNet.Mvc/View/CompositeViewEngine.cs create mode 100644 Microsoft.AspNet.Mvc/View/IView.cs create mode 100644 Microsoft.AspNet.Mvc/View/IViewEngine.cs create mode 100644 Microsoft.AspNet.Mvc/View/ViewContext.cs create mode 100644 Microsoft.AspNet.Mvc/View/ViewEngineResult.cs create mode 100644 Microsoft.AspNet.Mvc/View/ViewResult.cs create mode 100644 MvcSample/ViewMetadata.cs create mode 100644 MvcSample/Views/Layout.cs create mode 100644 MvcSample/Views/MyView.cs diff --git a/Microsoft.AspNet.Mvc.Razor/HtmlString.cs b/Microsoft.AspNet.Mvc.Razor/HtmlString.cs new file mode 100644 index 0000000000..33ab128651 --- /dev/null +++ b/Microsoft.AspNet.Mvc.Razor/HtmlString.cs @@ -0,0 +1,18 @@ + +namespace Microsoft.AspNet.Mvc.Razor +{ + public class HtmlString + { + private readonly string _input; + + public HtmlString(string input) + { + _input = input; + } + + public override string ToString() + { + return _input; + } + } +} diff --git a/Microsoft.AspNet.Mvc.Razor/IVirtualPathFactory.cs b/Microsoft.AspNet.Mvc.Razor/IVirtualPathFactory.cs new file mode 100644 index 0000000000..7beb50f1d0 --- /dev/null +++ b/Microsoft.AspNet.Mvc.Razor/IVirtualPathFactory.cs @@ -0,0 +1,10 @@ + +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Mvc.Razor +{ + public interface IVirtualPathFactory + { + Task CreateInstance(string virtualPath); + } +} diff --git a/Microsoft.AspNet.Mvc.Razor/MetadataVirtualPathProvider.cs b/Microsoft.AspNet.Mvc.Razor/MetadataVirtualPathProvider.cs new file mode 100644 index 0000000000..efd1eb13c0 --- /dev/null +++ b/Microsoft.AspNet.Mvc.Razor/MetadataVirtualPathProvider.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Mvc.Razor +{ + public class MetadataVirtualPathProvider : IVirtualPathFactory + { + private readonly Dictionary _viewMetadata; + + public MetadataVirtualPathProvider(Assembly assembly) + { + var metadataType = assembly.GetType("ViewMetadata"); + if (metadataType != null) + { + object metadata = metadataType.GetProperty("Metadata", BindingFlags.Static | BindingFlags.Public) + .GetValue(obj: null); + + _viewMetadata = new Dictionary((Dictionary)metadata, StringComparer.OrdinalIgnoreCase); + } + else + { + // Code to support precompiled views generated via RazorGenerator + _viewMetadata = assembly.GetExportedTypes() + .Where(type => typeof(RazorView).IsAssignableFrom(type)) + .ToDictionary(type => GetVirtualPath(type), StringComparer.OrdinalIgnoreCase); + } + } + + public Task CreateInstance(string virtualPath) + { + Type type; + object view = null; + if (_viewMetadata.TryGetValue(virtualPath, out type)) + { + view = (RazorView)Activator.CreateInstance(type); + } + return Task.FromResult(view); + } + + private static string GetVirtualPath(Type type) + { + VirtualPathAttribute attribute = type.GetCustomAttribute(); + return attribute.VirtualPath; + } + } +} diff --git a/Microsoft.AspNet.Mvc.Razor/Microsoft.AspNet.Mvc.Razor.csproj b/Microsoft.AspNet.Mvc.Razor/Microsoft.AspNet.Mvc.Razor.csproj new file mode 100644 index 0000000000..62f6d35082 --- /dev/null +++ b/Microsoft.AspNet.Mvc.Razor/Microsoft.AspNet.Mvc.Razor.csproj @@ -0,0 +1,79 @@ + + + + + Debug + AnyCPU + {224A14D0-ECA7-441C-AE89-B6E66A57EF9B} + Library + Properties + Microsoft.AspNet.Mvc.Razor + Microsoft.AspNet.Mvc.Razor + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\packages\Microsoft.Owin.2.0.2\lib\net45\Microsoft.Owin.dll + + + False + ..\packages\Owin.1.0\lib\net40\Owin.dll + + + + + + + + + + + + + + + + + + + + + {ec38534c-a2d1-413f-97d1-55eef5d2fb71} + Microsoft.AspNet.CoreServices + + + {2a0c26f1-0240-4ae1-ae00-4691c291b122} + Microsoft.AspNet.Mvc + + + + + + + + \ No newline at end of file diff --git a/Microsoft.AspNet.Mvc.Razor/Properties/AssemblyInfo.cs b/Microsoft.AspNet.Mvc.Razor/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..1cf665ed45 --- /dev/null +++ b/Microsoft.AspNet.Mvc.Razor/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Microsoft.AspNet.Mvc.Razor")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Microsoft.AspNet.Mvc.Razor")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("af993404-738b-4c26-94af-38bec7c4e487")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Microsoft.AspNet.Mvc.Razor/RazorView.cs b/Microsoft.AspNet.Mvc.Razor/RazorView.cs new file mode 100644 index 0000000000..d326aff664 --- /dev/null +++ b/Microsoft.AspNet.Mvc.Razor/RazorView.cs @@ -0,0 +1,104 @@ +using System; +using System.Globalization; +using System.IO; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNet.CoreServices; +using Microsoft.Owin; + +namespace Microsoft.AspNet.Mvc.Razor +{ + public abstract class RazorView : IView + { + public IOwinContext Context { get; set; } + + public object Model { get; set; } + + public string Layout { get; set; } + + protected TextWriter Output { get; set; } + + private string BodyContent { get; set; } + + public async Task RenderAsync(ViewContext context, TextWriter writer) + { + Model = context.Model; + + var contentBuilder = new StringBuilder(1024); + using (var bodyWriter = new StringWriter(contentBuilder)) + { + Output = bodyWriter; + Execute(); + } + + string bodyContent = contentBuilder.ToString(); + if (!String.IsNullOrEmpty(Layout)) + { + await RenderLayoutAsync(context, writer, bodyContent); + } + else + { + await writer.WriteAsync(bodyContent); + } + } + + private async Task RenderLayoutAsync(ViewContext context, TextWriter writer, string bodyContent) + { + var virtualPathFactory = context.ServiceProvider.GetService(); + RazorView razorView = (RazorView)(await virtualPathFactory.CreateInstance(Layout)); + if (razorView == null) + { + string message = String.Format(CultureInfo.CurrentCulture, "The layout view '{0}' could not be located.", Layout); + throw new InvalidOperationException(message); + } + + razorView.BodyContent = bodyContent; + await razorView.RenderAsync(context, writer); + } + + protected abstract void Execute(); + + public virtual void Write(object value) + { + WriteTo(Output, value); + } + + public virtual void WriteTo(TextWriter writer, object content) + { + if (content != null) + { + var htmlString = content as HtmlString; + if (htmlString != null) + { + writer.Write(content.ToString()); + } + else + { + WebUtility.HtmlEncode(content.ToString(), writer); + } + } + } + + public void WriteLiteral(object value) + { + WriteLiteralTo(Output, value); + } + + public virtual void WriteLiteralTo(TextWriter writer, object text) + { + if (text != null) + { + writer.Write(text.ToString()); + } + } + + protected virtual void RenderBody() + { + if (BodyContent != null) + { + WriteLiteral(BodyContent); + } + } + } +} diff --git a/Microsoft.AspNet.Mvc.Razor/RazorViewEngine.cs b/Microsoft.AspNet.Mvc.Razor/RazorViewEngine.cs new file mode 100644 index 0000000000..8b442c12d7 --- /dev/null +++ b/Microsoft.AspNet.Mvc.Razor/RazorViewEngine.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Mvc.Razor +{ + public class RazorViewEngine : IViewEngine + { + private static readonly string[] _viewLocationFormats = new[] + { + "~/Views/{1}/{0}.cshtml", + "~/Views/Shared/{0}.cshtml", + }; + private readonly IActionDescriptorProvider _actionDescriptorProvider; + private readonly IVirtualPathFactory _virtualPathFactory; + + public RazorViewEngine(IActionDescriptorProvider actionDescriptorProvider, + IVirtualPathFactory virtualPathFactory) + { + _actionDescriptorProvider = actionDescriptorProvider; + _virtualPathFactory = virtualPathFactory; + } + + public IEnumerable ViewLocationFormats + { + get { return _viewLocationFormats; } + } + + public async Task FindView(RequestContext requestContext, string viewName) + { + var actionDescriptor = _actionDescriptorProvider.CreateDescriptor(requestContext) as ControllerBasedActionDescriptor; + + if (actionDescriptor == null) + { + return null; + } + + if (String.IsNullOrEmpty(viewName)) + { + viewName = actionDescriptor.ActionName; + } + + string controllerName = actionDescriptor.ControllerName; + var searchedLocations = new List(_viewLocationFormats.Length); + for (int i = 0; i < _viewLocationFormats.Length; i++) + { + string path = String.Format(CultureInfo.InvariantCulture, _viewLocationFormats[i], viewName, controllerName); + RazorView view = (RazorView)(await _virtualPathFactory.CreateInstance(path)); + if (view != null) + { + return ViewEngineResult.Found(view); + } + searchedLocations.Add(path); + } + return ViewEngineResult.NotFound(searchedLocations); + } + } +} diff --git a/Microsoft.AspNet.Mvc.Razor/VirtualPathAttribute.cs b/Microsoft.AspNet.Mvc.Razor/VirtualPathAttribute.cs new file mode 100644 index 0000000000..67109c49ae --- /dev/null +++ b/Microsoft.AspNet.Mvc.Razor/VirtualPathAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace Microsoft.AspNet.Mvc.Razor +{ + [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] + public class VirtualPathAttribute : Attribute + { + public VirtualPathAttribute(string virtualPath) + { + VirtualPath = virtualPath; + } + + public string VirtualPath { get; private set; } + } +} diff --git a/Microsoft.AspNet.Mvc.Razor/packages.config b/Microsoft.AspNet.Mvc.Razor/packages.config new file mode 100644 index 0000000000..57b34eaa5d --- /dev/null +++ b/Microsoft.AspNet.Mvc.Razor/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Microsoft.AspNet.Mvc.sln b/Microsoft.AspNet.Mvc.sln index abccee7ece..8c5c49787e 100644 --- a/Microsoft.AspNet.Mvc.sln +++ b/Microsoft.AspNet.Mvc.sln @@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MvcSample", "MvcSample\MvcS EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.CoreServices", "Microsoft.AspNet.CoreServices\Microsoft.AspNet.CoreServices.csproj", "{EC38534C-A2D1-413F-97D1-55EEF5D2FB71}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.Mvc.Razor", "Microsoft.AspNet.Mvc.Razor\Microsoft.AspNet.Mvc.Razor.csproj", "{224A14D0-ECA7-441C-AE89-B6E66A57EF9B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +29,10 @@ Global {EC38534C-A2D1-413F-97D1-55EEF5D2FB71}.Debug|Any CPU.Build.0 = Debug|Any CPU {EC38534C-A2D1-413F-97D1-55EEF5D2FB71}.Release|Any CPU.ActiveCfg = Release|Any CPU {EC38534C-A2D1-413F-97D1-55EEF5D2FB71}.Release|Any CPU.Build.0 = Release|Any CPU + {224A14D0-ECA7-441C-AE89-B6E66A57EF9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {224A14D0-ECA7-441C-AE89-B6E66A57EF9B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {224A14D0-ECA7-441C-AE89-B6E66A57EF9B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {224A14D0-ECA7-441C-AE89-B6E66A57EF9B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Microsoft.AspNet.Mvc/ActionResultHelper.cs b/Microsoft.AspNet.Mvc/ActionResultHelper.cs index 677259cc7e..f8bf55f670 100644 --- a/Microsoft.AspNet.Mvc/ActionResultHelper.cs +++ b/Microsoft.AspNet.Mvc/ActionResultHelper.cs @@ -1,9 +1,19 @@ using System; +using Microsoft.AspNet.CoreServices; namespace Microsoft.AspNet.Mvc { public class ActionResultHelper : IActionResultHelper { + private readonly IServiceProvider _serviceProvider; + private readonly IViewEngine _viewEngine; + + public ActionResultHelper(IServiceProvider serviceProvider, IViewEngine viewEngine) + { + _serviceProvider = serviceProvider; + _viewEngine = viewEngine; + } + public IActionResult Content(string value) { return new ContentResult @@ -28,7 +38,11 @@ namespace Microsoft.AspNet.Mvc public IActionResult View() { - throw new NotImplementedException(); + return new ViewResult(_serviceProvider, _viewEngine) + { + ViewName = null, + Model = null + }; } } } diff --git a/Microsoft.AspNet.Mvc/Microsoft.AspNet.Mvc.csproj b/Microsoft.AspNet.Mvc/Microsoft.AspNet.Mvc.csproj index 2ea0f9defd..5b5e2cafc2 100644 --- a/Microsoft.AspNet.Mvc/Microsoft.AspNet.Mvc.csproj +++ b/Microsoft.AspNet.Mvc/Microsoft.AspNet.Mvc.csproj @@ -61,6 +61,9 @@ + + + @@ -84,6 +87,9 @@ + + + diff --git a/Microsoft.AspNet.Mvc/MvcHandler.cs b/Microsoft.AspNet.Mvc/MvcHandler.cs index b93149b272..e933f10c28 100644 --- a/Microsoft.AspNet.Mvc/MvcHandler.cs +++ b/Microsoft.AspNet.Mvc/MvcHandler.cs @@ -20,6 +20,11 @@ namespace Microsoft.AspNet.Mvc _serviceProvider = serviceProvider ?? MvcServices.Create(); } + //public ServiceProvider ServiceProvider + //{ + // get { return (ServiceProvider)_serviceProvider; } + //} + public Task ExecuteAsync(IOwinContext context, IRouteData routeData) { var requestContext = new RequestContext(context, routeData); diff --git a/Microsoft.AspNet.Mvc/MvcServices.cs b/Microsoft.AspNet.Mvc/MvcServices.cs index 5b5acbfdce..20de6ab0dd 100644 --- a/Microsoft.AspNet.Mvc/MvcServices.cs +++ b/Microsoft.AspNet.Mvc/MvcServices.cs @@ -6,7 +6,7 @@ namespace Microsoft.AspNet.Mvc { public static class MvcServices { - public static IServiceProvider Create() + public static ServiceProvider Create() { var services = new ServiceProvider(); DoCallback((service, implementation) => services.Add(service, implementation)); @@ -21,6 +21,7 @@ namespace Microsoft.AspNet.Mvc callback(typeof(IActionResultFactory), typeof(ActionResultFactory)); callback(typeof(IContentNegotiator), typeof(DefaultContentNegotiator)); + // TODO: Should be many callback(typeof(IActionDescriptorProvider), typeof(ActionDescriptorProvider)); callback(typeof(IActionInvokerProvider), typeof(ActionInvokerProvider)); diff --git a/Microsoft.AspNet.Mvc/View/CompositeViewEngine.cs b/Microsoft.AspNet.Mvc/View/CompositeViewEngine.cs new file mode 100644 index 0000000000..685b727d78 --- /dev/null +++ b/Microsoft.AspNet.Mvc/View/CompositeViewEngine.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Mvc.View +{ + public class CompositeViewEngine : IViewEngine + { + private readonly List _viewEngines; + + public CompositeViewEngine() + : this(Enumerable.Empty()) + { + } + + public CompositeViewEngine(IEnumerable viewEngines) + { + _viewEngines = viewEngines.ToList(); + } + + public void Insert(int index, IViewEngine viewEngine) + { + _viewEngines.Insert(index, viewEngine); + } + + public void Add(IViewEngine viewEngine) + { + _viewEngines.Add(viewEngine); + } + + public async Task FindView(RequestContext controllerContext, string viewName) + { + if (_viewEngines.Count == 0) + { + return ViewEngineResult.NotFound(Enumerable.Empty()); + } + + var searchedPaths = new HashSet(StringComparer.OrdinalIgnoreCase); + for (int i = 0; i < _viewEngines.Count; i++) + { + ViewEngineResult result = await _viewEngines[i].FindView(controllerContext, viewName); + if (result.Success) + { + return result; + } + foreach (string location in result.SearchedLocations) + { + searchedPaths.Add(location); + } + } + + return ViewEngineResult.NotFound(searchedPaths); + } + } +} diff --git a/Microsoft.AspNet.Mvc/View/IView.cs b/Microsoft.AspNet.Mvc/View/IView.cs new file mode 100644 index 0000000000..f0ca1f9372 --- /dev/null +++ b/Microsoft.AspNet.Mvc/View/IView.cs @@ -0,0 +1,10 @@ +using System.IO; +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Mvc +{ + public interface IView + { + Task RenderAsync(ViewContext context, TextWriter writer); + } +} diff --git a/Microsoft.AspNet.Mvc/View/IViewEngine.cs b/Microsoft.AspNet.Mvc/View/IViewEngine.cs new file mode 100644 index 0000000000..a28e4fb71d --- /dev/null +++ b/Microsoft.AspNet.Mvc/View/IViewEngine.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Mvc +{ + public interface IViewEngine + { + Task FindView(RequestContext requestContext, string viewName); + } +} diff --git a/Microsoft.AspNet.Mvc/View/ViewContext.cs b/Microsoft.AspNet.Mvc/View/ViewContext.cs new file mode 100644 index 0000000000..b1a9581d3e --- /dev/null +++ b/Microsoft.AspNet.Mvc/View/ViewContext.cs @@ -0,0 +1,19 @@ +using System; +using Microsoft.AspNet.Mvc.Routing; +using Microsoft.Owin; + +namespace Microsoft.AspNet.Mvc +{ + public class ViewContext : RequestContext + { + public ViewContext(IOwinContext context, IRouteData routeData, object model) : + base(context, routeData) + { + Model = model; + } + + public object Model { get; private set; } + + public IServiceProvider ServiceProvider { get; set; } + } +} diff --git a/Microsoft.AspNet.Mvc/View/ViewEngineResult.cs b/Microsoft.AspNet.Mvc/View/ViewEngineResult.cs new file mode 100644 index 0000000000..624977da3c --- /dev/null +++ b/Microsoft.AspNet.Mvc/View/ViewEngineResult.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; + +namespace Microsoft.AspNet.Mvc +{ + public class ViewEngineResult + { + private ViewEngineResult() + { + } + + public IEnumerable SearchedLocations { get; private set; } + + public IView View { get; private set; } + + public bool Success + { + get { return View != null; } + } + + public static ViewEngineResult NotFound(IEnumerable searchedLocations) + { + if (searchedLocations == null) + { + throw new ArgumentNullException("searchedLocations"); + } + + return new ViewEngineResult + { + SearchedLocations = searchedLocations + }; + } + + public static ViewEngineResult Found(IView view) + { + if (view == null) + { + throw new ArgumentNullException("view"); + } + + return new ViewEngineResult + { + View = view + }; + } + } +} diff --git a/Microsoft.AspNet.Mvc/View/ViewResult.cs b/Microsoft.AspNet.Mvc/View/ViewResult.cs new file mode 100644 index 0000000000..fc508771f8 --- /dev/null +++ b/Microsoft.AspNet.Mvc/View/ViewResult.cs @@ -0,0 +1,61 @@ +using System; +using System.Globalization; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNet.CoreServices; + +namespace Microsoft.AspNet.Mvc +{ + public class ViewResult : IActionResult + { + private readonly IServiceProvider _serviceProvider; + private readonly IViewEngine _viewEngine; + + public ViewResult(IServiceProvider serviceProvider, IViewEngine viewEngine) + { + _serviceProvider = serviceProvider; + _viewEngine = viewEngine; + } + + public string ViewName {get; set; } + + public object Model { get; set; } + + public async Task ExecuteResultAsync(RequestContext context) + { + if (context == null) + { + throw new ArgumentNullException("context"); + } + + IView view = await FindView(context, ViewName); + using (view as IDisposable) + { + context.HttpContext.Response.ContentType = "text/html"; + using (var writer = new StreamWriter(context.HttpContext.Response.Body, Encoding.UTF8, 1024, leaveOpen: true)) + { + var viewContext = new ViewContext(context.HttpContext, context.RouteData, Model) + { + ServiceProvider = _serviceProvider + + }; + await view.RenderAsync(viewContext, writer); + } + } + } + + private async Task FindView(RequestContext requestContext, string viewName) + { + ViewEngineResult result = await _viewEngine.FindView(requestContext, viewName); + if (!result.Success) + { + string locationsText = String.Join(Environment.NewLine, result.SearchedLocations); + const string message = @"The view '{0}' was not found. The following locations were searched:{1}."; + throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, message, viewName, locationsText)); + } + + return result.View; + } + } +} diff --git a/MvcSample/HomeController.cs b/MvcSample/HomeController.cs index c03b32c9dd..2e7d89b762 100644 --- a/MvcSample/HomeController.cs +++ b/MvcSample/HomeController.cs @@ -47,5 +47,10 @@ namespace MvcSample return user; } + + public IActionResult MyView() + { + return Result.View(); + } } } \ No newline at end of file diff --git a/MvcSample/MvcSample.csproj b/MvcSample/MvcSample.csproj index 04f0576d4e..f0e1bcfe89 100644 --- a/MvcSample/MvcSample.csproj +++ b/MvcSample/MvcSample.csproj @@ -70,8 +70,18 @@ + + + + {ec38534c-a2d1-413f-97d1-55eef5d2fb71} + Microsoft.AspNet.CoreServices + + + {224a14d0-eca7-441c-ae89-b6e66a57ef9b} + Microsoft.AspNet.Mvc.Razor + {2a0c26f1-0240-4ae1-ae00-4691c291b122} Microsoft.AspNet.Mvc diff --git a/MvcSample/Startup.cs b/MvcSample/Startup.cs index 5b3d053578..9d2f1e3e4a 100644 --- a/MvcSample/Startup.cs +++ b/MvcSample/Startup.cs @@ -1,6 +1,8 @@ using System; using System.Threading.Tasks; +using Microsoft.AspNet.CoreServices; using Microsoft.AspNet.Mvc; +using Microsoft.AspNet.Mvc.Razor; using Microsoft.AspNet.Mvc.Routing; using Microsoft.Owin; using Owin; @@ -15,8 +17,12 @@ namespace MvcSample { app.UseErrorPage(); - var handler = new MvcHandler(); - + var serviceProvider = MvcServices.Create(); + serviceProvider.AddInstance(new MetadataVirtualPathProvider(GetType().Assembly)); + serviceProvider.Add(); + + var handler = new MvcHandler(serviceProvider); + app.Run(async context => { // Pretending to be routing diff --git a/MvcSample/ViewMetadata.cs b/MvcSample/ViewMetadata.cs new file mode 100644 index 0000000000..1050f363b7 --- /dev/null +++ b/MvcSample/ViewMetadata.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; + +public class ViewMetadata +{ + public static Dictionary Metadata + { + get + { + return new Dictionary + { + { + "~/Views/Home/MyView.cshtml", + typeof(MvcSample.Views.MyView) + }, + { + "~/Views/Shared/_Layout.cshtml", + typeof(MvcSample.Views.Layout) + } + }; + } + } +} diff --git a/MvcSample/Views/Layout.cs b/MvcSample/Views/Layout.cs new file mode 100644 index 0000000000..1bb09670f9 --- /dev/null +++ b/MvcSample/Views/Layout.cs @@ -0,0 +1,20 @@ +using System; +using Microsoft.AspNet.Mvc.Razor; + +namespace MvcSample.Views +{ + [VirtualPath("~/Views/Shared/_Layout.cshtml")] + public class Layout : RazorView + { + protected override void Execute() + { + WriteLiteral(""); + WriteLiteral(""); + WriteLiteral("

Hello world

"); + WriteLiteral("
"); + WriteLiteral(""); + } + } +} \ No newline at end of file diff --git a/MvcSample/Views/MyView.cs b/MvcSample/Views/MyView.cs new file mode 100644 index 0000000000..f44152c4da --- /dev/null +++ b/MvcSample/Views/MyView.cs @@ -0,0 +1,17 @@ +using System; +using Microsoft.AspNet.Mvc.Razor; + +namespace MvcSample.Views +{ + [VirtualPath("~/Views/Home/MyView.cshtml")] + public class MyView : RazorView + { + protected override void Execute() + { + Layout = "~/Views/Shared/_Layout.cshtml"; + WriteLiteral("
The time is now"); + Write(new HtmlString(DateTime.UtcNow.ToString())); + WriteLiteral("
"); + } + } +} \ No newline at end of file