1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > 新浪微博PC客户端(DotNet WinForm版)——功能实现分解介绍

新浪微博PC客户端(DotNet WinForm版)——功能实现分解介绍

时间:2023-08-21 04:11:22

相关推荐

新浪微博PC客户端(DotNet WinForm版)——功能实现分解介绍

上一篇:新浪微博PC客户端(DotNet WinForm版)—— 初探

说明一下:只是兴趣,并不是想发布为一个软件,说实在的,如果要作为一个软件发布,要做的工作还有很多。

新浪微博API地址:http://open./wiki/index.php/API%E6%96%87%E6%A1%A3?retcode=0。

目前提供的SDK:

1Adobe Air2Flash SDK3PHP4C++5C#6Java SDK7Python SDK8Javascript SDK(JSSDK)9iOS SDK10OAuth Library

其它的不清楚,C#的还不完善,而且不是官方的。

当前已实现的功能:

1、HTTP普通鉴权(Basic Authentication)的身份验证方式,说白了就是每次访问API都要发送帐号和密码,当然是不安全的,但是相比OAuth验证方式门槛要低的多。要使用OAuth验证方式可以去看下SDK。

===》account/verify_credentials验证当前用户身份是否合法

OAuth是一种国际通用的授权方式,它的特点是不需要用户在第三方应用输入用户名及密码。OAuth的技术说明可参看官方网站 。学习和研究OAuth的意义不仅仅在于新浪围脖,而是对以后开发类似的项目非常有意义和价值(如果你还不知道OAuth授权方式)。

验证通过则返回用户的个人信息,参照下面的API字段说明。

API字段说明

status微博信息内容user用户资料信息comment评论信息描述direct_message私信信息省份城市编码表

上一篇说了,API返回的结果格式有两种:XML和JSON,在调用API的时候可以指定。

这里我指定的是XML格式,实现代码如下:

登录窗体:FrmLogin.cs,可以看上一篇的截图。

(1)把申请AppKey和AppSecret写到app.config

<? xmlversion="1.0"encoding="utf-8" ?>

< configuration >

< appSettings >

< add key ="AppKey" value ="3476523072" />

< add key ="AppSecret" value ="3c909770efa6865fd5843a564545826d" />

</ appSettings >

</ configuration >

要申请AppKey,需要登录新浪围脖,然后在这个地址创建应用并生成:http://open./apps/new。

(2)创建了一个静态类,保存用户信息

代码 using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Runtime.Serialization;

namespace MBClient.Entities

{

/// <summary>

/// 微博帐号资料

/// </summary>

internal class UserInfo

{

internal UserInfo(){}

internal static string UserName{ get ; set ;}

internal static string Password{ get ; set ;}

internal static string UserNamePassword{ get ; set ;}

internal static int ID{ get ; set ;}

internal static string ScreenName{ get ; set ;}

internal static string Name{ get ; set ;}

internal static int Province{ get ; set ;}

internal static int City{ get ; set ;}

internal static string Location{ get ; set ;}

internal static string Description{ get ; set ;}

internal static string Url{ get ; set ;}

internal static string ProfileImageUrl{ get ; set ;}

internal static string Domain{ get ; set ;}

internal static string Gender{ get ; set ;}

/// <summary>

/// 粉丝数

/// </summary>

internal static int FollowersCount{ get ; set ;}

/// <summary>

/// 关注数

/// </summary>

internal static int FriendsCount{ get ; set ;}

/// <summary>

/// 微博数

/// </summary>

internal static int StatusesCount{ get ; set ;}

internal static int FavouritesCount{ get ; set ;}

/// <summary>

/// 微博帐号日期

/// </summary>

internal static string CreatedAt{ get ; set ;}

internal static bool Following{ get ; set ;}

internal static bool Verified{ get ; set ;}

internal static bool AllowAllActMsg{ get ; set ;}

internal static bool GeoEnabled{ get ; set ;}

}

}

(3)调用API

代码 using System;

using System.Collections.Generic;

using System.Text;

using System.IO;

using ;

using MBClient.Entities;

using System.Runtime.Serialization.Json;

using System.Data;

using System.Drawing;

using MBClient.Class;

namespace MBClient.API

