每周源代码22- Digg, Flickr, Facebook, YouTube, Twitter, Live 服务,Google和Web 2.0 APIs 的C#,VB .NET库

[原文发表地址] The Weekly Source Code 22 - C# and VB .NET Libraries to Digg, Flickr, Facebook, YouTube, Twitter, Live Services, Google and other Web 2.0 APIs

[原文发表时间] 2008-03-26 04:02

有人最近给我发电子邮件说,在.NET中找不到足够多的范例来学习最新激增的“Web 2.0 APIs”。所以我琢磨着要列一张总表,一起看一些源码。我认为一个好的API封装包通常是不错的,但是由于这些API过于透明和基本,而且我们已经有LINQ to XML了,所以必要性不大。但我深知,当遇到"API"这个词时,还是会下意识的想到一个封装包。
需要提到的一点是,99.9%的API会调用
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
它们利用结果字符串在幕后工作。有些隐藏URL创建,有些使用XmlDocuments,有些使用XmlSerialization。如果您使用网络上的随便一个什么API, 那么就不要期望太高。你能看到不同的人对于他们心目中应该如何调用API的不同看法。有些人更喜欢速战速决,有些人则想得更透彻。
我会尽力比照它们之间的差距,但我希望你们记住,我们谈论的是摆布尖括号,而非其它的。你完全可以自己做。
所以,亲爱的读者,我现在向你们展示"The Weekly Source Code."一系列帖子中的第(22)个。
Digg
Digg(掘客)是一个由社区投票选出并有评论控制的汇聚大量新闻的站点 。它们的API是"REST",并有XML和JSON接口。
DiggApiNET是Digg API的一个.NET封装。它没有发布,所以你得拿它的源代码来用。最后一次更新是在2007年5月。还有一个在CodeProject中,名字比较“有创意”,叫digg API.NET
让我们来谈谈它的设计理念,然后看一下第一个库。这是从代码中抽选出来的一些代码段。该API构建了URL,并将访问结果加载到XmlDocument里,停顿片刻然后通过SelectNodes将值存入Digg特定的对象里。这些对象知道System.Xml的存在。
  1: private const string get_popular = "https://services.digg.com/stories/popular/comments/{0}"; 
  2:  
  3: public DiggComments GetPopular()
  4: { 
  5: return GetPopular(new Hashtable());
  6: }
  7: public DiggComments GetPopular(Hashtable args)
  8: { 
  9: string uri = String.Format(get_popular, HttpBuildUrl(args)); 
  10: return new DiggComments(Request(uri));
  11: }
  12: public DiggComments(XmlDocument xml_doc) : base(xml_doc, "events")
  13: { 
  14: _comments = new List<diggcomment>(); 
  15: if (xml_doc.SelectSingleNode("events") == null 
  16: || xml_doc.SelectSingleNode("events").SelectNodes("comment") == null) {
  17: throw new DiggApiException("XML response appears to be malformed, or contains unexpected data."); 
  18: }
  19: foreach (XmlNode node in xml_doc.SelectSingleNode("events").SelectNodes("comment")) {
  20: _comments.Add(new DiggComment(node)); 
  21: }
  22: }</diggcomment>
