個人空間的OpenAPI與facebook非常接近(從需要求上就要做成一樣),所以我們在hi中也將會使用類擬於fbml這樣的自訂標籤(himl)。在對notifications.send介面的開發中需要傳入notification參數,這個參數是通知的內容,其實就是一些html和himl標籤混合在一起的字串,其中有一個需求:使用者傳進一個UserName參數要求我們把它變成這個使用者的真實姓名,例如:<hi:name id="billok" />。這裡就是一個himl自訂標籤了。我們要做的工作就是要解析類擬的自定標籤把它轉換成真實姓名。
架構還是有點複雜,為了表達清楚點,這裡不談很多細節,只列一下這個subject相關的內容。
首先,應用程式把notification參數傳入notifications.send介面後,會經過一點的流程把參數html傳進如下一個處理請求的基類的方法:
string ParseContent(string html){} 這個方法是使用者分析html參數把其中的himl標籤進行特殊處理。
protected virtual string ParseContent(string html)
{
//載入html到HtmlDocument
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(HttpUtility.HtmlDecode(html));
//遍曆第一層節點
foreach (HtmlNode node in doc.DocumentNode.ChildNodes)
{
HtmlUtil.ForEach(node);
}
return doc.DocumentNode.OuterHtml;
}
這裡我們使用了HtmlAgilityPack對html分析組件,首先把html字串加裁成HtmlDocument對象,這樣就會自動分析出文檔的dom樹結構,然後把我們通過遍曆第一層節點,使用HtmlUtil.ForEach處理第一層根節點,處理完後就把所有的html進行輸出。使用HtmlAgilityPack的好處是可以方便的對dom進行操作,可以刪除節點和替換節點,不過相信還有更好的類庫可以使用或者以後改為LINQ to xml進行處理。
我們還是詳細看一下ForEach方法的實現吧。
public static void ForEach(HtmlNode node)
{
if (node.NodeType != HtmlNodeType.Element)
{
return;
}
else
{
if (!node.HasChildNodes)
{
//分葉節點
HandleNode(node);
}
else
{
foreach (HtmlNode childNode in node.ChildNodes)
{
ForEach(childNode);
}
//子節點:有子與有父節點
HandleNode(node);
}
}
}
這裡實際上是一個遞迴操作,如果傳進的節點不是一個Element元素(例如:不帶標籤的純檔案,註解等),這樣的node我們是不需要處理的,所以就直接return終結迴圈就好了。對於Element節點的處理,就只分為有子節點和沒有子節點的情況就行,如果有子節點的話就再遍曆子節點並遞迴調用ForEach方法,目的就是把有Element處理一遍。你也發現了我們對節點的處理又被封裝到另一個方法中了HandleNode:
private static void HandleNode(HtmlNode node)
{
//分析節點
IHtmlTag iHtmlTag = ObjectFactory.CreateIHtmlTag(node.Name);
Dictionary<string, string> dict = new Dictionary<string, string>();
//dict.Add("HtmlNode_OuterHtml", node.OuterHtml);//可以傳入節點的全部html,以作進一步處理
foreach (HtmlAttribute attr in node.Attributes)
{
if (!dict.ContainsKey(attr.Name))
{
dict.Add(attr.Name, attr.Value);
}
}
HtmlTag htmlTag = iHtmlTag.Handle(dict);
//處理節點
switch (htmlTag.HandleType)
{
case HtmlTagHandleType.Remove:
if (node.ParentNode != null)
node.ParentNode.RemoveChild(node);
break;
case HtmlTagHandleType.Replace:
if (node.ParentNode != null)
{
HtmlNode newNode = HtmlNode.CreateNode(htmlTag.Html);
node.ParentNode.ReplaceChild(newNode, node);
}
break;
case HtmlTagHandleType.Unknown:
default:
//不做任務處理
break;
}
}
這個方法分為兩個步聚:分析節點和處理節點,並且利用了IOC模式。
IOC模式是系統架構的一部分,這裡就不詳細談了,你只要知道從ObjectFactory.CreateIHtmlTag方法可以得到標籤名(如:hi:name)所以指定的關於IHtmlTag介面的一個實現,如果沒有合符處理的實現就會使用一個預設實現(就是告訴"處理節點"步聚什麼都不用做)。對於IHtmlTag介面我們看一下定義:
public enum HtmlTagHandleType
{
Unknown,
Remove,
Replace,
}
public class HtmlTag
{
public HtmlTagHandleType HandleType { get; set; }
public string Html { get; set; }
public HtmlTag()
{
HandleType = HtmlTagHandleType.Unknown;
Html = string.Empty;
}
}
public interface IHtmlTag
{
HtmlTag Handle(IDictionary<string, string> attributes);
}
IHtmlTag介面只有一個方法
HtmlTag Handle(IDictionary<string, string> attributes); 接受一個節點屬性的索引值對字典,返回對這些索引值對進行處理的結果。
返回的HtmlTag可以指示出需要後續進行怎麼樣的操作(HtmlTagHandleType),以及處理完後的html
Okay! 對節點進行分析後得到HtmlTag,現在就可以進行節點處理的工作了。節點的處理方式以HtmlTagHandleType的返回為準,分別是Remove刪除節點,Replace替換節點,不作處理。
經過這些處理後,返加doc.DocumentNode.OuterHtml就可以得到處理後的html內容了。通過實現IHtmlTag介面,可以很方便的添加新的himl標籤對內容時行特殊處理了。並用標籤還可以任竟擴充屬性,例如:<hi:name id="billok,junbiaochen" target="abc" /> 等等。