{

internal class ReadDataBySinaAPI

{

internal ReadDataBySinaAPI(){}

/// <summary>

/// 根据用户登录信息登录并读取用户信息到DataSet

/// </summary>

/// <paramname="url"> APIURL(XMLFormat) http://api./account/verify_credentials.xml?source=AppKey </param>

/// <paramname="httpRequestMethod)"> HTTP请求方式:GET或POST </param>

/// <returns></returns>

internal static DataSetReadXMLDataToDataSet( string url, string httpRequestMethod)

{

try

{

WebResponsewr = BasicAuthorizationRequest(url,httpRequestMethod).GetResponse();

DataSetds = new DataSet();

StreamreceieveStream = wr.GetResponseStream();

StreamReaderreader = new StreamReader(receieveStream,Encoding.UTF8);

ds.ReadXml(reader);

reader.Close();

receieveStream.Close();

wr.Close();

return ds;

}

catch (Exceptione)

{

return null ;

}

}

/// <summary>

/// HTTP普通验证(BasicAuthentication)方式

/// </summary>

/// <paramname="url"> APIURL </param>

/// <paramname="httpRequestMethod)"> HTTP请求方式:GET或POST </param>

/// <returns></returns>

private static WebRequestBasicAuthorizationRequest( string url, string httpRequestMethod)

{

CredentialCachemycache = new CredentialCache();

mycache.Add( new Uri(url), " Basic " , new NetworkCredential(UserInfo.UserName,UserInfo.Password));

WebRequestmyReq = WebRequest.Create(url);

myReq.Credentials = mycache;

myReq.Headers.Add( " Authorization " , " Basic " + Convert.ToBase64String( new ASCIIEncoding().GetBytes(UserInfo.UserNamePassword)));

myReq.Method = httpRequestMethod;

return myReq;

}

}

}

我这里直接用DataSet读取XML了,当然也可以将XML数据反序列化,后面会看到,我将返回的JSON格式的围脖信息进行了反序列化。

(4)登录窗体代码

代码 using System;

using System.Data;

using System.Windows.Forms;

using MBClient.Entities;

using MBClient.API;

using MBClient.Class;

using Sunisoft.IrisSkin;

using System.Threading;

namespace MBClient

