【问题标题】:ASP MVC3 - How to load a custom user defined layout for the page from a database?ASP MVC3 - 如何从数据库加载页面的自定义用户定义布局?
【发布时间】:2011-10-28 12:56:01
【问题描述】:

我在带有 Razor 视图的 ASP.NET MVC3 中有在线表单构建器应用程序。 和这个类似——https://examples.wufoo.com/forms/workshop-registration/

我需要用户能够自定义页面设计。 不仅可以上传自定义css,还可以自定义HTML页面模板。 假设用户应该完全控制其自定义 Web 表单页面的 Layout 的 HTML。除了包含在布局中的表单之外,用户应该能够编辑页面上的任何 HTML。

我不确定如何使用 Razor 和 ASP.NET MVC 3 做到这一点。 是否可以:

  1. 从数据库中以字符串或其他方式加载布局
  2. 将一些自定义标签(如“FORM1_INCLUDE”)替换为 @Html.Partial("some_non_customizable_layout_for_form1")
  3. 将结果用作用户表单页面的有效布局文件

也许 1-3 不是满足我需要的最佳方式。 对于带有 Razor 视图的 ASP.NET MVC 3 中这种用户定义的页面布局方法,您有什么建议?

更新 1 使用 VirtualPathProvider 我能够从数据库加载视图,但它只返回如下文本:

@inherits System.Web.Mvc.WebViewPage
<body>
@Html.EditorFor(z => z.Customer)
</body>

并且根本不处理任何 Razor 语法。 它可能有什么问题?

已解决:

需要将此行作为 Application_Start() 方法中的第一行:

HostingEnvironment.RegisterVirtualPathProvider(new MyVirtualPathProvider());

更新 2 Custom View Provider 在 Global.asax.cs 中注册为:

protected void Application_Start()
        {
            HostingEnvironment.RegisterVirtualPathProvider(new MyVirtualPathProvider());

            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);
        }

MyVirtualPathProvider 代码是:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Hosting;
using System.IO;
using System.Text;

namespace new_frontend
{
    public class MyVirtualPathProvider : VirtualPathProvider
    {
        public override bool FileExists(string virtualPath)
        {
            var td = FindTemplate(virtualPath);
            if (td == null)
            {
                return true;
                //return base.FileExists(virtualPath);
            }
            else
            {
                return true;
            }
        }

        public override VirtualFile GetFile(string virtualPath)
        {
            var td = FindTemplate(virtualPath);
            if (td == null)
            {
                return new MyVirtualFile(virtualPath, "");
                //return base.GetFile(virtualPath);
            }
            else
            {
                return new MyVirtualFile(virtualPath, td.ContentStep1);
            }
        }

        private Facade.Dto.TemplateData FindTemplate(string virtualPath)
        {
            string prefix = "Template#";

            int id = 0;
            Facade.Dto.TemplateData td = null;

            string fileName = System.IO.Path.GetFileNameWithoutExtension(virtualPath);
            if (fileName.StartsWith(prefix))
                Int32.TryParse(fileName.Substring(prefix.Length), out id);

            if (id > 0)
                td = Facade.FrontEndServices.GetTemplate(id);

            return td;
        }
    }

    public class MyVirtualFile : VirtualFile
    {
        private byte[] data;

        public MyVirtualFile(string virtualPath, string body)
            : base(virtualPath)
        {                      // 'System.Web.WebPages.ApplicationStartPage
            string _body = /*body +*/ @"

@inherits System.Web.Mvc.WebViewPage
@using (Ajax.BeginForm(""Submit"", new AjaxOptions { UpdateTargetId = ""main"" }))
{
}
<!-- <PERSONAL_INFO> -->
<div id=""personal_info"" class=""op2-block"">

</div>
<!-- <PERSONAL_INFO> -->";

            this.data = Encoding.UTF8.GetBytes(_body);
        }

        public override System.IO.Stream Open()
        {
            return new MemoryStream(data);
        }
    }
}

现在对于上面定义为字符串的 Razor 视图代码,我得到了这个异常:

