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

flickr 拼写扩展控件 for ASP.NET Atlas

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (13投票s)

2006年5月27日

CPOL

12分钟阅读

viewsIcon

73014

downloadIcon

508

一个 ASP.NET Atlas 服务器扩展控件,它将控件中指定的文本转换为 flickr 中的图像。

Sample image

引言

不久前,我偶然发现了 Spell with flickr 网页。您可以在该网页中输入一些文本,然后使用 flickr 的 oneletteronedigit 组中的图像来拼写该文本。该网页使用 PHP。我当时正在尝试使用 Atlas 中的桥接概念来开发一个混搭应用程序。在我实际的混搭应用程序中,我需要类似 Spell with flickr 网站的功能。结果就是这个扩展控件,它允许您将 HTML 元素内的文本显示为来自 oneletteronedigit 组的、代表字母的随机图像。在详细介绍扩展控件之前,让我们先了解一下 Atlas 扩展控件的概念。

扩展 ASP.NET 服务器控件

Atlas 引入了扩展控件的概念,它允许您为现有的 ASP.NET 控件添加丰富的功能。Atlas 扩展控件的一个示例是 AutoCompleteExtender,它为 ASP.NET TextBox 服务器控件添加了服务器辅助的自动完成支持。例如,您通常通过页面中的以下声明性标记在 ASP.NET 网页中使用 ASP.NET TextBox

<asp:TextBox ID="Textbox1" runat="server"></asp:TextBox>

通过添加 Atlas AutoCompleteExtender 控件,您可以扩展此 ASP.NET 服务器控件,使其提供类似 Google Suggest 的自动完成支持,如下所示:

<atlas:AutoCompleteExtender ID="Extender1" runat="server">
   <atlas:AutoCompleteProperties TargetControlID="TextBox1" ... />
</atlas:AutoCompleteExtender>

扩展控件定义有两个方面:

  1. 扩展控件标签:atlas:AutoCompleteExtender,您可以在其中指定 runat 属性和 ID 属性。
  2. 一个或多个扩展控件属性元素,用于指定需要扩展的目标控件以及特定于扩展程序的任何其他附加属性。

扩展控件使用 Atlas 中的客户端行为概念。Atlas 行为在概念上类似于 DHTML 行为,它使用 JavaScript 和 DOM 为现有的 DHTML 元素添加丰富的功能。通常,行为通过使用 Atlas XML-Script 指定。服务器上的 Atlas 扩展控件为行为生成 XML-Script。现在我们对扩展控件有了初步的了解,让我们来看看如何使用 flickrSpell 扩展控件。

使用 flickrSpell 扩展控件

Atlas 项目中使用 flickrSpell 扩展控件的步骤如下:

  1. 构建 FlickrSpell 项目,这将生成 FlickrSpell.dll 程序集。
  2. FlickrSpell.dllMicrosoft.AtlasControlExtender.dll 复制到您网站的 bin 目录。
  3. 在您的网页中,注册要使用的控件的标签前缀,如下所示:
    <%@ Register Assembly="FlickrSpell" TagPrefix="flickrSpell" 
                                        Namespace="FlickrSpell" %>
  4. FlickrBridge.asbxFlickrBridge.asbx.cs 文件复制到您的网站。这些是 Atlas 桥接文件,它们将 flickr 服务暴露给 JavaScript 代码。
  5. 需要扩展的控件应该是服务器控件,即,它应该将 runat 属性设置为 server。例如,以下代码片段声明了一个 h1 服务器控件:
    <h1 id="Test" runat="server">The CodeProject</h1>
  6. 要扩展此服务器控件,请添加扩展控件,如下所示:

    <flickrSpell:FlickrSpellExtender ID="FE1" runat="server" >
        <flickrSpell:FlickrSpellProperties TargetControlID="test" />
    </flickrSpell:FlickrSpellExtender>
  7. 从 flickr 获取 API 密钥。有关更多详细信息,请查看 Flickr Services 网站
  8. 获取 API 密钥后,编辑您网站的 web.config 文件,并将密钥添加到 appsettings 部分:
    <add key="FlickrAPIKey" value="Your API key"/>