{

public partial class FrmLogin:Form

{

string ErrorMsg = null ;

public FrmLogin()

{

InitializeComponent();

this .skinEngine.SkinFile = @" skin\msn.ssk " ;

this .Text = " 登录 " + Properties.Resources.SoftName;

}

private void FrmLogin_Load( object sender,EventArgse)

{

// this.TransparencyKey=this.BackColor;

// 各线程之间没有互相争抢控件资源的情况,设置允许跨线程访问

CheckForIllegalCrossThreadCalls = false ;

cBoxAccountSina.Select();

InitBackGroundWorker();

}

private void InitBackGroundWorker()

{

backWorker.DoWork += new ponentModel.DoWorkEventHandler(backWorker_DoWork);

backWorker.ProgressChanged += new ponentModel.ProgressChangedEventHandler(backWorker_ProgressChanged);

backWorker.RunWorkerCompleted += new ponentModel.RunWorkerCompletedEventHandler(backWorker_RunWorkerCompleted);

}

void backWorker_ProgressChanged( object sender,ponentModel.ProgressChangedEventArgse)

{

// thrownewNotImplementedException();

}

void backWorker_RunWorkerCompleted( object sender,ponentModel.RunWorkerCompletedEventArgse)

{

if (ErrorMsg == null )

{

System.Threading.Thread.Sleep( 200 );

FrmMainmain = new FrmMain();

this .Hide();

main.Show();

}

else

{

tabControl.Visible = true ;

MessageBox.Show(ErrorMsg, " 登录失败 " ,MessageBoxButtons.OK,

MessageBoxIcon.Exclamation);

}

}

void backWorker_DoWork( object sender,ponentModel.DoWorkEventArgse)

{

try

{

ErrorMsg = null ;

UserInfo.UserName = cBoxAccountSina.Text.Trim();;

UserInfo.Password = txtPwdSina.Text.Trim();

UserInfo.UserNamePassword = UserInfo.UserName + " : " + UserInfo.Password;

// source参数为申请的新浪微博AppKey

string url = Function.ACCOUNTVERIFY + " ?source= " + Function.APPKEY;

DataSetds = ReadDataBySinaAPI.ReadXMLDataToDataSet(url, " GET " );

DataRowdr = ds.Tables[ 0 ].Rows[ 0 ];

#region // 将用户信息保存到UserInfo

UserInfo.AllowAllActMsg = bool .Parse(dr[ " allow_all_act_msg " ].ToString());

UserInfo.City = int .Parse(dr[ " city " ].ToString());

UserInfo.CreatedAt = dr[ " created_at " ].ToString();

UserInfo.Description = dr[ " description " ].ToString();

UserInfo.Domain = dr[ " domain " ].ToString();

UserInfo.FavouritesCount = int .Parse(dr[ " favourites_count " ].ToString());

UserInfo.FollowersCount = int .Parse(dr[ " followers_count " ].ToString());

UserInfo.Following = bool .Parse(dr[ " following " ].ToString());

UserInfo.FriendsCount = int .Parse(dr[ " friends_count " ].ToString());

UserInfo.Gender = dr[ " gender " ].ToString();

UserInfo.GeoEnabled = bool .Parse(dr[ " geo_enabled " ].ToString());

UserInfo.ID = int .Parse(dr[ " ID " ].ToString());

UserInfo.Location = dr[ " location " ].ToString();

UserInfo.Name = dr[ " name " ].ToString();

UserInfo.ProfileImageUrl = dr[ " profile_image_url " ].ToString();

UserInfo.Province = int .Parse(dr[ " province " ].ToString());

UserInfo.ScreenName = dr[ " screen_name " ].ToString();

UserInfo.StatusesCount = int .Parse(dr[ " statuses_count " ].ToString());

UserInfo.Url = dr[ " url " ].ToString();

UserInfo.Verified = bool .Parse(dr[ " verified " ].ToString());

#endregion

}

catch (Exceptionex)

{

ErrorMsg = ex.Message;

}

}

private void FrmLogin_FormClosing( object sender,FormClosingEventArgse)

{

Application.ExitThread();

}

private void btnLoginSina_Click( object sender,EventArgse)

{

tabControl.Visible = false ;

backWorker.RunWorkerAsync();

}

private void btnRegSina_Click( object sender,EventArgse)

{

System.Diagnostics.Process.Start( " /reg.php?ps=u3&lang=zh " );

}

}

}

关于登录窗体的代码,说两点:

(1)使用皮肤

在你的Windows系统盘搜索IrisSkin2.dll

找到后添加到工具箱,右击工具箱——选择项——选择这个dll

然后将SkinEngine控件拖到窗体上,在代码中按照如下方式写:

this .skinEngine.SkinFile = @" skin\msn.ssk " ;

这个ssk文件我使用了网上的资源,你也可以到网上去搜,可以下载到有20种左右的皮肤,有了皮肤文件,要实现换肤功能就非常简单了。你也可以使用皮肤工具生成皮肤,如:skin++。

(2)使用BackgroundWorker异步加载 :关于BackgroundWorker,MSDN说的很清楚了,这里不再赘述,/zh-cn/library/ponentmodel.backgroundworker(v=vs.80).aspx。

2、获取当前登录用户及其所关注用户的最新微博消息,并分页显示,这儿是我代码最多的地方,主要是由于所有显示的控件都动态加载的缘故。

今天看到新浪围脖API更新了(近一个周都没看),之前是只能传页码,默认每页返回20条数据;现在是可以传页码和返回数据的条数。

===》statuses/friends_timeline获取当前登录用户及其所关注用户的最新微博消息 (别名: statuses/home_timeline)

这里在第一次加载和翻页时也使用BackgroundWorker。具体代码不贴出来了,代码太长,后面提供下载,供参考。

具体实现简单说一下:

根据调用接口返回的JSON数据格式化为泛型对象集合,然后遍历集合在Panel中动态添加控件显示数据,主要一点是要计算好控件的位置。

(1)围脖类

代码 using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Runtime.Serialization;

namespace MBClient.Entities