虽然这不算是完全“简洁”的方法,但却非常直观。使用SelectSingleNode和SelectNodes并不算太快,但我们关注的只是一小块数据,可能都低于100k.我可能会用XmlReader和XmlSerializer来处理,保不住还可能会用LINQ to XML. 我会创建一个服务(Service)来处理此协议,并让这些对象管的事情再少一些。
Facebook
Facebook有一个非常复杂和深入的API,并在.NET上有很多的支持。Nikhil对其进行了很好的诠释。你可以使用免费的Express Visual Studio版本来为Facebook做开发
下面有很多可用的:
有详细说明Facebook.NET资源库和Clarity Consulting's公司的Facebook开发工具包Jay LagorioVB.NET编写了一个不错的Facebook客户端库JD Conley已经发布了一个叫fbasync的Facebook资源库,其主要专注于异步实现
Nikhil的Facebook客户端API的构造非常好。为每个主要的Facebook服务都创建了单独服务,还为Facebook会话对象提供了上下文状态。请求被封装到FacebookRequest里,会提供了异步选项,这个想的非常周到。
这儿有个编辑过的(为简便起见)WinForm范例,可以允许您设置您的Facebook状态。我喜欢IsPermissionGranted调用,在有很多权限的情况下,这个方法非常简洁,聪明。
  1: public partial class StatusForm : Form { 
  2: 
  3: private const string _apiKey = "[Your API Key]"; 
  4: private const string _secret = "[Your Secret]"; 
  5: 
  6: private FacebookService _fbService; 
  7: private bool _loggingIn; 
  8: 
  9: private void LoadStatus() { 
  10: _nameLabel.Text = "Loading..."; 
  11: 
  12: User user = _fbService.Users.GetUser(null, "name,status"); 
  13: if (user != null) { 
  14: _nameLabel.Text = user.Name; 
  15: 
  16: _statusTextBox.Text = user.Status.Message; 
  17: _dateLabel.Text = user.Status.UpdateDate.ToLocalTime().ToString("g"); 
  18: } 
  19: 
  20: bool canSetStatus = _fbService.Permissions.IsPermissionGranted(Permission.SetStatus); 
  21: _permissionsLink.Visible = !canSetStatus; 
  22: _updateButton.Enabled = canSetStatus; 
  23: _statusTextBox.ReadOnly = !canSetStatus; 
  24: } 
  25: 
  26: protected override void OnActivated(EventArgs e) { 
  27: base.OnActivated(e); 
  28: 
  29: if ((_fbService == null) && (_loggingIn == false)) { 
  30: _loggingIn = true; 
  31: 
  32: try { 
  33: FacebookClientSession fbSession = new FacebookClientSession(_apiKey, _secret); 
  34: if (fbSession.Initialize(this)) { 
  35: _fbService = new FacebookService(fbSession); 
  36: LoadStatus(); 
  37: } 
  38: } 
  39: finally { 
  40: _loggingIn = false; 
  41: } 
  42: } 
  43: } 
  44: 
  45: private void OnUpdateButtonClick(object sender, EventArgs e) { 
  46: string text = _statusTextBox.Text.Trim(); 
  47: 
  48: _fbService.Users.SetStatus(text, /* includesVerb */ true); 
  49: LoadStatus(); 
  50: } 
  51: }
  52: }
有趣的是,Facebook的API还包括了自带的JsonReader和JsonWriter,而不是使用新的JsonSerializer,大概是因为lib是一年前写的缘故吧。
Windows Live服务
https://dev.live.com/有一大堆信息和带有源码的完整的示例应用程序,以及一个Live SDK 互动网站。例如,Live Contacts API,不幸的是,在通讯录(Contacts)的API中却找不到封装过尖括号(XML)的.NET范例,所以你可以按照你喜欢的方式解析。
Alpha SDK中提供的对象从一开始就集中在安全性和权限上。例如,在我以编程方式访问我的通讯录(Contacts)之前,我必须显示的允许访问,并选择一个允许访问的时间段。安全起见,我设定的安全访问时间是一天。
一旦你要检索一些数据,这是非常简单的事情,诸如https://cumulus.services.live.com/wlddemo@hotmail.com/LiveContacts这样的请求,会带给你:
  1: <LiveContacts> 
  2: <Owner> 
  3: <FirstName/> 
  4: <LastName/> 
  5: <WindowsLiveID/> 
  6: </Owner> 
  7: <Contacts> 
  8: <Contact> 
  9: <ID>{ContactID}</ID> 
  10: <WindowsLiveID>{Passport Member Name}</WindowsLiveID> 
  11: <Comment>comment here</Comment> 
  12: <Profiles/> 
  13: <Emails/> 
  14: <Phones/> 
  15: <Locations/> 
  16: </Contact> 
  17: </Contacts>
  18: </LiveContacts>

Live Search API 用SOAP协议,并且已经有了六种语言的范例, 包括C#, VB, Ruby, PHP, Python, and Java.

