65.9K
CodeProject 正在变化。 阅读更多。
Home

端到端真实世界 BlackBerry 应用程序,第 6 部分

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2009年4月16日

CPOL

4分钟阅读

viewsIcon

31872

downloadIcon

371

如何创建真实世界的 BlackBerry 应用程序。

引言

在本系列的第六篇端到端 BlackBerry 应用程序演练中,我将涵盖以下主题:

  • 创建 .NET HTTP Handler,用于发送和接收来自手持设备应用程序的信息
  • 从我们的 HTTP Handler 调用服务器端业务逻辑服务
  • 从手持设备应用程序发起请求并接收数据

虽然本系列中 之前的文章 讨论了如何创建将在手持设备上安装的 Java 应用程序,但今天我将处理服务器端模块。让我们再次审视我们的基本构件:

服务器端模块包括一个 .NET HTTP Handler,一组包含业务逻辑和数据访问代码的类库,以及一个将作为我们知识库应用程序文章存储库的 SQL Server 数据库。

创建 HTTP Handler

HTTP Handler 的任务是从手持设备发起的 HTTP 请求中提取信息,确定需要执行的操作类型,并要求业务逻辑层执行这些操作。它还将业务逻辑产生的結果附加到 HTTP 响应中,并将它们发送到手持设备应用程序。

为了让手持设备应用程序和 Handler 能够相互通信,手持设备应用程序可能发送的所有命令都需要在 Handler 中进行定义,就像它们在手持设备中定义一样。

 // The commands the handheld can send us.
private const int CMD_SEARCH_ARTICLES = 1;
private const int CMD_GET_ARTICLES_BY_TAG = 2;
private const int CMD_GET_TAG_COUNTS = 3;

标识每个请求参数的键也需要进行定义。

// The request keys.
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)) 
{
    // Bail if we don't receive a numeric command.
    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 Handler 中,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<IArticle> 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;
}
private string GetArticlesForTag(string tag)
{
    Debug.Assert(tag != null);
    if (null == tag)
    {
        ExceptionPolicy.HandleException(
            new ArgumentNullException("tag"),Shared.KB_EXCEPTION_PLCY);
    }
 
    string result = string.Empty; 
    try
    {
        List<IArticle> articles = articlesServer.GetArticlesForTag(tag);
        if (null == articles) return string.Empty;
        result = SerializeArticlesList(articles);
    }
    catch (Exception ex)
    {
        ExceptionPolicy.HandleException(ex, Shared.KB_EXCEPTION_PLCY);
    }
    return result;
}
private string GetTagCounts()
{
    StringBuilder sb = new StringBuilder(""); 
    try
    {  
        Dictionary<string, int> tagCounts = articlesServer.GetTagCounts();
        if (null != tagCounts && tagCounts.Count > 0)
        {
            foreach (KeyValuePair<string, int> tagStat in tagCounts)
            {
                sb.Append(string.Format("{0}^~^{1}~^~", 
                          tagStat.Key, tagStat.Value));  
            }
        }
    }
    catch (Exception ex)
    {
        ExceptionPolicy.HandleException(ex, Shared.KB_EXCEPTION_PLCY);
    }
    return sb.ToString();
}

在业务逻辑结果传递给 Handler 后,我需要根据编写手持设备代码时建立的格式约定对结果进行序列化。例如,这个例程会将文章列表转换为一个字符串,该字符串将作为 HTTP 响应的一部分发送到手持设备。

private string SerializeArticlesList(List<IArticle> 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)
    {
        // Decode the html characters in the body of the article.
        body = HttpUtility.HtmlDecode(HttpUtility.UrlDecode(article.Body));
        // Strip the html characters
        // (We're using a RichTextField on the handheld,
        // which isn't capable yet of displaying 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 Handler。我将做的第一件事是停止使用 MockHTTPTransport 实例,并开始使用 KbHTTPTransport。虽然 MockHTTPTransport 只是模拟 HTTP 请求以允许在设备上测试所有渲染逻辑,但 KbHTTPTransport 才是真正的实现。这是我在 Articles Screen 或 Tags Screen 中使用它的方式:

private void sendHttpRequest(String[] keys, String[] values) {   
   articlesList.set(null);  // Show "No Articles" while we download.
   String url = DataStore.getAppServerUrl();
   // Make sure we can create an http request.
   // If the app. server URL has not been set, we cannot make any http requests.
    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 模拟器来测试带有真实数据的应用程序。启动两个模拟器后,我将检查 Options Screen,确保我指向了正确的 HTTP Handler URL。

image2.gif

回到 Main Screen,我选择“浏览标签”。

image3.gif

在这里,我从 Handler 中获取到了真实数据。

image4.gif

选择一个标签应该会返回一些文章。

image5.gif

选择一篇文章应该会显示其内容。

image6.gif

结论

我希望你觉得这一系列的帖子很有帮助。我认为这个应用程序已经达到了一个合理的质量水平,你可以将其模块作为自己项目的 foundational。虽然我将转向其他主题,但我愿意回到工作中或就你感兴趣的任何领域进行更多讨论。请告诉我你的想法。

历史

以下是本系列前几篇文章的链接:

© . All rights reserved.