{

[DataContract]

internal class Status

{

[DataMember]

internal string created_at{ get ; set ;}

[DataMember]

internal string id{ get ; set ;}

[DataMember]

internal string text{ get ; set ;}

[DataMember]

internal string source{ get ; set ;}

[DataMember]

internal bool favorited{ get ; set ;}

[DataMember]

internal bool truncated{ get ; set ;}

[DataMember]

internal string geo{ get ; set ;}

[DataMember]

internal string in_reply_to_status_id{ get ; set ;}

[DataMember]

internal string in_reply_to_user_id{ get ; set ;}

[DataMember]

internal string in_reply_to_screen_name{ get ; set ;}

[DataMember]

internal string thumbnail_pic{ get ; set ;}

[DataMember]

internal string bmiddle_pic{ get ; set ;}

[DataMember]

internal string original_pic{ get ; set ;}

[DataMember]

internal PostUseruser{ get ; set ;}

[DataMember]

internal RetweetedStatusretweeted_status{ get ; set ;}

}

}

(2)用户类

代码 using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Runtime.Serialization;

namespace MBClient.Entities

{

[DataContract]

internal class PostUser

{

[DataMember]

internal string id{ get ; set ;}

[DataMember]

internal string screen_name{ get ; set ;}

[DataMember]

internal string name{ get ; set ;}

[DataMember]

internal string province{ get ; set ;}

[DataMember]

internal string city{ get ; set ;}

[DataMember]

internal string location{ get ; set ;}

[DataMember]

internal string description{ get ; set ;}

[DataMember]

internal string url{ get ; set ;}

[DataMember]

internal string profile_image_url{ get ; set ;}

[DataMember]

internal string domain{ get ; set ;}

[DataMember]

internal string gender{ get ; set ;}

[DataMember]

internal string followers_count{ get ; set ;}

[DataMember]

internal string friends_count{ get ; set ;}

[DataMember]

internal string statuses_count{ get ; set ;}

[DataMember]

internal string favourites_count{ get ; set ;}

[DataMember]

internal string created_at{ get ; set ;}

[DataMember]

internal string following{ get ; set ;}

[DataMember]

internal string verified{ get ; set ;}

[DataMember]

internal string allow_all_act_msg{ get ; set ;}

[DataMember]

internal string geo_enabled{ get ; set ;}

}

}

(3)转发的围脖类

代码 using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Runtime.Serialization;

namespace MBClient.Entities

{

[DataContract]

internal class RetweetedStatus

{

[DataMember]

internal string created_at{ get ; set ;}

[DataMember]

internal string id{ get ; set ;}

[DataMember]

internal string text{ get ; set ;}

[DataMember]

internal string source{ get ; set ;}

[DataMember]

internal string favorited{ get ; set ;}

[DataMember]

internal string truncated{ get ; set ;}

[DataMember]

internal string geo{ get ; set ;}

[DataMember]

internal string in_reply_to_status_id{ get ; set ;}

[DataMember]

internal string in_reply_to_user_id{ get ; set ;}

[DataMember]

internal string in_reply_to_screen_name{ get ; set ;}

[DataMember]

internal string thumbnail_pic{ get ; set ;}

[DataMember]

internal string bmiddle_pic{ get ; set ;}

[DataMember]

internal string original_pic{ get ; set ;}

[DataMember]

internal PostUseruser{ get ; set ;}

}

}

(4)根据URL输出头像或图片

/// <summary>

/// 获取并输出图片

/// </summary>

/// <paramname="imgUrl"> 图片URL地址 </param>

/// <returns></returns>

internal static ImageGetAvatarImage( string imgUrl)

{

try

{

UrimyUri = new Uri(imgUrl);

WebRequestwebRequest = WebRequest.Create(myUri);

WebResponsewebResponse = webRequest.GetResponse();

BitmapmyImage = new Bitmap(webResponse.GetResponseStream());

return (Image)myImage;

}

catch (Exceptione)

{

return null ;

}

}

可以去看一下上一篇我贴出的XML数据,基本就清楚了

(5)读取微博信息并反序列化

代码 /// <summary>

/// 以JSON格式字符串返回用户所有关注用户最新n条微博信息。和用户“我的首页”返回内容相同。

/// http://open./wiki/index.php/Statuses/friends_timeline

/// </summary>