YouTube
YouTube有两个不同版本的API但原来的/旧版本已正式被弃用。因为它们已加入了Google,所以Youtube上的API全部都是Gdata风格的取代了原来的REST/XML-RPC API
有一个.NET库可以与GData XML格式交互,并可以非常简单的用C#来查询YouTube。你甚至可以像这位先生一样以编程的方式上传视频到YouTube.
这位朋友避开了GData的uber库,使用StringBuilder来构建GData有效载荷,这也行哦。:)
  1: private string GetHeader(string title, string description, Catagory catagory, 
  2: string keywords, string videoFileName)
  3: { 
  4: StringBuilder xml = new StringBuilder(); 
  5: xml.Append(boundary + lineTerm + "Content-Type: application/atom+xml; charset=UTF-8" + lineTerm + lineTerm); 
  6: xml.Append("<?xml version=\"1.0\"?><entry xmlns=\"https://www.w3.org/2005/Atom\" "); 
  7: xml.Append("xmlns:media=\"https://search.yahoo.com/mrss/\" xmlns:yt=\"https://gdata.youtube.com/schemas/2007\">"); 
  8: xml.AppendFormat("<media:group><media:title type=\"plain\">{0}</media:title>", title); 
  9: xml.AppendFormat("<media:description type=\"plain\">{0}</media:description>", description); 
  10: xml.AppendFormat("<media:category scheme=\"https://gdata.youtube.com/schemas/2007/categories.cat\">{0}</media:category>", catagory); 
  11: xml.AppendFormat("<media:keywords>{0}</media:keywords>", keywords); 
  12: xml.Append("</media:group></entry>" + lineTerm); 
  13: xml.Append(boundary + lineTerm + "Content-Type: video/*" + lineTerm + "Content-Transfer-Encoding: binary" + lineTerm + lineTerm); 
  14: return xml.ToString();
  15: }
GData
GData是谷歌通过XML 和HTTP传输数据的标准协议。Blogger, Google Calendar, Notebook, Spreadsheets, Documents, Picassa等都有GData端点。下面是从它们的网站上截下来的:

NET开发人员指南是为特定数据API而制定的。你可以在每个数据API的页面上找到

GData C# 客户端是由Google编写的,所以我对他们的代码很感兴趣,因为他们的面试过程富有传奇色彩。我猜他们个个都是17岁的博士。该代码是极其面向对象的,包含有10个文件夹之多的165个文件(还不包括单元测试和项目上的东西)。注释也非常好,但有趣的是,该注释并不是大多数MSFT(微软)程序员使用的标准XML注释,而是一种我不熟悉的另外的格式。

所有的API都很相似。这儿有个GData查询日期范围内的日历上事件的范例。

  1: static void DateRangeQuery(CalendarService service, DateTime startTime, DateTime endTime)
  2: { 
  3: EventQuery myQuery = new EventQuery(feedUri); 
  4: myQuery.StartTime = startTime; 
  5: myQuery.EndTime = endTime; 
  6: 
  7: EventFeed myResultsFeed = service.Query(myQuery) as EventFeed; 
  8: 
  9: Console.WriteLine("Matching events from {0} to {1}:", 
  10: startTime.ToShortDateString(), 
  11: endTime.ToShortDateString()); 
  12: Console.WriteLine(); 
  13: for (int i = 0; i < myResultsFeed.Entries.Count; i++) 
  14: { 
  15: Console.WriteLine(myResultsFeed.Entries[i].Title.Text); 
  16: } 
  17: Console.WriteLine();
  18: }