当您使用 Web 浏览器浏览网页时,您将看到文本“The CodeProject”被图像替换。转换可能很慢,因为 flickr 网站有时会非常慢。在本文的其余部分,我们将了解该控件的工作原理。我们将从查看 flickr 网站服务开始。

访问 flickr 上的图像

flickr 是一个流行的照片分享和存储网站。flickr 网站还允许您标记和分组照片。本文中感兴趣的 flickr 网站上有两个组:oneletteronedigit。flickr 中的 oneletter 组包含描绘不同字母的照片。onedigit 组对数字执行相同操作。每个字母和数字都标记为自身,少数例外。例如,B 标记为文本“b”,C 标记为文本“c”,依此类推。两个例外是字母“A”,标记为文本“aa”,以及字母 I,标记为文本“ii”。因此,使用适当的标签搜索组中的照片池将得到特定字母或数字的适当照片列表。

flickr API 提供了一种让外部应用程序访问 flickr 网站的方式。API 有许多功能,但本文中使用的功能是从组中获取图像。flickr API 可以通过三种方式调用:使用 REST、使用 SOAP 和使用 XML-RPC。我们将使用 REST。

访问 flickr 的 REST 方法是通过 URL:http://www.flickr.com/services/rest/。您可以向此 URL 提供不同的查询字符串参数来访问不同的功能。要访问的 flickr 网站的功能通过 method 参数指示。用于列出组照片池中照片的方法称为 flickr.groups.pools.getPhotos。以下是此方法接受的参数(文本摘自 flickr 网站):

api_key (必需)
您的 API 应用程序密钥。
group_id (必需)
您希望获取照片列表的组的 ID。
tags (可选)
用于过滤照片池的标签。目前,一次只支持一个标签。
user_id (可选)
用户的 NSID。指定此参数将仅检索用户贡献给组照片池的照片。
extras (可选)
用于获取每条返回记录的额外信息的逗号分隔列表。目前,支持的字段包括:licensedate_uploaddate_takenowner_nameicon_serveroriginal_formatlast_update
per_page (可选)
每页返回的照片数。如果省略此参数,则默认为 100。允许的最大值为 500。
page (可选)
返回结果的页码。如果省略此参数,则默认为 1。

oneletter 照片池的组 ID 为 27034531@N00onedigit 组的组 ID 为 54718308@N00。访问 flickr 上字母 A 的照片的 HTTP GET 请求如下所示:

http://www.flickr.com/services/rest?api_key=XYZ&
  method=flickr.groups.pools.getPhotos
  &group_id=27034531@N00&tag=aa&per_page=100

返回的响应形式如下:

<rsp stat="ok">
    <photos page="1" pages="1" perpage="1" total="1">
    <photo id="2645" owner="12037949754@N01" title="36679_o"
    secret="a9f4a06091" server="2"
    ispublic="1" isfriend="0" isfamily="0"
    /> 
</photos>
</rsp>

下一步是构造照片的 URL。flickr 返回不同尺寸的图像。小型图像(75px x 75px)的 URL 格式如下:http://static.flickr.com/{server}/{photo id}_{secret}_s.jpgserverphoto_idsecret 都是 XML 响应中的值。这完成了访问图像的过程。让我们看看如何在 ASP.NET Atlas 中编写此过程的代码。

桥接到 flickr

