个人空间的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" /> 等等。