下面是用Picassa从一个特定的用户上下载所有照片的示例。GData里的所有东西都是一个"AtomEntry" ,而且有很多扩展项。您可以处理GData类型或使用特定的子类,比如PhotoQuery,或其他什么方法,来使事情变得更简单。

  1: private static void DownAlbum(string UserN, string AlbumN)
  2: { 
  3: string fileName; 
  4: Uri uriPath; 
  5: WebClient HttpClient = new WebClient(); 
  6: // Three important elements of PicasaWeb API are 
  7: // PhotoQuery, PicasaService and PicasaFeed 
  8: PhotoQuery query = new PhotoQuery(); 
  9: query.Uri = new Uri(PhotoQuery.CreatePicasaUri(UserN, AlbumN)); 
  10: PicasaService service = new PicasaService("Sams PicasaWeb Explorer"); 
  11: PicasaFeed feed = (PicasaFeed)service.Query(query); 
  12: 
  13: Directory.SetCurrentDirectory("c:\\"); 
  14: foreach (AtomEntry aentry in feed.Entries) 
  15: { 
  16: uriPath = new Uri(aentry.Content.Src.ToString()); 
  17: fileName = uriPic.LocalPath.Substring(uriPath.LocalPath.LastIndexOf('/')+1); 
  18: try { 
  19: Console.WriteLine("Downloading: " + fileName); 
  20: HttpClient.DownloadFile(aentry.Content.Src.ToString(), fileName); 
  21: Console.WriteLine("Download Complete"); 
  22: } 
  23: catch (WebException we) 
  24: { Console.WriteLine(we.Message); } 
  25: }
  26: }

当然了如果你喜欢的话你也可以使用标准的Systeml.Xml API。

GData扩展了Atom Pub协议。Atom Pub是被Astoria(ADO.NET数据扩展)使用的一种协议,而Astoria基本上可以通过”LINQ to REST” 来访问。”

Flickr
Flickr有一个很好的API,并且WackyLabsy有一个用C#编写的针对FlickrNET API库的CodePlex项目。这也被证实了可以在Compact Framework,Mono以及.NET 1.1以上的版本上使用。在该库上有一篇很好的Coding4Fun文章
  1: PhotosSearchOptions options = new PhotosSearchOptions();
  2: options.Tags = "blue,sky";
  3: options.Extras |= PhotoSearchExtras.DateTaken | PhotoSearchExtras.OriginalFormat;
  4: Photos photos = flickr.PhotosSearch(options);
该API非常易用。例如,这个可以搜索带有蓝色和天空标签的图片,并且确保它返回DateTaken和OriginalFormat属性。
PhotosSearch()方法带有几十个包含日期范围、分页和其他选项的重载。所有工作都是让GetResponse()通过GetResponseCache()来实现的。URL都是在一个方法中被构建的,而响应是通过XmlSerializer进行获取数据并反序列化的。该API是最接近于我做的方式的。非常务实,并且尽可能多的使用了底层库,这个并不是真正的可扩展或过度的面向对象,但它能干净利落地完成任务。
由于Flickr是数据密集型的,该库也包括了一个线程安全的PersistentCache用来存储所有的数据。我可能会用System.Web.Cache,因为它能存在于任何应用程序中,甚至可以在ASP.NET之外。然而,该缓存是持久化的,可以将庞大的数据块保存到可配置的位置。我觉得,这其实是个非常有用的类,可以考虑在这个lib之外使用。它将所有东西存储在超级“穷光蛋”数据库上,这基本上是一个序列化的blob哈希表,ala (gasp) OLE结构的存储结构。

WordPress和基于XML - RPC的博客

大多数博客都使用Blogger或MetaWeblog API,因为他们很容易通过.NET来调用。包括MSN SpacesDasBlogSubText等等。关于如何用C# 或 VB访问XML-RPC,在MSDN上有很多深入研究的范例

当你写帖子时,Windows Live WriterBlogJet会使用这些API来与博客进行交互,所以我现在用的就是.NET和XML-RPC ;)

这儿有个用VB.NET编写的使用了很棒的 XML-RPC.NET库的的一个非常简单的示例。这里有一个更完整的范例这是一个小型的博客客户端

DasBlog使用这个库做成了一个XML-RPC服务器。

在此示例中,"IWP"类型派生自XmlRpcProxy,并使用了分类结构。该库还负责处理所有的反序列化映射,比方说调用XML-RPC ,,感觉像是使用WebService,尽管XML - RPC是SOAP的前身,但并不是你正在使用的SOAP。

  1: Dim proxy As IWP = XmlRpcProxyGen.Create(Of IWP)()
  2: Dim args() As String = {“https://myblog.blogstogo.com”, _ 
  3: “username”, “password”}
  4: Dim categories() As categorycategories = proxy.getCategories(args)

