2012-08-07 46 views
0

注入HTML成母版我想開發一種方式,它有可能纏上網站的正確的外觀和感覺。淨MVC的Web應用程序,它是被鏈接到。屏幕抓取頁面使用C#和MVC

基本上我要存儲一個「參考頁」針對我的應用程序將使用屏幕抓取的頁眉/頁腳從HTML在其母版頁使用無所不包站點的URL。

所以,如果/當網站(從CMS輸出)改變其結構/圖像/顏色我的應用程序將只使用新創建的「模板」,並相應地包裝自己。

在'template'中設置了開始/結束div標籤,因此我只需要屏蔽HTML,在相關點分割它,並以某種方式將其注入到我的應用程序的MasterPage中。

的屏幕抓取部分看起來相當簡單,它是注入到主網頁,其中我有問題整理出來。

任何幫助將不勝感激。 :)

編輯 - 我目前在我的腦海規劃這一點,並沒有代碼來發布。正如我所說的,屏幕抓圖部分看起來不錯,但是我將如何將從頁眉/頁腳的「參考頁面」中提取的相關HTML插入/注入到我的應用程序正在使用的主頁中?

+0

您好MrSharky - 不幸的是,這個問題(?)的範圍太廣泛,而且在您尋找的實際幫助方面還不太清楚。你描述了很多,但沒有解釋你在哪裏遇到問題,你曾經嘗試過什麼,以及你需要什麼樣的幫助。 – Oded 2012-08-07 09:37:00

+0

你可以在主頁中使用文字嗎?你可以將頭部HTML細分,然後將其存儲在一個字符串中,然後在主頁面代碼中設置literal = header頭部 – dtsg 2012-08-07 09:37:28

+0

@Oded道歉,目前這並不是什麼問題,你是對的。我會編輯它以更具體一點。謝謝 – MrSharky 2012-08-07 10:05:05

回答

0

我知道你可能已經解決了這一點,但這裏要說的是母版頁和MVC(和ASP.Net形式,以及)有效的解決方案。

