Подтвердить что ты не робот

Инструмент для преобразования разметки обозревателя WebForm в разметку Engine Razor

Есть ли (или будет) инструмент для преобразования разметки обозревателя WebForm (aspx) в разметку engine view (cshtml) Razor?

4b9b3361

Ответ 1

Мы тоже столкнулись с проблемой преобразования большого количества представлений WebForms в Razor. И угадайте, что, мы также придумали инструмент:

https://github.com/telerik/razor-converter

Он также полагается на регулярные выражения (и немало из них), чтобы понять WebMorms mumbo-jumbo, как инструмент JohnnyO. Наши могут охватывать еще несколько случаев, но не задумывайтесь об этом и попробуйте на нескольких взглядах.

Ответ 2

Вот просто консольное приложение, которое я написал для преобразования представлений WebForms в Razor. К сожалению, это не пуленепробиваемый. Я конвертировал около 20 просмотров с этим, и я нахожу это для простых представлений, он получает это на 100% правильно, но когда представление становится очень сложным, преобразование только приблизительно на 80% правильное. Даже на 80% он все же лучше, чем начинать с самого начала вручную.

В этом инструменте преобразования есть некоторые вещи, которые могут быть специфическими для того, как я разработал свои представления в WebForms, поэтому я прокомментировал его, чтобы вы могли удалить его или настроить его.

Вы можете использовать его, выполнив консольное приложение и пройдя путь к папке или путь к файлу. Если вы перейдете в папку, он преобразует все файлы .aspx и .ascx в папку (но не будет записывать в подпапки). Если вы пройдете путь к файлу, он преобразует только этот файл. Как только это будет сделано с преобразованием, он создаст новый .cshtml файл с тем же именем, что и файл .aspx или .ascx.

ПРЕДУПРЕЖДЕНИЕ. Если у вас уже есть файл .cshtml с тем же именем, что и файл .aspx/.ascx, который вы пытаетесь преобразовать, это приложение перезапишет файл .cshtml, когда он завершит .aspx/.ascx преобразование.

ИСПОЛЬЗУЙТЕ СВОЙ СОБСТВЕННЫЙ РИСК. УБЕДИТЕСЬ, ЧТОБЫ ОБРАТИТЬСЯ ВСЕ НА ИСПОЛЬЗОВАНИЕ ЭТОГО ИНСТРУМЕНТА.

Примечание. Если ваш файл aspx/ascx помечен как "только для чтения", вы получите сообщение об ошибке, потому что консольное приложение не сможет его открыть. Убедитесь, что флажок только для чтения отключен для всех файлов aspx/ascx, которые вы пытаетесь преобразовать.

class Program {
    private readonly static string razorExtension = ".cshtml";

    /// <summary>Usage: RazorConverter.exe  "C:\Files" or RazorConverter.exe  "C:\Files\MyFile.aspx"</summary>
    static void Main(string[] args) {

        if (args.Length < 1)
            throw new ArgumentException("File or folder path is missing.");
        string path = args[0];

        List<string> convertedFiles = new List<string>();
        Regex webFormsExtension = new Regex(".aspx$|.ascx$");
        if (Directory.Exists(path)) {
            foreach (var file in Directory.GetFiles(path, "*.aspx")) {
                var outputFile = webFormsExtension.Replace(file, razorExtension);
                ConvertToRazor(file, outputFile);
                convertedFiles.Add(file);
            }

            foreach (var file in Directory.GetFiles(path, "*.ascx")) {
                var outputFile = webFormsExtension.Replace(file, razorExtension);
                ConvertToRazor(file, outputFile);
                convertedFiles.Add(file);
            }
        } else if (File.Exists(path)) {
            var match = webFormsExtension.Match(path);
            if (match.Success) {
                ConvertToRazor(path, webFormsExtension.Replace(path, razorExtension));
                convertedFiles.Add(path);
            } else {
                throw new ArgumentException(String.Format("{0} file isn't a WebForms view", path));
            }
        } else {
            throw new ArgumentException(String.Format("{0} doesn't exist", path));
        }

        Console.WriteLine(String.Format("The following {0} files were converted:", convertedFiles.Count));
        foreach (var file in convertedFiles) {
            Console.WriteLine(file);
            }
    }