Bridges(桥接)于 Atlas 的三月 CTP 版本引入,它允许客户端脚本访问与托管网页不同域的 Web 服务。AJAX 开发中的一个问题是访问跨域服务,因为浏览器(在默认配置下)不允许通过 XMLHttpRequest 对象进行跨域访问。ASP.NET Atlas 通过将 JavaScript 的跨域 Web 服务调用路由到托管 Web 服务器来解决此问题。从浏览器的角度来看,该调用是针对托管 Web 服务器的,因此从安全角度来看是安全的。托管 Web 服务器上的桥接程序会向其他域上的 Web 服务发出实际调用。此方法涉及两次网络往返:一次从客户端到托管 Web 服务器,另一次从托管 Web 服务器到提供其他域上 Web 服务的服务器。在此,可以通过在托管 Web 服务器上提供缓存机制来提高性能。Web 服务器上的缓存的一个优点是它可以被多个客户端共享。

ASP.NET Atlas 桥接程序是一个扩展名为 asbx 的 XML 文件。桥接程序可以有一个代码隐藏文件。FlickrBridge.asbx 文件的内容如下所示:

<bridge 
   namespace="FlickrSpell" className="FlickrBridge" 
   partialClassFile="~/FlickrBridge.asbx.cs">
  <proxy type="Microsoft.Web.Services.BridgeRestProxy" 
          serviceUrl="% appsettings : FlickrRESTEndPoint %" />
    <method name="getLetterImages">
    <input>
      <parameter name="api_key" value="% appsettings : FlickrAPIKey %" 
                serverOnly="true"/>
      <parameter name="method" value="flickr.groups.pools.getPhotos" 
                serverOnly="true"/>
      <parameter name="group_id" value="27034531@N00" 
                 serveronly="true"/>
      <parameter name="letter" serverName="tags" />
      <parameter name="extras" serverOnly="true" value="owner_name" />
      <parameter name="per_page" value="25" />
      <parameter name="page" value="1" />
    </input>
    <caching>
      <cache type="Microsoft.Web.Services.BridgeCache" />
    </caching>
    <transforms>
      <transform type="Microsoft.Web.Services.XPathBridgeTransformer">
        <data>
          <attribute name="selector" value="/rsp/photos/photo" />
          <dictionary name="selectedNodes">
            <item name="id" value="@id" />
            <item name="owner" value="@ownername" />
            <item name="title" value="@title" />
            <item name="secret" value="@secret" />
            <item name="server" value="@server" />
          </dictionary>
        </data>
      </transform>
    </transforms>
  </method>
</bridge>

桥接文件由一个自定义生成提供程序(在配置文件中指定)转换为 C# 代码。C# 代码由 ASP.NET 运行时编译成程序集。让我们来分析桥接文件的代码。

  1. 桥接元素中的 namespaceclassName 属性指定了桥接生成提供程序将创建的类型的命名空间和名称。
  2. partialClassFile 属性指定了提供桥接类型部分实现的文件的名称。此类应派生自 Microsoft.Web.Services.BridgeHandler
  3. proxy 元素中的 type 属性指定了桥接程序访问 Web 服务的方法。Microsoft.Web.Services.BridgeRestProxy 指示将使用 REST 来访问 Web 服务。如果您使用的是 SOAP,则可以将 wsdl.exe 生成的代理类的名称作为此属性的值。
  4. serviceURL 指定了 Web 服务可用的 URL。字符串 "% appsettings : FlickrRESTEndPoint %" 表明该值应从 web.config 文件中的 appsettings 配置部分获取。以下是 web.config 文件的一个片段:
    <appSettings>
     <add key="FlickrRESTEndPoint" value="http://www.flickr.com/services/rest/"/>
            ...
    </appSettings>
  5. input 元素下的 parameter 元素指定了不同的查询字符串参数。namevalue 属性不言自明。serverOnly 属性的值 true 指定了该特定参数的值只能从服务器端代码中指定,而不能从 JavaScript 代码中指定。XML 文件中指定的 value 属性用于指示参数的默认值。
  6. caching 部分指示了如何缓存响应。Microsoft.Web.Services.BridgeCache 类型提供了一个简单的缓存机制。可以为缓存指定任何实现 Microsoft.Web.Services.IBridgeRequestCache 的类型。
  7. transoforms 部分指定了在将输出返回给客户端之前如何进行转换。桥接程序的输出以 JSON 格式返回给客户端。可以指定多个转换。转换是实现 Microsoft.Web.Services.IBridgeResponseTransformer 接口的类型。
  8. 在提供的代码片段中,使用了 Microsoft.Web.Services.XPathBridgeTransformer 类型的转换。XPath 桥接转换器从 XML 响应中提取项。一个返回节点列表的 XPath 表达式在名为 attribute 的元素中指定,该元素有一个名为 name 的属性,其值为 selector。在我们的代码列表中,XPath 表达式 /rsp/photos/photo 选择响应中的所有 photo 元素。
  9. 对于通过匹配 XPath 表达式得到的每个节点(如第 8 点所述),可以进一步提取值以生成键值对列表。这是在 dictionary 部分完成的。item 元素指定字典中的条目。name 属性指定条目的名称,value 属性指定条目的值。此字典被转换为 JavaScript 对象。