“编译器错误消息:CS1061:‘System.Web.Mvc.AjaxHelper’不包含‘BeginForm’的定义,并且没有扩展方法‘BeginForm’接受‘System.Web.Mvc’类型的第一个参数.AjaxHelper' 可以找到(您是否缺少 using 指令或程序集引用?)"

当我将 Razor View 代码更改为:

string _body = /*body +*/ @"

@using System.Web.WebPages;
@using System.Web.Mvc;
@using System.Web.Mvc.Ajax;
@using System.Web.Mvc.Html;
@using System.Web.Routing;

@inherits System.Web.Mvc.WebViewPage<dynamic>

@using (Ajax.BeginForm(""Submit"", new AjaxOptions { UpdateTargetId = ""main"" }))
{
}
<!-- <PERSONAL_INFO> -->
<div id=""ppg_op2_personal_info"" class=""op2-block"">

</div>
<!-- <PERSONAL_INFO> -->";

我得到一个不同的错误:

类型“ASP._Page__appstart_cshtml”不继承自“System.Web.WebPages.ApplicationStartPage”

当我改变时

@inherits System.Web.Mvc.WebViewPage

@inherits System.Web.WebPages.ApplicationStartPage

为了修复上面的错误,我得到了一个新的:

“编译器错误消息:CS0103:名称 'Ajax' 在当前上下文中不存在”

更新3: 我尝试使用 base.XXX:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Hosting;
using System.IO;
using System.Text;

namespace new_frontend
{
    public class MyVirtualPathProvider : VirtualPathProvider
    {
        public override bool FileExists(string virtualPath)
        {
            var td = FindTemplate(virtualPath);
            if (td == null)
            {
                //return true;
                return base.FileExists(virtualPath);
            }
            else
            {
                return true;
            }
        }

        public override VirtualFile GetFile(string virtualPath)
        {
            var td = FindTemplate(virtualPath);
            if (td == null)
            {
                //return new MyVirtualFile(virtualPath, "");
                return base.GetFile(virtualPath);
            }
            else
            {
                return new MyVirtualFile(virtualPath, td.ContentStep1);
            }
        }

        private Facade.Dto.TemplateData FindTemplate(string virtualPath)
        {
            string prefix = "Template#";

            int id = 0;
            Facade.Dto.TemplateData td = null;

            string fileName = System.IO.Path.GetFileNameWithoutExtension(virtualPath);
            if (fileName.StartsWith(prefix))
                Int32.TryParse(fileName.Substring(prefix.Length), out id);

            if (id > 0)
                td = Facade.FrontEndServices.GetTemplate(id);

            return td;
        }
    }

    public class MyVirtualFile : VirtualFile
    {
        private byte[] data;

        public MyVirtualFile(string virtualPath, string body)
            : base(virtualPath)
        {                      // 'System.Web.WebPages.ApplicationStartPage
            string _body = /*body +*/ @"

@inherits System.Web.Mvc.WebViewPage<PPG.Facade.Dto.NewOrderPageData>
@using (Ajax.BeginForm(""Submit"", new AjaxOptions { UpdateTargetId = ""main"" }))
{
}
<!-- <PERSONAL_INFO> -->
<div id=""personal_info"" class=""op2-block"">

</div>
<!-- <PERSONAL_INFO> -->";

            this.data = Encoding.UTF8.GetBytes(_body);
        }

        public override System.IO.Stream Open()
        {
            return new MemoryStream(data);
        }
    }
}

在这种情况下,我得到一个根本没有解析的视图,这是我在网络浏览器中得到的:

@using System.Web.WebPages;    
@using System.Web.Mvc;    
@using System.Web.Mvc.Ajax;    
@using System.Web.Mvc.Html;    
@using System.Web.Routing;    
@inherits System.Web.Mvc.WebViewPage<PPG.Facade.Dto.NewOrderPageData>
@using (Ajax.BeginForm("Submit", new AjaxOptions { UpdateTargetId = "main" }))
{
}
<!-- <PERSONAL_INFO> -->
<div id="ppg_op2_personal_info" class="op2-block">
</div>
<!-- <PERSONAL_INFO> -->

