ASP MVC3 — Как загрузить пользовательский пользовательский макет для страницы из базы данных?

#asp.net-mvc #asp.net-mvc-3 #razor

#asp.net-mvc #asp.net-mvc-3 #razor

Вопрос:

У меня есть приложение для создания онлайн-форм в ASP.NET MVC3 с видом на Бритву. Это похоже на это — https://examples.wufoo.com/forms/workshop-registration /

Мне нужно, чтобы пользователи могли настраивать дизайн страницы. Не только для загрузки пользовательского css, но и для настройки шаблона HTML-страницы. Допустим, пользователи должны иметь полный контроль над HTML-макетом для своей пользовательской страницы веб-формы. Пользователь должен иметь возможность редактировать любой HTML-код на странице, кроме формы, которая включена в макет.

Я не уверен, как это сделать с помощью Razor и ASP.NET MVC 3. Возможно ли:

  1. загрузите макет где-нибудь из базы данных в виде строки или чего-то еще
  2. замените некоторые пользовательские теги, такие как «FORM1_INCLUDE» на @Html.Partial(«some_non_customizable_layout_for_form1»)
  3. используйте результат в качестве допустимого файла макета для страницы формы пользователя

Возможно, 1-3 — не лучший способ сделать то, что мне нужно. Что вы можете предложить для такого пользовательского подхода к макету страницы в ASP.NET MVC 3 с представлениями Razor?

ОБНОВЛЕНИЕ 1 С помощью VirtualPathProvider я смог загрузить представление из базы данных, но он просто возвращает текст типа:

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

и вообще не обрабатывает синтаксис Razor.
В чем может быть проблема?

РЕШАЕМАЯ:

Необходимо поместить эту строку в качестве первой в метод Application_Start():

 HostingEnvironment.RegisterVirtualPathProvider(new MyVirtualPathProvider());
 

ОБНОВЛЕНИЕ 2
Поставщик пользовательских представлений зарегистрирован в 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);
        }
    }
}
 

And now for the Razor view code defined as a string above I get this exception:

«Compiler Error Message: CS1061: ‘System.Web.Mvc.AjaxHelper’ does not contain a definition for ‘BeginForm’ and no extension method ‘BeginForm’ accepting a first argument of type ‘System.Web.Mvc.AjaxHelper’ could be found (are you missing a using directive or an assembly reference?)»

And when I changed Razor View code to:

 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> -->";
 

I got a different error:

Тип ‘ASP._Page__appstart_cshtml’ не наследуется от ‘System.Web.WebPages.ApplicationStartPage’

Когда я меняю

@наследует System.Web.Mvc.WebViewPage

Для

@наследует System.Web.WebPages.ApplicationStartPage

чтобы исправить указанную выше ошибку, я получаю новую:

«Сообщение об ошибке компилятора: CS0103: имя «Ajax» не существует в текущем контексте»

UPDATE3: Я пытался использовать 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> -->
 

Комментарии:

1. Как вы используете свои представления? Вы не должны использовать своего провайдера напрямую, а зарегистрировать его вместо стандартного.

2. @gauffin Я создал UPDATE2, чтобы ответить на вопрос об использовании представления.

3. Может быть только один VirtualPathProvider. Вам нужно вызвать base.XXXX для всех файлов, которые вы не можете обработать. Также: все ваши представления должны наследоваться @inherits System.Web.Mvc.WebViewPage<Namespace.YourModelName> , чтобы быть строго типизированными.

4. base.XXXX — конечно. Но это не связано с проблемами, описанными выше. @наследует System.Web.Mvc.WebViewPage<Пространство имен. YourModelName> Это первое, что я попробовал. Описанные выше проблемы возникают со строго типизированными представлениями и без них.

5. Эта ошибка: тип ‘ASP.Page_appstart_cshtml’ не наследуется от ‘System.Web.WebPages. ApplicationStartPage очень странный. Это происходит, когда я использую @inherits System.Web.Mvc.WebViewPage<Namespace.AnyModel> или просто @inherits System.Web.Mvc.WebViewPage

Ответ №1:

Вы должны создать поставщик виртуального пути, который извлекает ваши пользовательские представления из базы данных.

Здесь есть несколько вопросов о них. Просто найдите VirtualPathProvider

Обновления (из моих комментариев к обсуждению вопроса)

  • VirtualPathProvider должен быть зарегистрирован в Application_Start с помощью HostingEnvironment.RegisterVirtualPathProvider(new MyVirtualPathProvider());
  • Базовый класс ДОЛЖЕН вызываться для всех файлов, которые вы в настоящее время не можете обслуживать. Это требуется, поскольку может быть только один VirtualPathProvider . (В противном случае вы увидите много странных ошибок)
  • @model директива не работает в файлах, которые вы обслуживаете. Вместо этого вы должны использовать @inherits System.Web.Mvc.WebViewPage<YourNameSpace.YourModelName> .
  • IIRC вам также необходимо переопределить GetCacheDependency и вернуть null для собственных ресурсов.

Комментарии:

1. Я следил за всем этим. Результат находится в моем update3. Теперь шаблон Razor отображается в браузерах без синтаксического анализа. Похоже, я пропустил здесь что-то простое…

2. Только что проверил моего собственного провайдера. Проверьте мой последний маркер.

3. Расширение пользовательского файла 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>");
 

Конечно, в зависимости от характера вашего приложения вы можете преобразовать это в какое-то преобразование tag => markup, которое позволит вам добавлять больше пользовательских тегов и соответствующих им реальных разметок HTML.

Надеюсь, это поможет, ура!

Комментарии:

1. @Html.Raw() не поддерживает помощники Mvc, такие как Html и Url. И это необходимо, потому что форма создается с использованием помощников Mvc.

Ответ №3:

Я думаю, вам нужно использовать что-то вроде этого:

http://vibrantcode.com/blog/2010/11/16/hosting-razor-outside-of-aspnet-revised-for-mvc3-rc.html

Ответ №4:

Я сделал что-то подобное, и вы можете счесть, что XML очень полезен. Вы сохраняете определение макета в формате XML в БД. Это означает, что им легко манипулировать, либо напрямую, либо путем сериализации / десериализации в объектную модель.

Если вы хотите отобразить страницу, используйте XSLT для преобразования вашего XML в HTML, применяя стили и т. Д. К выходным данным.

Комментарии:

1. XML с XSLT здесь не будет работать. Пользователи, которые будут редактировать макет, хотят использовать HTML. На самом деле они разместят дизайн HTML-страницы со своего собственного сайта, чтобы страница формы на моем сайте выглядела похожей на их сайт, когда они перенаправляют посетителей на страницу формы.