至此,我们完成了对桥接文件的讨论。现在,让我们分析桥接文件的代码隐藏文件。

namespace FlickrSpell
{
    public partial class FlickrBridge : BridgeHandler
    {
        public override void TransformRequest()
        {
            string letter = this.BridgeRequest.Args["letter"] as string;

            string replacement = letter;

            switch (letter.ToLower())
            {
                //The special case for a and i where

                //the tag to be passed to flickr is different

                case "a":
                    replacement = "aa";
                    break;
                case "i":
                    replacement = "ii";
                    break;
                //If a digit is specified

                //use the onedigit group id    

                case "0":
                case "1": 
                case "2":
                case "3":
                case "4":
                case "5":
                case "6":
                case "7":
                case "8":
                case "9":
                    this.BridgeRequest.Args["group_id"] = "54718308@N00";
                    break;
            };
            
            this.BridgeRequest.Args["letter"] = replacement;
            base.TransformRequest();
        }

        public override void TransformResponse()
        {
            string responseXML = this.ServiceResponse.Response as string;
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(responseXML);
            
            //Check for errors returned by flickr

            string responseStatus = doc.SelectSingleNode("/rsp/@stat").Value;

            if (string.Compare(responseStatus, "ok", true) != 0)
            {
                throw new Exception(doc.SelectSingleNode("/rsp/err/@msg").Value);
            }

            base.TransformResponse();
        }
    }
}

桥接 asbx 文件和代码隐藏类都定义了名为 FlickrBridge 的类。该类派生自 BridgeHandler,其中包含多个可重写的方法。如代码清单所示,我们重写的两个方法是 TransformRequestTranformResponse

TransformRequest 函数在将客户端 JavaScript 代码发送的请求发送到 Web 服务之前对其进行转换;通常,TransformRequest 的实现会添加或修改要发送到 Web 服务的参数。这是可以为仅服务器参数提供值的地方之一。在上面的代码清单中,我们做了两件重要的事情:

  1. 桥接方法请求中的 letter 参数是 flickr 照片标签。我们之前已经看到,代表“A”的照片的标签实际上是“aa”,代表“I”的照片的标签是“ii”;因此,我们修改请求参数值以使用适当的标签值。这样做是为了简化客户端脚本。
  2. 如果请求的是数字,则将组 ID 的值(其默认值在 asbx 文件中指定)更改为 onedigit 组的组 ID。

我们已经了解了请求是如何修改的,现在让我们看看在 TransformResponse 方法中做了什么。在 TransformResponse 方法中,我们放置了用于检查 flickr 返回错误的通用代码。flickr 通过将 stat 属性的值设置为“ok”来指示成功;任何其他值都表示失败。如果发生错误,flickr 返回的 XML 将在根 rsp 元素内包含一个 err 元素。err 元素的两个属性 msgcode 提供了有关错误的更多信息。在 TransformResponse 方法中,我们检查错误并将 flickr 错误转换为 Exception。这完成了服务器端代码的详细信息,现在让我们分析客户端代码。

客户端代码