    private static void ConvertToRazor(string inputFile, string outputFile) {

        // Known Bug: when writing anything directly to the response (other than for HTML helpers (e.g. Html.RenderPartial or Html.RenderAction)),
        // this Converter will not correctly generate the markup. For example:
        // <% Html.RenderPartial("LogOnUserControl"); %> will properly convert to @{ Html.RenderPartial("LogOnUserControl"); }
        // but
        // <% MyCustom("Foo"); %> will incorrectly convert to @MyCustom("Foo");

        string view;
        using (FileStream fs = new FileStream(inputFile, FileMode.Open))
        using (StreamReader sr = new StreamReader(fs)) {
            view = sr.ReadToEnd();
        }

        // Convert Comments
        Regex commentBegin = new Regex("<%--\\s*");
        Regex commentEnd = new Regex("\\s*--%>");
        view = commentBegin.Replace(view, "@*");
        view = commentEnd.Replace(view, "*@");

        // Convert Model
        Regex model = new Regex("(?<=Inherits=\"System.Web.Mvc.ViewPage<|Inherits=\"System.Web.Mvc.ViewUserControl<)(.*?)(?=>\")");
        Regex pageDeclaration = new Regex("(<%@ Page|<%@ Control).*?%>");
        Match modelMatch = model.Match(view);
        if (modelMatch.Success) {
            view = pageDeclaration.Replace(view, "@model " + modelMatch.Value);
        } else {
            view = pageDeclaration.Replace(view, String.Empty);
        }

        // TitleContent
        // I'm converting the "TitleContent" ContentPlaceHolder to View.Title because
        // that what TitleContent was for.  You may want to ommit this.
        Regex titleContent = new Regex("<asp:Content.*?ContentPlaceHolderID=\"TitleContent\"[\\w\\W]*?</asp:Content>");
        Regex title = new Regex("(?<=<%:\\s).*?(?=\\s*%>)");
        var titleContentMatch = titleContent.Match(view);
        if (titleContentMatch.Success) {
            var titleVariable = title.Match(titleContentMatch.Value).Value;
            view = titleContent.Replace(view, "@{" + Environment.NewLine + "    View.Title = " + titleVariable + ";" + Environment.NewLine + "}");
            // find all references to the titleVariable and replace it with View.Title
            Regex titleReferences = new Regex("<%:\\s*" + titleVariable + "\\s*%>");
            view = titleReferences.Replace(view, "@View.Title");
        }

        // MainContent
        // I want the MainContent ContentPlaceholder to be rendered in @RenderBody().
        // If you want another section to be rendered in @RenderBody(), you'll want to modify this
        Regex mainContent = new Regex("<asp:Content.*?ContentPlaceHolderID=\"MainContent\"[\\w\\W]*?</asp:Content>");
        Regex mainContentBegin = new Regex("<asp:Content.*?ContentPlaceHolderID=\"MainContent\".*?\">");
        Regex mainContentEnd = new Regex("</asp:Content>");
        var mainContentMatch = mainContent.Match(view);
        if (mainContentMatch.Success) {
            view = view.Replace(mainContentMatch.Value, mainContentBegin.Replace(mainContentEnd.Replace(mainContentMatch.Value, String.Empty), String.Empty));
        }

        // Match <%= Foo %> (i.e. make sure we're not HTML encoding these)
        Regex replaceWithMvcHtmlString = new Regex("<%=\\s.*?\\s*%>"); // removed * from the first <%=\\s.*?\\s*%> here because I couldn't figure out how to do the equivalent in the positive lookbehind in mvcHtmlStringVariable
        Regex mvcHtmlStringVariable = new Regex("(?<=<%=\\s).*?(?=\\s*%>)");
        // Match <%, <%:
        Regex replaceWithAt = new Regex("<%:*\\s*");
        // Match  %>, <% (but only if there a proceeding })
        Regex replaceWithEmpty = new Regex("\\s*%>|<%\\s*(?=})");

        var replaceWithMvcHtmlStrings = replaceWithMvcHtmlString.Matches(view);
        foreach (Match mvcString in replaceWithMvcHtmlStrings) {
            view = view.Replace(mvcString.Value, "@MvcHtmlString.Create(" + mvcHtmlStringVariable.Match(mvcString.Value).Value + ")");
            }

        view = replaceWithEmpty.Replace(view, String.Empty);
        view = replaceWithAt.Replace(view, "@");

        Regex contentPlaceholderBegin = new Regex("<asp:Content[\\w\\W]*?>");
        Regex contentPlaceholderId = new Regex("(?<=ContentPlaceHolderID=\").*?(?=\")");
        Regex contentPlaceholderEnd = new Regex("</asp:Content>");
        MatchCollection contentPlaceholders = contentPlaceholderBegin.Matches(view);
        foreach (Match cp in contentPlaceholders) {
            view = view.Replace(cp.Value, "@section " + contentPlaceholderId.Match(cp.Value).Value + " {");
        }
        view = contentPlaceholderEnd.Replace(view, "}");

        // if we have something like @Html.RenderPartial("LogOnUserControl");, replace it with @{ Html.RenderPartial("LogOnUserControl"); }
        Regex render = new Regex("@Html\\.\\S*\\(.*\\)\\S*?;");
        var renderMatches = render.Matches(view);
        foreach (Match r in renderMatches) {
            view = view.Replace(r.Value, "@{ " + r.Value.Substring(1) + " }");
        }


        using (FileStream fs = new FileStream(outputFile, FileMode.Create)) {
            byte[] bytes = Encoding.UTF8.GetBytes(view);
            fs.Write(bytes, 0, bytes.Length);
        }
    }
}

Ответ 3

Пользователи ReSharper могут проголосовать за возможность автоматического преобразования из aspx в cshtml. ReSharper будет иметь представление AST для обеих сторон, поэтому они могут (теоретически) выполнять очень прочную работу, не требуя регулярных выражений.

http://youtrack.jetbrains.net/issue/RSRP-194060