[项目总结-原创]InternetRadio项目Ajax技术方案选型
1 综述
InternetRadio项目是一个基于Web2.0理念的面向大众的音乐共享站点,其大量使用了Ajax功能来提高用户的体验效果,并提高数据传输的效率,典型的比如Autosuggest、添加音乐评论等。为了便于相关代码的集中控制,InternetRadio项目从底层封装、Ajax框架选型、代码管理等方面加以控制。
Ajax框架选型:Prototype提供Ajax封装,Script.aculo.us提供UI封装。
代码集中管理:在App_Code/BusinessHandler.cs中统一控制Ajax内容的输出。
Server端底层实现:利用C#的反射机制,实现Ajax客户端对Server端API的调用。
1.1 Ajax和UI框架选型
InternetRadio选用Prototype(http://www.prototypejs.org/)作为底层Ajax底层框架,包括Ajax API封装和 Javascript扩展;另外选择Script.aculo.us(http://script.aculo.us/)作为UI框架,利用其包括Autosuggest等在内的UI特性。
另外,作为辅助,增加Javascript/AjaxPublic.js进一步封装Prototype API,提供一系列方法简化Ajax客户端调用,比如PreparePostData()、AjaxGetFromUrl()、AjaxGetFromUrlSynchronous()等。
1.2 基于反射的Ajax远程调用实现
与Ajax.NET Professional(http://www.ajaxpro.info/)类似,InternetRadio项目也支持Ajax客户端对Server端API的直接调用,比如:
function Init() {
new Ajax.Autocompleter(
'<%=KeywordTextBox.ClientID %>',
'AutoSuggestion',
'../BusinessHandler/AutosuggestForKeywordSearch.ajax',
{
tokens:",",
paramName:"ajax_keyword",
callback: AutoSuggestionCallback
}
);
}
在上述代码中,Ajax直接调用Server端BusinessHandler类的AutosuggestForKeywordSearch方法。Suryani.Ajax Project提供了上述调用的支持和实现。
提供Suryani.Ajax.Service.ServiceManager.Register(string package, Type serviceType)方法,用于注册要开放给客户端Ajax调用的类。ServiceManager将所注册的类保存在全局变量modules中。
开放Suryani.Ajax.Utility.AjaxConfiguration.Register(Dictionary<string, Type> source)供Global.asax调用,用于在Website启动时向Server注册全部要开放给客户端调用的类。AjaxConfiguration.Register()方法调用ServiceManager.Register()方法完成所有类的注册。
增加Suryani.Ajax.Service.AjaxMethodAttribute自定义标签类,用于标识要开放给客户端调用的类方法。Suryani.Ajax.Service.ServiceManager.Register()方法将遍历所注册类的全部方法,将附加AjaxMethod自定义标签的所有方法保存在modules全局变量中,以备客户端Ajax调用。
提供Suryani.Ajax.Service.AjaxHttpHandler自定义HttpHandler类,拦截来自客户端的所有Ajax 请求(该请求的URL以".ajax"结尾),解析URL,利用反射的原理从全局变量modules中获取已注册的类和方法,并将执行结果输出到客户端。
初始化并注册类
Ajax远程调用Server端API的过程
1.3 Ajax代码集中管理
由于InternetRadio项目中大量用到Ajax功能,客户端Ajax可以直接调用Server端的API,而且每一个Ajax请求都需要Server端根据请求组织要交付Browser端的内容。为了对这些代码集中管理,我们在App_Code中增加了BusinessHandler类,作为Browser端与Server端的中介,负责请求的响应和内容的组织。而且整个项目中,只开放BusinessHandler供客户端直接调用调用。比如:
/// <summary>
/// 根据标签关键词搜索对应的标签
/// </summary>
/// <param name="keyword"></param>
[AjaxMethod]
public string FindTagsByKeyword(string keyword)
{
StringBuilder builder = new StringBuilder();
try
{
IList<Tag> tags = TagManager.FindTagsByKeyword(keyword);
if (tags != null)
{
builder.Append("<ul>");
foreach (Tag tag in tags)
{
builder.Append(string.Format(CultureInfo.CurrentCulture, "<li>{0}</li>", tag.TagName));
}
builder.Append("</ul>");
}
}
catch { }
return builder.ToString();
}
1.4 典型应用
1.4.1 简单的Ajax应用:添加歌曲标签
URL:http://nyxm.vicp.net:5052/Music/Music.aspx?MusicId=26553
功能描述:为当前歌曲添加相应的自定义标签
程序主文件:UserControl/MusicInfoControl.ascx
代码流程:
单击"保存"按钮:SaveTagButtonOnClick()
function SaveTagButtonOnClick()
{
var tagName = $('<%= TagNameTextBox.ClientID %>').value;
var musicId=$('<%= MusicIdHiddenField.ClientID %>').value;
var url = "/BusinessHandler/AddTag.ajax?ajax_tagName=" + encodeURIComponent(tagName) + "&ajax_musicId="+musicId;
AjaxGetFromUrl(url,SaveTagCallBack);
$('<%= TagNameTextBox.ClientID %>').value="";
}
调用AjaxGetFromUrl()方法,通过Prototype向Server端发起Ajax请求。
AjaxHttpHandler拦截到Ajax请求,执行BusinessHandler.AddTag()方法,向Browser输出以<span>形式组织的标签列表:
/// <summary>
/// 添加歌曲的标签
/// </summary>
/// <param name="tagName"></param>
/// <param name="musicId"></param>
[AjaxMethod]
public string AddTag(string tagName, int musicId)
{
int userId = ContextAccessor.Current.UserId;
string createBy = ContextAccessor.Current.UserName;
TagManager.Add(tagName, createBy, musicId, userId);
IList<Tag> tags = TagManager.FindTagsByMusicId(musicId);
StringBuilder returnString = new StringBuilder();
foreach (Tag tag in tags)
{
returnString.Append("<span>");
returnString.Append(PageContentBuilder.BuildTagSearchLink(tag.TagName, tag.TagName));
returnString.Append("</span> \n");
}
return returnString.ToString();
}
回调函数SaveTagCallBack()检测到 Server端返回的标签列表,将其呈现在页面上:
function SaveTagCallBack(request)
{
$('TagListDiv').innerHTML = request.responseText;
}
1.4.2 结合Script.aculo.us的应用:Autosuggest
URL:http://nyxm.vicp.net:5052/Index.aspx
功能描述:根据关键词框的输入,自动提示补录。
程序主文件:UserControl/ QuickSearchControl.ascx
代码流程:
页面初始化:
当页面载入的时候,利用Script.aculo.us的Autocompleter控件实现对关键词输入框的监控。
function Init() {
new Ajax.Autocompleter(
'<%=KeywordTextBox.ClientID %>',
'AutoSuggestion',
'../BusinessHandler/AutosuggestForKeywordSearch.ajax',
{
tokens:",",
paramName:"ajax_keyword",
callback: AutoSuggestionCallback
}
);
}
Event.observe(window,'load',Init);
当用户在关键词输入框输入关键词时,Autocompleter控件向Server端发起Ajax请求,检索提示内容。AjaxHttpHandler拦截该请求,执行BusinessHandler.AutosuggestForKeywordSearch()方法:
/// <summary>
/// 根据关键词检索对应的音频
/// </summary>
/// <param name="searchType"></param>
/// <param name="keyword"></param>
/// <returns></returns>
[AjaxMethod]
public string AutosuggestForKeywordSearch(string searchType, string keyword)
{
if (searchType.Equals(WebConstants.Search_Player, StringComparison.OrdinalIgnoreCase))
{
return FindPlayersByKeyword(keyword);
}
else if (searchType.Equals(WebConstants.Search_Music, StringComparison.OrdinalIgnoreCase))
{
return FindMusicByKeyword(keyword);
}
else if (searchType.Equals(WebConstants.Search_Tag))
{
return FindTagsByKeyword(keyword);
}
return string.Empty;
}
Server端以无序列表<ul>的形式组织返回给Browser端的Autosuggest内容列表。
回调函数AutoSuggestionCallback()将Autosuggest内容呈现在页面上:
function AutoSuggestionCallback(obj) {
var searchType = "Player";
if($("<%=PlayerRadioButton.ClientID %>").checked == true) searchType = "Player";
else if($("<%=MusicRadioButton.ClientID %>").checked == true) searchType = "Music";
else if($("<%=TagRadioButton.ClientID %>").checked == true) searchType = "Tag";
return "ajax_searchType="+searchType+"&ajax_keyword="+ encodeURIComponent(obj.value);
}