考虑以下 HTML:

<h1>The CodeProject</h1>

这个简单的 HTML 有一个 h1 的 DOM 元素节点,其中包含文本“The CodeProject”的 DOM 文本节点。我们可以将文本节点分解为每个字符的 img 元素。另一种选择是将文本分解为 span 元素并设置每个元素的背景图像。第一种方法的缺点是文本会丢失。第二种方法的缺点是图像无法调整大小。我们将使用一种混合方法。您可以使用 AtlasControlToolkit 来生成扩展控件的入门代码。AtlasControlToolkit 有一个向导可以生成扩展控件项目。您可以从这个 链接 下载 AtlasControlToolkit。

FlickrSpellExtender.FlickrSpellExtenderBehavior = function() {
  FlickrSpellExtender.FlickrSpellExtenderBehavior.initializeBase(this);
       
  this.initialize = function() {
    FlickrSpellExtender.FlickrSpellExtenderBehavior.callBaseMethod(this, 
                           'initialize');
        
    if (!this.control)
       return;
        
    Sys.Net.WebRequestManager.set_enableBatching(true);
        
    var children = this.control.element.childNodes;
      
    for(var i = 0; i < children.length; i++)
    {
        if(children[i].nodeType == 3)
        {
           replaceTextNode(children[i]);
        }
    }
        
    Sys.Net.WebRequestManager.set_enableBatching(false);
  }
    
  function replaceTextNode(node)
  {
     var text = node.data.trim();
     var list = document.createElement("span");
     list.className = "flickr-text";
     list.title = text;
        
     var currentWord = null;
        
     for(var i = 0; i < text.length; i++)
     {
         //TODO: Allow for word breaks            

         var listElem = document.createElement("span");
         listElem.innerText = text.charAt(i);
         list.appendChild(listElem);
         listElemToFlickrImage(listElem);    
     }
        
     node.parentNode.replaceChild(list, node);
  }
    
  function listElemToFlickrImage(listElem)
  {
     var letter = listElem.innerText.charAt(0).toUpperCase();
     
     if (letter >= "A" && letter <="Z" || 
        (letter >= "0" && letter <= "9"))
     {
         FlickrSpellExtender.FlickrBridge.getLetterImages({"letter" : letter}, 
             Function.createDelegate(this, onWebServiceCompleted), 
             null, 
             null, 
             null, 
             listElem);
      }
   }
       
   function onWebServiceCompleted(results, reponse, listElem)
   {
      if (results.length < 1)
         return;
        
      var img = document.createElement("img");
      var i = Math.round(Math.random()*(results.length - 1));

      img.onload = function()
      {
         listElem.insertBefore(img, listElem.firstChild);
         img.title = results[i].title + "\nOwner: " + results[i].owner;
      }

      img.src = String.format("http://static.flickr.com/{0}/{1}_{2}_s.jpg", 
               results[i].server, 
                  results[i].id, 
                  results[i].secret);
   }

以下是客户端代码的工作原理:

  1. 元素的文本节点被分解为每个字符的 span 元素。
  2. 对于每个 span 元素,调用 Web 服务来查找图像的 URL。
  3. Web 服务返回后,会创建一个图像元素来保存字母或数字的图像。
  4. 图像元素的 onload 事件被设置为一个添加图像元素到 span 元素的功能。
  5. 图像的 src 属性被设置为触发异步加载。注意,我们是在设置 onload 事件处理程序之后执行此操作的。有时,图像会随着 src 属性的设置而立即加载。当图像已在缓存中时,就会发生这种情况。

另一点需要注意的功能是批量处理。我们将所有 Web 服务调用进行批量处理,以减少网络往返次数。

结论和未来增强

我正在将此控件用作一个更大应用程序的一部分。应用程序准备好后,我将发布它。请就如何改进此控件或为其添加功能提出建议。

flickr 拼写扩展控件 for ASP.NET Atlas - CodeProject - 代码之家
© . All rights reserved.