我首先嚐試覆蓋母版頁的Render方法,然後使用RenderControl渲染ContentPlaceHolders,並用渲染結果替換模板中的某些標記。這適用於ASP.Net表單,但不適用於MVC - 這種方式<% using (Html.BeginForm("A","B")) { %>總是會導致在doctype之前在頁面的頂部呈現表單標記。

解決方案

檢索模板,並將其分割成其組成部分,有些是文字部分,有些是佔位符部分。在您的母版頁中,您有一個HTML文檔和您的佔位符 - 不僅是您的佔位符。這樣VS設計師就不會抱怨。但是,渲染時,首先清除Controls集合,然後將每個部分添加爲LiteralControl或ContentPlaceHolder。您只需將實際渲染保留到ASP.Net。以下是靈感代碼。

母版頁:

<!DOCTYPE html> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head runat="server"> 
    <title runat="server"></title> 
    <asp:PlaceHolder ID="HeadPlaceHolder" runat="server"> 
     <script type="text/javascript" src="/cnnet/Resources/Js/jquery-1.8.1.min.js"></script> 
    </asp:PlaceHolder> 
    <asp:ContentPlaceHolder ID="HeadContentPlaceHolder" runat="server"/> 
</head> 
<body> 
    <asp:ContentPlaceHolder ID="MainContentPlaceHolder" runat="server" /> 
</body> 
</html> 

母版頁的代碼隱藏:

private HtmlHead originalPageHeader; 
static readonly Regex HeadStartRegex = new Regex(@"^\s*<head[^>]*>"); 
static readonly Regex HeadEndRegex = new Regex(@"</head>\s*$"); 
static readonly Regex TitleRegex = new Regex(@"<title>[^<]*</title>"); 

public Default() { Init += Default_Init; } 

private void Default_Init(object sender, EventArgs e) { DoScraping(); } 

protected override void Render(HtmlTextWriter writer) 
{ 
    // get content from html head control generated via Page.Header: 
    string headHtml = RenderControl(originalPageHeader); 
    Controls.Remove(originalPageHeader); 
    headHtml = HeadStartRegex.Replace(headHtml, string.Empty); 
    headHtml = HeadEndRegex.Replace(headHtml, string.Empty); 
    headHtml = TitleRegex.Replace(headHtml, string.Empty); 
    // head.Controls.Add(new LiteralControl(headHtml)); doesnt work if head content placeholder contains code blocks (i.e. <% ... %>) 
    // Instead add content this way: 
    int headIndex = Controls.IndexOf(HeadContentPlaceHolder); 
    if (headIndex != -1) 
     Controls.AddAt(headIndex + 1, new LiteralControl(headHtml)); 

    base.Render(writer); 
} 

private void DoScraping() 
{ 
    IList<PagePart> parts = ... // do your scraping and splitting into parts 
    Controls.Clear(); 

    foreach (PagePart part in parts) 
    { 
     var literalPart = part as LiteralPart; 
     if (literalPart != null) 
     { 
      Controls.Add(new LiteralControl(literalPart.Text)); 
     } 
     else 
     { 
      var placeHolderPart = part as PlaceHolderPart; 
      switch (placeHolderPart.Type) 
      { 
       case PlaceHolderType.Title: 
        Controls.Add(new LiteralControl(HttpUtility.HtmlEncode(Page.Title))); 
        break; 
       case PlaceHolderType.Head: 
        Controls.Add(HeadPlaceHolder); 
        Controls.Add(HeadContentPlaceHolder); 
        break; 
       case PlaceHolderType.Main: 
        Controls.Add(new LiteralControl("<div class='boxContent'>")); 
        Controls.Add(MainContentPlaceHolder); 
        Controls.Add(new LiteralControl("<div/>")); 
        break; 
      } 
     } 
    } 
} 

private string RenderControl(Control control) 
{ 
    string innerHtml; 
    using (var stringWriter = new StringWriter()) 
    { 
     using (var writer = new HtmlTextWriter(stringWriter)) 
     { 
      control.RenderControl(writer); 
      writer.Flush(); 
      innerHtml = stringWriter.ToString(); 
     } 
    } 
    return innerHtml; 
} 

配件:

public class PagePart {} 

public class LiteralPart : PagePart 
{ 
    public LiteralPart(string text) { Text = text; } 
    public string Text { get; private set; } 
} 

public class PlaceHolderPart : PagePart 
{ 
    public PlaceHolderPart(PlaceHolderType type) { Type = type; } 
    public PlaceHolderType Type { get; private set; } 
} 

public enum PlaceHolderType { Title, Head, Main } 

分裂:

class PlaceHolderInfo 
{ 
    public PlaceHolderInfo(PlaceHolderType type, Regex splitter) 
    { 
     Type = type; 
     Splitter = splitter; 
    } 

    public PlaceHolderType Type { get; private set; } 
    public Regex Splitter { get; private set; } 
} 

private static readonly List<PlaceHolderInfo> PlaceHolderInfos = new List<PlaceHolderInfo> 
    { 
     new PlaceHolderInfo(PlaceHolderType.Title, new Regex(TitleString)), 
     new PlaceHolderInfo(PlaceHolderType.Head, new Regex(HeadString)), 
     new PlaceHolderInfo(PlaceHolderType.Main, new Regex(MainString)), 
    }; 

private static List<PagePart> SplitPage(string html) 
{ 
    var parts = new List<PagePart>(new PagePart[] { new LiteralPart(html) }); 
    foreach (PlaceHolderInfo info in placeHolderInfos) 
    { 
     var newParts = new List<PagePart>(); 
     foreach (PagePart part in parts) 
     { 
      if (part is PlaceHolderPart) 
      { 
       newParts.Add(part); 
      } 
      else 
      { 
       var literalPart = (LiteralPart)part; 
       // Note about Regex.Split: if match is found in beginning or end of string, an empty string is returned in corresponding end of returned array. 
       string[] split = info.Splitter.Split(literalPart.Text); 
       for (int i = 0; i < split.Length; i++) 
       { 
        newParts.Add(new LiteralPart(split[i])); 
        if (i + 1 < split.Length) // If result of Split returned more than one string, it means there was a match and we insert the placeholder between each string 
         newParts.Add(new PlaceHolderPart(info.Type)); 
       } 
      } 
     } 
     parts = newParts; 
    } 
    return parts; 
} 

請注意,此解決方案很容易擴展到更多佔位符(麪包屑,菜單,您的名稱)。它不會假定模板中佔位符的順序或其存在的順序。

編輯1: 我原來從Render方法調用DoScraping。事實證明,這是有問題的,因爲它重新編制了Web表單中的控件名稱(例如ctl00 $ MainContentPlaceHolder $ RequestingRepeater $ ctl01 $ ctl01)。它搞砸了數字到OnCommand在中繼器內的按鈕停止工作的點。控件的重新排序必須儘可能早地發生,以避免這種情況,所以現在已將它移動到Init

編輯2: 某些頁面使用Page.Header生成樣式和腳本標記。爲了支持這個功能,我添加了一些黑客來保留原始<head>標記並在渲染時插入生成的內容。