每周源代码22- Digg, Flickr, Facebook, YouTube, Twitter, Live 服务,Google和Web 2.0 APIs 的C#,VB .NET库
[原文发表时间] 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有一个非常复杂和深入的API,并在.NET上有很多的支持。Nikhil对其进行了很好的诠释。你可以使用免费的Express Visual Studio版本来为Facebook做开发。
下面有很多可用的:
有详细说明的Facebook.NET资源库和Clarity Consulting's公司的Facebook开发工具包。Jay Lagorio用VB.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 Spaces、 DasBlog、 SubText等等。关于如何用C# 或 VB访问XML-RPC,在MSDN上有很多深入研究的范例。
当你写帖子时,Windows Live Writer和BlogJet会使用这些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 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写了一个。
希望你们喜欢,另外代码的海洋里,我错过什么了呢?
Comments
- Anonymous
September 05, 2011
Cool, I'll keep it for reference.