【问题讨论】:

  • 您如何使用您的视图?你不应该直接使用你的提供者,而是注册它而不是默认的。
  • @gauffin 我创建了 UPDATE2 来回答关于视图使用的问题。
  • VirtualPathProvider 只能有一个。您需要为您无法处理的所有文件调用base.XXXX。另外:您的所有视图都必须继承 @inherits System.Web.Mvc.WebViewPage&lt;Namespace.YourModelName&gt; 才能进行强类型化。
  • base.XXXX - 当然。但这与上述问题无关。 @inherits System.Web.Mvc.WebViewPage 这是我尝试的第一件事。上面描述的问题发生在强类型视图和没有它们的情况下。
  • 这个错误:Type 'ASP.Page_appstart_cshtml' does not inherit from 'System.Web.WebPages.ApplicationStartPage 很奇怪。当我使用 @inherits System.Web.Mvc.WebViewPage&lt;Namespace.AnyModel&gt; 或仅使用 @inherits System.Web.Mvc.WebViewPage 时会发生这种情况

标签: asp.net-mvc asp.net-mvc-3 razor


【解决方案1】:

您应该创建一个虚拟路径提供程序,从数据库中获取您的自定义视图。

这里有几个关于它们的问题。只需搜索 VirtualPathProvider

更新(从我的 cmets 到问题讨论)

  • VirtualPathProvider 必须在 Application_Start 中使用HostingEnvironment.RegisterVirtualPathProvider(new MyVirtualPathProvider());注册

  • 必须为您当前无法提供的所有文件调用基类。这是必需的,因为只能有一个 VirtualPathProvider。 (否则你会看到很多奇怪的错误)

  • @model 指令在您提供的文件中不起作用。您必须改用@inherits System.Web.Mvc.WebViewPage&lt;YourNameSpace.YourModelName&gt;

  • IIRC 您还需要覆盖 GetCacheDependency 并返回 null 以获得您自己的资源。

【讨论】:

  • 我遵循了所有这些。结果在我的update3中。现在 Razor 模板无需解析就显示在浏览器中。看起来我在这里错过了一些简单的东西......
  • 刚刚检查了我自己的提供商。检查我的最后一个子弹。
  • virtualview 自定义文件的扩展名很重要。更改后应该是 .cshtml - 开始解析文件。
【解决方案2】:

根据您希望赋予的灵活性和自定义程度,这可能是一项非常简单到非常耗时的任务。

如果您希望用户简单地自定义页面上的 HTML,那么您可以使用 WYSIWYG 编辑器并将原始 html 存储在数据库中。

在视图中,使用

@Html.Raw(Model.body) // Where body is the field containing the wysiwyg content

这将按原样呈现标记。

要包含自定义标记/替换,您必须定义一个字符串常量列表,在使用 WYSIWYG 编辑器时可以插入这些常量。然后可以搜索 - 显示时替换它们。

例如在您的控制器或模型中:

model.body.replace("$[form1]", "<form action='something' method='post' name='form1'></form>");

当然,根据您的应用程序的性质,您可能希望将其重新分解为某种标记 => 标记转换,这将允许您添加更多自定义标记及其各自的真实 HTML 标记。

希望这会有所帮助,干杯!

【讨论】:

  • @Html.Raw() 不支持 Html 和 Url 等 Mvc 助手。它是必需的,因为表单是使用 Mvc 助手构建的。
【解决方案3】:
【解决方案4】:

我做过类似的事情,您可能会觉得 XML 非常有用。将布局定义作为 XML 保存到 DB。这意味着它很容易操作,直接或通过序列化/反序列化到对象模型中。

当您想要显示页面时,使用 XSLT 将您的 XML 转换为 HTML,将样式等应用到输出。

【讨论】:

  • XML 与 XSLT 在这里不起作用。将编辑布局的用户希望使用 HTML。实际上,当他们将访问者重定向到表单页面时,他们会放置自己网站上的 HTML 页面设计,以使我网站上的表单页面看起来与他们的网站相似。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-24
  • 2020-05-26
  • 2021-03-09
  • 2012-04-12
  • 2019-12-22
相关资源
最近更新 更多