服务器端模块包括一个.NETHTTP处理器、一组包含业务逻辑和数据访问代码的类库,以及一个作为知识库应用程序文章存储库的SQL Server数据库。
HTTP处理器的任务是从手持设备发出的HTTP请求中提取信息,确定需要执行的操作类型,并要求业务逻辑层执行这些操作。它还将业务逻辑层生成的结果附加到HTTP响应中,并将其发送到手持应用程序。
为了使手持应用程序和处理器能够相互通信,所有手持应用程序可能发送的命令都需要在处理器中定义,就像在手持设备中定义的那样:
private const int CMD_SEARCH_ARTICLES = 1;
private const int CMD_GET_ARTICLES_BY_TAG = 2;
private const int CMD_GET_TAG_COUNTS = 3;
每个请求参数的键也需要定义:
private const string KEY_COMMAND = "cmd";
private const string KEY_SEARCH_PHRASE = "sp";
private const string KEY_TAG = "tg";
KEY_SEARCH_PHRASE标识当请求搜索现有文章时携带搜索短语的请求参数。KEY_TAG标识当请求检索给定标签的文章时携带标签名称的参数。
确定要执行的操作是检查标记有KEY_COMMAND键的请求参数:
string commandString = request.Form[KEY_COMMAND];
if (!int.TryParse(commandString, out currentCommand)) {
// 如果没有收到数字命令,则退出。
return;
}
articlesServer = Shared.GetArticlesServer(context);
switch (currentCommand) {
case CMD_SEARCH_ARTICLES:
string searchPhrase = request.Params[KEY_SEARCH_PHRASE];
responseString = SearchArticles(searchPhrase);
break;
case CMD_GET_ARTICLES_BY_TAG:
string tag = request.Params[KEY_TAG];
responseString = GetArticlesForTag(tag);
break;
case CMD_GET_TAG_COUNTS:
responseString = GetTagCounts();
break;
default:
return;
}
今天不会详细介绍业务逻辑和数据访问层的构建,因为它们已经在文章《端到端ExtJS应用程序》系列中介绍过了。值得一提的是,这些层支持将文章存储在文件中或SQL Server数据库中。今天文章的代码下载默认使用基于文件的存储,但可以通过运行包含的SQL脚本来安装数据库,并通过更改Web应用程序的配置文件将业务层指向它。
在HTTP处理器中,SearchArticles()、GetArticlesForTag()和GetTagCounts()简单地将请求转发到业务逻辑层:
private string SearchArticles(string searchPhrase) {
Debug.Assert(searchPhrase != null);
if (null == searchPhrase) {
ExceptionPolicy.HandleException(new ArgumentNullException("searchPhrase"), Shared.KB_EXCEPTION_PLCY);
}
string result = string.Empty;
try {
List articles = articlesServer.GetArticlesForSearchPhrase(searchPhrase);
if (null == articles) return string.Empty;
result = SerializeArticlesList(articles);
}
catch (Exception ex) {
ExceptionPolicy.HandleException(ex, Shared.KB_EXCEPTION_PLCY);
}
return result;
}
在业务逻辑结果传递给处理器后,需要根据在编写手持代码时建立的格式约定来序列化结果。例如,这是将文章列表转换为字符串的例程,该字符串将作为HTTP响应的一部分发送到手持设备:
private string SerializeArticlesList(List articles) {
Debug.Assert(articles != null);
if (null == articles) {
ExceptionPolicy.HandleException(new ArgumentNullException("articles"), Shared.KB_EXCEPTION_PLCY);
}
StringBuilder sb = new StringBuilder("");
string body;
foreach (IArticle article in articles) {
// 解码文章正文中的HTML字符。
body = HttpUtility.HtmlDecode(HttpUtility.UrlDecode(article.Body));
// 删除HTML字符
// (使用的手持设备上的RichTextField尚不能显示HTML。)
body = Regex.Replace(body, @"<(.|\n)*?>", string.Empty);
sb.Append(string.Format("{0}^~^{1}^~^{2}^~^{3}^~^", article.Id.ToString(), article.Title, article.DateCreated.ToShortDateString(), article.Author));
sb.Append(string.Format("{0}^~^{1}~^~", article.Tags, body));
}
return sb.ToString();
}
是否注意到在上述代码中,还删除了文章正文中的HTML字符?必须这样做,因为在手持设备上用于显示文章正文的字段尚不能渲染HTML。
现在剩下的就是将数据发送到手持设备:
response.ContentType = "text/plain";
int contentLength = response.Output.Encoding.GetByteCount(responseString);
response.AppendHeader("content-length", contentLength.ToString());
response.Write(responseString);
response.End();
知道服务器端代码的介绍非常快。鼓励下载代码并查看所有细节。此外,阅读《端到端ExtJS应用程序》文章可以让更深入地了解业务逻辑和数据访问层。
让回到设备端代码,并准备将其连接到HTTP处理器。将做的第一件事是停止使用MockHTTPTransport实例,开始使用KbHTTPTransport。而MockHTTPTransport只是模拟HTTP请求,允许在设备上测试所有渲染逻辑,KbHTTPTransport是真正的交易。这是在文章屏幕或标签屏幕上的使用方式:
private void sendHttpRequest(String[] keys, String[] values) {
articlesList.set(null);
// 在下载时显示"No Articles"。
String url = DataStore.getAppServerUrl();
// 确保可以创建一个http请求。
// 如果应用程序服务器URL未设置,则无法进行任何http请求。
if (null == url || url.length() == 0) {
Dialog.alert(resources.getString(KnowledgeBaseResource.ERR_INVALID_APP_SERVER_URL));
return;
}
statusDlg.show();
byte[] requestContents = TransportUtils.createPostData(keys, values);
KbHTTPTransport transport = new KbHTTPTransport();
transport.setHTTPTransportListener(this);
transport.send(KbHTTPTransport.REQ_METHOD_POST, url, requestContents);
}
此时,所有部分都已就绪,将使用BlackBerry模拟器和MDS模拟器使用真实数据测试应用程序。在启动两个模拟器后,将检查选项屏幕,确保指向的是HTTP处理器的正确URL。