/// <paramname="url"> APIURL(JSONFormat) http://api./statuses/friends_timeline.json?source=AppKey </param>

/// <paramname="httpRequestMethod"> HTTP请求方式 </param>

/// <returns></returns>

internal static string ReadJsonDataToString( string url, string httpRequestMethod)

{

try

{

WebResponsewr = BasicAuthorizationRequest(url,httpRequestMethod).GetResponse();

string content = null ;

StreamreceieveStream = wr.GetResponseStream();

StreamReaderreader = new StreamReader(receieveStream,Encoding.UTF8);

content = reader.ReadToEnd();

reader.Close();

receieveStream.Close();

wr.Close();

return content;

}

catch (Exceptione)

{

return null ;

}

}

/// <summary>

/// 将JSON格式字符串反序列化为Status集合对象

/// </summary>

/// <paramname="url"> 同ReadJsonDataToString() </param>

/// <paramname="httpRequestMethod"> HTTP请求方式 </param>

/// <returns></returns>

internal static List < Status > DeserializeJsonToListObject( string url, string httpRequestMethod)

{

try

{

List < Status > listObj;

MemoryStreamstream = new MemoryStream();

DataContractJsonSerializerser = new DataContractJsonSerializer( typeof (List < Status > ));

StreamWriterwr = new StreamWriter(stream);

wr.Write(ReadJsonDataToString(url,httpRequestMethod));

wr.Flush();

stream.Position = 0 ;

Objectobj = ser.ReadObject(stream);

listObj = (List < Status > )obj;

wr.Close();

stream.Close();

return listObj;

}

catch (Exceptione)

{

return null ;

}

}

3、发表围脖

下面的方法还是写在ReadDataBySinaAPI这个类里。

发表围脖分为两种情况:

(1)纯文本 ===》statuses/update发布一条微博信息

代码 /// <summary>

/// 发表一条微博

/// </summary>

/// <paramname="url"> APIURL http://api./statuses/update.json </param>

/// <paramname="data"> AppKey和微博内容"source=123456&status="+微博内容; </param>

/// <paramname="httpRequestMethod"> HTTP请求方式 </param>

/// <returns></returns>

internal static bool PostBlog( string url, string data, string httpRequestMethod)

{

try

{

HttpWebRequesthttpRequest = BasicAuthorizationRequest(url,httpRequestMethod) as HttpWebRequest;

httpRequest.ContentType = " application/x-www-form-urlencoded " ;

Encodingencoding = Encoding.UTF8; // System.Text.Encoding.ASCII

byte []bytesToPost = encoding.GetBytes(data); // System.Web.HttpUtility.UrlEncode(data)

httpRequest.ContentLength = bytesToPost.Length;

StreamrequestStream = httpRequest.GetRequestStream();

requestStream.Write(bytesToPost, 0 ,bytesToPost.Length);

requestStream.Close();

return true ;

}

catch

{

return false ;

}

}

(2)带图片 ===》statuses/upload上传图片并发布一条微博信息

代码 /// <summary>

/// 发布带图片的微博

/// ref: http://sarlmolapple.is-/posts/22435.html

/// </summary>

/// <paramname="url"> 微博API </param>

/// <paramname="data"> 包含微博内容和图片的集合 </param>

/// <paramname="httpRequestMethod"> HTTP请求方式 </param>

/// <returns></returns>

internal static bool PostBlogWithPic( string url,List < string > data, string httpRequestMethod)