你也可以使用WCF来与XML-RPC进行交互

Twitter
以前我也提到过Twitter。它有一个Twitter API,这至少是个比其网站更重要的重量级的东东。有一大堆的源代码,可以与Twitter进行交互。
去年,Alan Le在博客上写到了要创建一个围绕Twitter的API库的大胆设想Witty是面向Twitter的活跃开发中的WPF C#应用程序。你可以浏览源网址,参见他们的简单的TwitterLib
TwitterNet.cs是它的核心部分,其使用XmlDocuments创建对象,并实现了这些代码,我叫它“左手/右手”代码。那是因为你左边有一个对象,右边有其他的object/bag/pileOdata,然后你会用很多代码行来不停的去左侧,右侧,左侧,右侧。
(裁剪过)的示例如下:
  1: public UserCollection GetFriends(int userId) 
  2: { 
  3: UserCollection users = new UserCollection(); 
  4:  
  5: // Twitter expects https://twitter.com/statuses/friends/12345.xml 
  6: string requestURL = FriendsUrl + "/" + userId + Format; 
  7:  
  8: int friendsCount = 0; 
  9: 
  10: // Since the API docs state "Returns up to 100 of the authenticating user's friends", we need 
  11: // to use the page param and to fetch ALL of the users friends. We can find out how many pages 
  12: // we need by dividing the # of friends by 100 and rounding any remainder up. 
  13: // merging the responses from each request may be tricky. 
  14: if (currentLoggedInUser != null && currentLoggedInUser.Id == userId) 
  15: { 
  16: friendsCount = CurrentlyLoggedInUser.FollowingCount; 
  17: } 
  18: else 
  19: { 
  20: // need to make an extra call to twitter 
  21: User user = GetUser(userId); 
  22: friendsCount = user.FollowingCount; 
  23: } 
  24: 
  25: int numberOfPagesToFetch = (friendsCount / 100) + 1; 
  26:  
  27: string pageRequestUrl = requestURL; 
  28: 
  29: for (int count = 1; count <= numberOfPagesToFetch; count++) 
  30: { 
  31: pageRequestUrl = requestURL + "?page=" + count; 
  32: HttpWebRequest request = WebRequest.Create(pageRequestUrl) as HttpWebRequest; 
  33: request.Credentials = new NetworkCredential(username, password); 
  34: 
  35: try 
  36: { 
  37: using (HttpWebResponse response = request.GetResponse() as HttpWebResponse) 
  38: { 
  39: StreamReader reader = new StreamReader(response.GetResponseStream()); 
  40: XmlDocument doc = new XmlDocument(); 
  41: doc.Load(reader); 
  42: XmlNodeList nodes = doc.SelectNodes("/users/user"); 
  43: 
  44: foreach (XmlNode node in nodes) 
  45: { 
  46: User user = new User(); 
  47: user.Id = int.Parse(node.SelectSingleNode("id").InnerText); 
  48: user.Name = node.SelectSingleNode("name").InnerText; 
  49: user.ScreenName = node.SelectSingleNode("screen_name").InnerText; 
  50: user.ImageUrl = node.SelectSingleNode("profile_image_url").InnerText; 
  51: user.SiteUrl = node.SelectSingleNode("url").InnerText; 
  52: user.Location = node.SelectSingleNode("location").InnerText; 
  53: user.Description = node.SelectSingleNode("description").InnerText; 
  54:  
  55: users.Add(user); 
  56: } 
  57: } 
  58: } 
  59: catch (WebException webExcp) 
  60: { 
  61: // SNIPPED BY SCOTT 
  62: } 
  63: } 
  64: return users; 
  65: }

到目前为止,我想使用的Web 2.0应用程序都有了相应的.NET库。我甚至在去年就匆匆的给Wesabe建了一个.NET客户端,后来又用IronRuby写了一个

希望你们喜欢,另外代码的海洋里,我错过什么了呢?