{

try

{

HttpWebRequesthttpRequest = BasicAuthorizationRequest(url,httpRequestMethod) as HttpWebRequest;

httpRequest.PreAuthenticate = true ;

httpRequest.AllowWriteStreamBuffering = true ;

string boundary = Guid.NewGuid().ToString();

httpRequest.ContentType = string .Format( " multipart/form-data;boundary={0} " ,boundary);

string header = string .Format( " --{0} " ,boundary);

string footer = string .Format( " --{0}-- " ,boundary);

StringBuildercontents = new StringBuilder();

contents.AppendLine(header);

contents.AppendLine(String.Format( " Content-Disposition:form-data;name=\ " {0 }\ "" , " status " ));

contents.AppendLine( " Content-Type:text/plain;charset=utf-8 " ); // US-ASCII

contents.AppendLine( " Content-Transfer-Encoding:8bit " );

contents.AppendLine();

// 微博文字

contents.AppendLine(data[ 0 ]);

contents.AppendLine(header);

contents.AppendLine( string .Format( " Content-Disposition:form-data;name=\ " {0 }\ "" , " source " ));

contents.AppendLine( " Content-Type:text/plain;charset=utf-8 " ); // US-ASCII

contents.AppendLine( " Content-Transfer-Encoding:8bit " );

contents.AppendLine();

contents.AppendLine(Function.APPKEY);

byte []bytesToPost = Encoding.UTF8.GetBytes(contents.ToString());

// 微博图片

contents = null ;

contents = new StringBuilder();

contents.AppendLine(header);

string fileHeader = string .Format( " Content-Disposition:form-data;name=\ " {0 }\ " ;filename=\ " {1 }\ "" , " pic " ,boundary);

string fileData = Encoding.GetEncoding( " iso-8859-1 " ).GetString(File.ReadAllBytes(@data[ 1 ]));

contents.AppendLine(fileHeader);

contents.AppendLine( " Content-Type:application/octet-stream;charset=utf-8 " );

contents.AppendLine( " Content-Transfer-Encoding:binary " );

contents.AppendLine();

contents.AppendLine(fileData);

contents.AppendLine(footer);

byte []bytesToPost2 = Encoding.GetEncoding( " iso-8859-1 " ).GetBytes(contents.ToString());

httpRequest.ContentLength = bytesToPost.Length + bytesToPost2.Length;

StreamrequestStream = httpRequest.GetRequestStream();

requestStream.Write(bytesToPost, 0 ,bytesToPost.Length);

requestStream.Write(bytesToPost2, 0 ,bytesToPost2.Length);

requestStream.Close();

return true ;

}

catch (Exceptione)

{

return false ;

}

}

这里要注意的一点是iso-8859-1这种编码方式不支持中文,所以,把图片内容和非图片内容分开处理了。

(3)验证输入字数不能超过140字符

// 计算字符串长度,中文及全角算1,其它算0.5

internal static int GetStringLength( string text)

{

int Len = 0 ;

// 记录非中文、非全角字符为0.5个长度

float charLen = 0 ;

char []chars = text.ToCharArray();

for ( int i = 0 ;i < chars.Length;i ++ )

{

int charLength = Encoding.UTF8.GetByteCount(chars[i].ToString());

if (charLength == 3 )

{

Len ++ ;

}

else

{

if (charLen == 0.5 )

{

charLen = 0 ;

}

else

{

charLen = 0.5f ;

Len ++ ;

}

}

}

return Len;

}

4、转发围脖 ===》statuses/repost转发一条微博信息

用的这个方法PostBlog(),HTTP请求方式为POST。

5、评论围脖 ===》statuses/comment对一条微博信息进行评论

用的这个方法PostBlog(),HTTP请求方式为POST。

6、收藏围脖 ===》favorites/create添加收藏

用的这个方法ReadXMLDataToDataSet(),HTTP请求方式为POST。

7、降低工作内存

/// <summary>

/// 设置工作内存,降低内存占用

/// </summary>

/// <paramname="maxWorkingSet"> 内存中允许的进程的最大工作集大小(以字节为单位) </param>

public static void SetWorkingSet( int maxWorkingSet)

{

System.Diagnostics.Process.GetCurrentProcess().MaxWorkingSet = (IntPtr)maxWorkingSet;

}

HTTP请求方式,API中有说明,所以上面没有一一指出。

可参考资料:

CredentialCache:/zh-cn/library/.credentialcache(v=VS.90).aspx

WebRequest:/zh-cn/library/.webrequest(v=VS.90).aspx

NetworkCredential:/zh-cn/library/workcredential(v=VS.90).aspx

Basic Access Authentication:/wiki/Basic_access_authentication;Basic:基本;digest:简要

枝枝节节的还有不少内容,不一一说了,稍后我提供源码,有兴趣的可以看下,一起讨论。

最后,围脖=微博,呵呵。

先提供编译后的,供测试:/Files/Ferry/MBPCClient/微博客户端.rar

C#源码下载

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。