UCenter和各个子站的通讯,主要就是通过 POST 的方式调用而已,没有什么技术含量。
表单参数都是经过 Base64 算法,加一个通信密钥进行加密和解密的。
返回的数据是单个参数(例如:0或者1),也可能是xml序列化后的数据。
例如一个请求:code=e145fscn314BSKnwxBvqLaQe2yrHJAnKO1M%2B8C4cAKQAtRRQfEqTh8mg665UVJPyrJIrPhDNnEM
解密后:action=test&time=1295631663
返回:1
以上是一个测试是否通讯成功的请求,上面是表单参数,解密的明文如上,返回1代表通讯成功!
而其中的难点就在于,Discuz 并没有公开所有的 API!而仅仅是提供了一个 php 版的函数库,帮你写好了这些通讯函数。
所以 php 的网站做起来很轻松,而别的网站就痛苦了。
这里的痛苦包括加密和解密函数、序列化和反序列化、还有期中各个参数的名称和格式…
怎么解决?只能翻阅 php 版本的源代码,把它的 API 一条条看过去,然后用自己需要用的语言重写一遍。
通讯原理就这么点,很简单,子站想联系 UCenter,就调用一个地址,传递一些参数就行了,反之亦然。
最难的其实是多点登录的过程。
因为一般的请求,比如在子站请求登录,调用一下,然后返回数据,就完成了;而多点登录没那么简单。
理解原理后才能知道应该怎么用多点登录。
先看一下多点登录的过程吧:
子站调用封装好的登录函数(参数:帐号、密码),返回登录信息(包括:uid、用户名、E-mail)
子站调用同步登录函数(参数:uid),返回一段 javascript 代码
在子站前端页面执行这段 javascript 代码
该代码通知其他子站,调用它们的同步登录函数
解惑:
刚看完这个,我也很疑惑,为什么要这样?那我反问下,你想怎么样?
1、子站调用同步登录函数,UCenter 通知子站同步登录?
2、子站调用同步登录函数,得到别的子站的同步登录 API,直接调用?
为什么上面两个不行?什么叫同步登录?
那就是:用户的电脑需要在每个子站下保存了 Session,或者在用户电脑上保存了该每个子站的 Cookie
那前面两个方案:
方案一:在 UCenter 服务器保存了 Cookie,所有子站服务器上的 Session 记录记录的是 UCenter 服务器的,而不是用户的
方案二:同理可得,Cookie 保存在了一台子站的服务器上,Session 记录的是那个子站服务器的
所以,一定要用户的电脑通知各子站才行,那怎么通知呢?通过 javascript
关键:
所以,UCenter 在同步登录的过程中,最关键的就是需要把这个 API 返回的 javascript 代码在前端运行
可以这样写:
using DS.Web.UCenterAPI.UCClient; var client = new UCClient(); var ucLoginReturn = client.UC_User_Login("admin", "admin"); if(ucLoginReturn.Success) { var js = client.UC_User_Synlogin(ucLoginReturn.User.Uid); Response.Write(js); }
这样就可以实现同步登录了,我们来看一下 UC_User_Synlogin 函数返回的 js 代码吧:
这个函数是什么?传说这是康盛公司对 php 发展做出的一个极大的贡献…
该函数可以实现可逆加密,在 php 中广为流传…
UCenter API 中的加密解密函数,被称为 php 领域的经典之作,也是康盛公司为 php 做的一大贡献
这个函数,可以通过一个 KEY ,生成动态的密文,并可以再通过这个 KEY 来解密
我没有研究过什么加密算法,所以对这个的基础知识也不是很了解,或许在 C# 中会有更强大的算法,但是这个函数在做 UCenter API 的时候是必需的。
也是 UCenter API php 版翻译成 C# 版本中最难的一个部分。
// $string: 明文 或 密文 // $operation:DECODE表示解密,其它表示加密 // $key: 密匙 // $expiry:密文有效期 //字符串解密加密 function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) { // 动态密匙长度,相同的明文会生成不同密文就是依靠动态密匙 $ckey_length = 4; // 随机密钥长度 取值 0-32; // 加入随机密钥,可以令密文无任何规律,即便是原文和密钥完全相同,加密结果也会每次不同,增大破解难度。 // 取值越大,密文变动规律越大,密文变化 = 16 的 $ckey_length 次方 // 当此值为 0 时,则不产生随机密钥 // 密匙 $key = md5($key ? $key : UC_KEY); // 密匙a会参与加解密 $keya = md5(substr($key, 0, 16)); // 密匙b会用来做数据完整性验证 $keyb = md5(substr($key, 16, 16)); // 密匙c用于变化生成的密文 $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : ''; // 参与运算的密匙 $cryptkey = $keya.md5($keya.$keyc); $key_length = strlen($cryptkey); // 明文,前10位用来保存时间戳,解密时验证数据有效性,10到26位用来保存$keyb(密匙b),解密时会通过这个密匙验证数据完整性 // 如果是解码的话,会从第$ckey_length位开始,因为密文前$ckey_length位保存 动态密匙,以保证解密正确 $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string; $string_length = strlen($string); $result = ''; $box = range(0, 255); $rndkey = array(); // 产生密匙簿 for($i = 0; $i <= 255; $i++) { $rndkey[$i] = ord($cryptkey[$i % $key_length]); } // 用固定的算法,打乱密匙簿,增加随机性,好像很复杂,实际上对并不会增加密文的强度 for($j = $i = 0; $i < 256; $i++) { $j = ($j + $box[$i] + $rndkey[$i]) % 256; $tmp = $box[$i]; $box[$i] = $box[$j]; $box[$j] = $tmp; } // 核心加解密部分 for($a = $j = $i = 0; $i < $string_length; $i++) { $a = ($a + 1) % 256; $j = ($j + $box[$a]) % 256; $tmp = $box[$a]; $box[$a] = $box[$j]; $box[$j] = $tmp; // 从密匙簿得出密匙进行异或,再转成字符 $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256])); } if($operation == 'DECODE') { // 验证数据有效性,请看未加密明文的格式 if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) { return substr($result, 26); } else { return ''; } } else { // 把动态密匙保存在密文里,这也是为什么同样的明文,生产不同密文后能解密的原因 // 因为加密后的密文可能是一些特殊字符,复制过程可能会丢失,所以用base64编码 return $keyc.str_replace('=', '', base64_encode($result)); } }
这份详解不是我写的,网上有很多,找不到原作者了
/// <summary> /// AuthCode解码&编码 /// </summary> /// <param name="sourceStr">原始字符串</param> /// <param name="operation">操作类型</param> /// <param name="keyStr">API KEY</param> /// <param name="expiry">过期时间 0代表永不过期</param> /// <returns></returns> private static string AuthCode(string sourceStr, AuthCodeMethod operation, string keyStr, int expiry = 0) { var ckeyLength = 4; var source = Encode.GetBytes(sourceStr); var key = Encode.GetBytes(keyStr); key = Md5(key); var keya = Md5(SubBytes(key, 0, 0x10)); var keyb = Md5(SubBytes(key, 0x10, 0x10)); var keyc = (ckeyLength > 0) ? ((operation == AuthCodeMethod.Decode) ? SubBytes(source, 0, ckeyLength) : RandomBytes(ckeyLength)) : new byte[0]; var cryptkey = AddBytes(keya, Md5(AddBytes(keya, keyc))); var keyLength = cryptkey.Length; if (operation == AuthCodeMethod.Decode) { while (source.Length % 4 != 0) { source = AddBytes(source, Encode.GetBytes("=")); } source = Convert.FromBase64String(BytesToString(SubBytes(source, ckeyLength))); } else { source = AddBytes( (expiry != 0 ? Encode.GetBytes((expiry + PhpTimeNow()).ToString()) : Encode.GetBytes("0000000000")), SubBytes(Md5(AddBytes(source, keyb)), 0, 0x10), source); } var sourceLength = source.Length; var box = new int[256]; for (var k = 0; k < 256; k++) { box[k] = k; } var rndkey = new int[256]; for (var i = 0; i < 256; i++) { rndkey[i] = cryptkey[i % keyLength]; } for (int j = 0, i = 0; i < 256; i++) { j = (j + box[i] + rndkey[i]) % 256; var tmp = box[i]; box[i] = box[j]; box[j] = tmp; } var result = new byte[sourceLength]; for (int a = 0, j = 0, i = 0; i < sourceLength; i++) { a = (a + 1) % 256; j = (j + box[a]) % 256; var tmp = box[a]; box[a] = box[j]; box[j] = tmp; result[i] = (byte)(source[i] ^ (box[(box[a] + box[j]) % 256])); } if (operation == AuthCodeMethod.Decode) { var time = long.Parse(BytesToString(SubBytes(result, 0, 10))); if ((time == 0 || time - PhpTimeNow() > 0) && BytesToString(SubBytes(result, 10, 16)) == BytesToString(SubBytes(Md5(AddBytes(SubBytes(result, 26), keyb)), 0, 16))) { return BytesToString(SubBytes(result, 26)); } return ""; } return BytesToString(keyc) + Convert.ToBase64String(result).Replace("=", ""); } /// <summary> /// Byte数组转字符串 /// </summary> /// <param name="b">数组</param> /// <returns></returns> public static string BytesToString(byte[] b) { return new string(Encode.GetChars(b)); } /// <summary> /// 计算Md5 /// </summary> /// <param name="b">byte数组</param> /// <returns>计算好的字符串</returns> public static byte[] Md5(byte[] b) { var cryptHandler = new MD5CryptoServiceProvider(); var hash = cryptHandler.ComputeHash(b); var ret = ""; foreach (var a in hash) { if (a < 16) { ret += "0" + a.ToString("x"); } else { ret += a.ToString("x"); } } return Encode.GetBytes(ret); } /// <summary> /// Byte数组相加 /// </summary> /// <param name="bytes">数组</param> /// <returns></returns> public static byte[] AddBytes(params byte[][] bytes) { var index = 0; var length = 0; foreach(var b in bytes) { length += b.Length; } var result = new byte[length]; foreach(var bs in bytes) { foreach (var b in bs) { result[index++] = b; } } return result; } /// <summary> /// Byte数组分割 /// </summary> /// <param name="b">数组</param> /// <param name="start">开始</param> /// <param name="length">结束</param> /// <returns></returns> public static byte[] SubBytes(byte[] b, int start, int length = int.MaxValue) { if (start >= b.Length) return new byte[0]; if (start < 0) start = 0; if (length < 0) length = 0; if (length>b.Length || start + length > b.Length) length = b.Length - start; var result = new byte[length]; var index = 0; for(var k = start;k< start + length;k++) { result[index++] = b[k]; } return result; } /// <summary> /// 计算Php格式的当前时间 /// </summary> /// <returns>Php格式的时间</returns> public static long PhpTimeNow() { return DateTimeToPhpTime(DateTime.UtcNow); } /// <summary> /// PhpTime转DataTime /// </summary> /// <returns></returns> public static DateTime PhpTimeToDateTime(long time) { var timeStamp = new DateTime(1970, 1, 1); //得到1970年的时间戳 var t = (time + 8 * 60 * 60) * 10000000 + timeStamp.Ticks; return new DateTime(t); } /// <summary> /// DataTime转PhpTime /// </summary> /// <param name="datetime">时间</param> /// <returns></returns> public static long DateTimeToPhpTime(DateTime datetime) { var timeStamp = new DateTime(1970, 1, 1); //得到1970年的时间戳 return (datetime.Ticks - timeStamp.Ticks) / 10000000; //注意这里有时区问题,用now就要减掉8个小时 } /// <summary> /// 随机字符串 /// </summary> /// <param name="lens">长度</param> /// <returns></returns> public static byte[] RandomBytes(int lens) { var chArray = new[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; var length = chArray.Length; var result = new byte[lens]; var random = new Random(); for (var i = 0; i < lens; i++) { result[i] = (byte) chArray[random.Next(length)]; } return result; } /// <summary> /// 操作类型 /// </summary> enum AuthCodeMethod { Encode, Decode, }
C# 版是一行一行按照原版本翻译的,增加了一些 C# 中没有的函数
1、string -> byte[] 的问题
在这段算法中,经常会用到 Base64 算法,C# 中的 Base64 要求输入的是 byte[] 数组
在 php 程序中,都是直接用字符串的,而且也没有问题。
那在 C# 版中自然想到了 Encoding.Default.GetBytes() 函数
但这个函数有个很奇怪的问题:
Encoding.UTF8.GetBytes(((char) 200).ToString())[0].ToString() //最后的值是多少?
运行一下后发现它不是200,因为这个函数涉及到了编码问题
所以上述的操作,如果直接对字符串操作,那会出现很多问题,因为 php 和 C# 对字符串使用的默认编码不同。
所以就改成了对 byte[] 进行操作
1、UCenter
这个当然是最基本的东西,安装起来也很简单,官方就有教程
http://faq.comsenz.com/userguide/x/install.html
安装完成后,因为还没有安装别的应用,所以应用数量是:0
2、Discuz
如果仅仅是为了用 UCenter,那有点得不偿失了,一般都会配上论坛
这里采用的是 Discuz! 7.2
http://faq.comsenz.com/userguide/discuz/install.html
这里没有什么难度,网上有许多教程
新建网站
既然是 UCenter 和 Asp.net 通讯,那肯定要搭建一个 Asp.net 的网站了
为了 方便测试,我们最好把网站直接在 IIS 中调试
新建网站应用程序 — 打开属性页面
这步非不要操作,但是可以模拟真实的场景,并且还可以在 IIS 里调试
设置完后我们看一下 IIS 里的情况(我把 UCenter 和 Discuz 挂在 IIS 下了)
在 UCenter 下新建应用程序
登录后点添加新应用
按照这张图配置一下
这里就一个地方和配置 php 的网站不同,就是“应用接口文件名称”
当然你也可以用 .php 然后配置 IIS,但是这个多麻烦?用 ashx 是最方便的,在后面会有详解,到时候你就知道为什么了
提交后复制一下配置信息,后面有用
配置Asp.net网站
接下来我们需要把配置文件写入 Asp.net 的网站的 Web.config 中
乍一看,这配置是 php 格式的!
但这里有一份完整的配置信息,只要替换对应的地方就行:
<!--客户端版本--> <add key="UC_CLIENT_VERSION" value="1.5.2"/> <!--发行时间--> <add key="UC_CLIENT_RELEASE" value="20101001"/> <!--API 开关(value类型:True False 默认值:True)--> <!--是否允许删除用户--> <add key="API_DELETEUSER" value="True"/> <!--是否允许重命名用户--> <add key="API_RENAMEUSER" value="True"/> <!--是否允许得到标签--> <add key="API_GETTAG" value="True"/> <!--是否允许同步登录--> <add key="API_SYNLOGIN" value="True"/> <!--是否允许同步登出--> <add key="API_SYNLOGOUT" value="True"/> <!--是否允许更改密码--> <add key="API_UPDATEPW" value="True"/> <!--是否允许更新关键字--> <add key="API_UPDATEBADWORDS" value="True"/> <!--是否允许更新域名解析缓存--> <add key="API_UPDATEHOSTS" value="True"/> <!--是否允许更新应用列表--> <add key="API_UPDATEAPPS" value="True"/> <!--是否允许更新客户端缓存--> <add key="API_UPDATECLIENT" value="True"/> <!--是否允许更新用户积分--> <add key="API_UPDATECREDIT" value="True"/> <!--是否允许向UCenter提供积分设置--> <add key="API_GETCREDITSETTINGS" value="True"/> <!--是否允许获取用户的某项积分--> <add key="API_GETCREDIT" value="True"/> <!--是否允许更新应用积分设置--> <add key="API_UPDATECREDITSETTINGS" value="True"/> <!--API 开关结束--> <!--返回值设置--> <!--返回成功(默认:1)--> <add key="API_RETURN_SUCCEED" value="1"/> <!--返回失败(默认:-1)--> <add key="API_RETURN_FAILED" value="-1"/> <!--返回禁用(默认:-2)--> <add key="API_RETURN_FORBIDDEN" value="-2"/> <!--返回值设置结束--> <!--[必填]通信密钥--> <add key="UC_KEY" value="FD144298AF7E4797A66ACC0C18C97EA3"/> <!--[必填]UCenter地址--> <add key="UC_API" value="http://localhost/ucenter"/> <!--[必填]默认编码--> <add key="UC_CHARSET" value="utf-8"/> <!--[非必填]UCenter IP--> <add key="UC_IP" value=""/> <!--[必填]应用ID--> <add key="UC_APPID" value="2"/>
其中,除了标记必填的,别的都可以不填,默认值就是这个
Asp.net 网站算是搭建成功了,但是现在还没有用到那个类库呢!
类库概况
类库分为以下几个部分
Api 用于提供给 UCenter 调用的结构
Client 用于调用 UCenter 的接口
Model 调用过程中的一些数据封装
UcConfig 静态类,读取上面的配置文件信息
UcUtility 一些常用函数
App.config 配置文件示例
调用 UCenter API
这步非常简单,只要配置好前面的东西,然后简单地调用一个类就行了。
IUcClient client = new UcClient(); var user = client.UserLogin("admin", "admin");//登陆 if (user.Success)//判断是否登陆成功 { client.PmSend(0, 0, "公告", "测试公告", user.Uid);//给该用户发送系统消息 }
那具体有哪些函数可以被调用呢?可以看一下IUcClient接口
using System.Collections.Generic; namespace DS.Web.UCenter.Client { ///<summary> /// UcApi客户端 ///</summary> public interface IUcClient { /// <summary> /// 用户注册 /// </summary> /// <param name="userName">用户名</param> /// <param name="passWord">密码</param> /// <param name="email">Email</param> /// <param name="questionId">登陆问题</param> /// <param name="answer">答案</param> /// <returns></returns> UcUserRegister UserRegister(string userName, string passWord, string email, int questionId = 0, string answer = ""); /// <summary> /// 用户登陆 /// </summary> /// <param name="userName">用户名/Uid/Email</param> /// <param name="passWord">密码</param> /// <param name="loginMethod">登录方式</param> /// <param name="checkques">需要登陆问题</param> /// <param name="questionId">问题ID</param> /// <param name="answer">答案</param> /// <returns></returns> UcUserLogin UserLogin(string userName, string passWord, LoginMethod loginMethod = LoginMethod.UserName, bool checkques = false, int questionId = 0, string answer = ""); /// <summary> /// 得到用户信息 /// </summary> /// <param name="userName">用户名</param> /// <returns></returns> UcUserInfo UserInfo(string userName); /// <summary> /// 得到用户信息 /// </summary> /// <param name="uid">Uid</param> /// <returns></returns> UcUserInfo UserInfo(int uid); /// <summary> /// 更新用户信息 /// 更新资料需验证用户的原密码是否正确,除非指定 ignoreoldpw 为 1。 /// 如果只修改 Email 不修改密码,可让 newpw 为空; /// 同理如果只修改密码不修改 Email,可让 email 为空。 /// </summary> /// <returns></returns> UcUserEdit UserEdit(string userName, string oldPw, string newPw, string email, bool ignoreOldPw = false, int questionId = 0, string answer = ""); /// <summary> /// 删除用户 /// </summary> /// <param name="uid">Uid</param> /// <returns></returns> bool UserDelete(params int[] uid); /// <summary> /// 删除用户头像 /// </summary> /// <param name="uid">Uid</param> void UserDeleteAvatar(params int[] uid); /// <summary> /// 同步登陆 /// </summary> /// <param name="uid">Uid</param> /// <returns>同步登陆的 Html 代码</returns> string UserSynlogin(int uid); /// <summary> /// 同步登出 /// </summary> /// <returns>同步登出的 Html 代码</returns> string UserSynLogout(); /// <summary> /// 检查 Email 格式 /// </summary> /// <param name="email">Email</param> /// <returns></returns> UcUserCheckEmail UserCheckEmail(string email); /// <summary> /// 增加受保护用户 /// </summary> /// <param name="admin">操作管理员</param> /// <param name="userName">用户名</param> /// <returns></returns> bool UserAddProtected(string admin, params string[] userName); /// <summary> /// 删除受保护用户 /// </summary> /// <param name="admin">操作管理员</param> /// <param name="userName">用户名</param> /// <returns></returns> bool UserDeleteProtected(string admin, params string[] userName); /// <summary> /// 得到受保护用户 /// </summary> /// <returns></returns> UcUserProtecteds UserGetProtected(); /// <summary> /// 合并用户 /// </summary> /// <param name="oldUserName">老用户名</param> /// <param name="newUserName">新用户名</param> /// <param name="uid">Uid</param> /// <param name="passWord">密码</param> /// <param name="email">Email</param> /// <returns></returns> UcUserMerge UserMerge(string oldUserName, string newUserName, int uid, string passWord, string email); /// <summary> /// 移除重名用户记录 /// </summary> /// <param name="userName">用户名</param> void UserMergeRemove(string userName); /// <summary> /// 得到用户积分 /// </summary> /// <param name="appId">应用程序Id</param> /// <param name="uid">Uid</param> /// <param name="credit">积分编号</param> /// <returns></returns> int UserGetCredit(int appId, int uid, int credit); /// <summary> /// 检查新消息 /// </summary> /// <param name="uid">Uid</param> /// <returns></returns> UcPmCheckNew PmCheckNew(int uid); /// <summary> /// 发送短消息 /// </summary> /// <param name="fromUid">发件人用户 ID,0 为系统消息</param> /// <param name="replyPmId">回复的消息 ID,0:发送新的短消息,大于 0:回复指定的短消息</param> /// <param name="subject">消息标题</param> /// <param name="message">消息内容</param> /// <param name="msgTo">收件人ID</param> /// <returns></returns> UcPmSend PmSend(int fromUid, int replyPmId, string subject, string message, params int[] msgTo); /// <summary> /// 发送短消息 /// </summary> /// <param name="fromUid">发件人用户 ID,0 为系统消息</param> /// <param name="replyPmId">回复的消息 ID,0:发送新的短消息,大于 0:回复指定的短消息</param> /// <param name="subject">消息标题</param> /// <param name="message">消息内容</param> /// <param name="msgTo">收件人用户名</param> /// <returns></returns> UcPmSend PmSend(int fromUid, int replyPmId, string subject, string message, params string[] msgTo); /// <summary> /// 删除短消息 /// </summary> /// <param name="uid">Uid</param> /// <param name="folder">文件夹</param> /// <param name="pmIds">短消息ID</param> /// <returns>删除的数量</returns> int PmDelete(int uid, PmDeleteFolder folder, params int[] pmIds); /// <summary> /// 删除会话 /// </summary> /// <param name="uid">发件人</param> /// <param name="toUids">收件人</param> /// <returns>删除的数量</returns> int PmDelete(int uid, params int[] toUids); /// <summary> /// 修改阅读状态 /// </summary> /// <param name="uid">发件人</param> /// <param name="toUids">收件人</param> /// <param name="pmIds">短消息ID</param> /// <param name="readStatus">阅读状态</param> void PmReadStatus(int uid, int toUids, int pmIds = 0, ReadStatus readStatus = ReadStatus.Readed); /// <summary> /// 修改阅读状态 /// </summary> /// <param name="uid">发件人</param> /// <param name="toUids">收件人数组</param> /// <param name="pmIds">短消息ID数组</param> /// <param name="readStatus">阅读状态</param> void PmReadStatus(int uid, IEnumerable<int> toUids, IEnumerable<int> pmIds, ReadStatus readStatus = ReadStatus.Readed); /// <summary> /// 获取短消息列表 /// </summary> /// <param name="uid">Uid</param> /// <param name="page">当前页编号,默认值 1</param> /// <param name="pageSize">每页最大条目数,默认值 10</param> /// <param name="folder">短消息所在的文件夹</param> /// <param name="filter">过滤方式</param> /// <param name="msgLen">截取短消息内容文字的长度,0 为不截取,默认值 0</param> /// <returns></returns> UcPmList PmList(int uid, int page = 1, int pageSize = 10, PmReadFolder folder = PmReadFolder.NewBox, PmReadFilter filter = PmReadFilter.NewPm, int msgLen = 0); /// <summary> /// 获取短消息内容 /// 本接口函数用于返回指定用户的指定消息 ID 的消息,返回的数据中包含针对这个消息的回复。 /// 如果指定 touid 参数,那么短消息将列出所有 uid 和 touid 之间的短消息,daterange 可以指定返回消息的日期范围。 /// </summary> /// <param name="uid">Uid</param> /// <param name="pmId">短消息ID</param> /// <param name="toUid">收件人ID</param> /// <param name="dateRange">日期范围</param> /// <returns></returns> UcPmView PmView(int uid, int pmId, int toUid = 0, DateRange dateRange = DateRange.Today); /// <summary> /// 获取单条短消息内容 /// </summary> /// <param name="uid">Uid</param> /// <param name="type">类型</param> /// <param name="pmId">短消息ID</param> /// <returns></returns> UcPm PmViewNode(int uid, ViewType type = ViewType.Specified, int pmId = 0); /// <summary> /// 忽略未读消息提示 /// </summary> /// <param name="uid">Uid</param> void PmIgnore(int uid); /// <summary> /// 得到黑名单 /// </summary> /// <param name="uid">Uid</param> /// <returns></returns> UcPmBlacklsGet PmBlacklsGet(int uid); /// <summary> /// 设置黑名单为禁止所有人(清空原数据) /// </summary> /// <param name="uid">Uid</param> /// <returns></returns> bool PmBlacklsSetAll(int uid); /// <summary> /// 设置黑名单(清空原数据) /// </summary> /// <param name="uid">Uid</param> /// <param name="userName">黑名单用户名</param> /// <returns></returns> bool PmBlacklsSet(int uid, params string[] userName); /// <summary> /// 添加黑名单为禁止所有人 /// </summary> /// <param name="uid">Uid</param> /// <returns></returns> bool PmBlacklsAddAll(int uid); /// <summary> /// 增加黑名单 /// </summary> /// <param name="uid">Uid</param> /// <param name="userName">黑名单用户名</param> /// <returns></returns> bool PmBlacklsAdd(int uid, params string[] userName); /// <summary> /// 删除黑名单中的禁止所有人 /// </summary> /// <param name="uid">Uid</param> /// <returns></returns> void PmBlacklsDeleteAll(int uid); /// <summary> /// 删除黑名单 /// </summary> /// <param name="uid">Uid</param> /// <param name="userName">黑名单用户名</param> void PmBlacklsDelete(int uid, params string[] userName); /// <summary> /// 增加好友 /// </summary> /// <param name="uid">Uid</param> /// <param name="friendId">好友ID</param> /// <param name="comment">备注</param> /// <returns></returns> bool UcFriendAdd(int uid, int friendId, string comment = ""); /// <summary> /// 删除好友 /// </summary> /// <param name="uid">Uid</param> /// <param name="friendIds">好友ID</param> /// <returns></returns> bool UcFriendDelete(int uid, params int[] friendIds); /// <summary> /// 获取好友总数 /// </summary> /// <param name="uid">Uid</param> /// <param name="direction">方向</param> /// <returns>好友数目</returns> int UcFriendTotalNum(int uid, FriendDirection direction = FriendDirection.All); /// <summary> /// 好友列表 /// </summary> /// <param name="uid">Uid</param> /// <param name="page">当前页编号</param> /// <param name="pageSize">每页最大条目数</param> /// <param name="totalNum">好友总数</param> /// <param name="direction">方向</param> /// <returns></returns> UcFriends UcFriendList(int uid, int page = 1, int pageSize = 10, int totalNum = 10, FriendDirection direction = FriendDirection.All); /// <summary> /// 积分兑换请求 /// </summary> /// <param name="uid">Uid</param> /// <param name="from">原积分</param> /// <param name="to">目标积分</param> /// <param name="toAppId">目标应用ID</param> /// <param name="amount">积分数额</param> /// <returns></returns> bool UcCreditExchangeRequest(int uid, int from, int to, int toAppId, int amount); ///<summary> /// 修改头像 ///</summary> ///<param name="uid">Uid</param> ///<param name="type"></param> ///<returns></returns> string Avatar(int uid, AvatarType type = AvatarType.Virtual); /// <summary> /// 得到头像地址 /// </summary> /// <param name="uid">Uid</param> /// <param name="size">大小</param> /// <param name="type">类型</param> /// <returns></returns> string AvatarUrl(int uid,AvatarSize size,AvatarType type = AvatarType.Virtual); /// <summary> /// 检查头像是否存在 /// </summary> /// <param name="uid"></param> /// <param name="size"></param> /// <param name="type"></param> /// <returns></returns> bool AvatarCheck(int uid, AvatarSize size = AvatarSize.Middle, AvatarType type = AvatarType.Virtual); /// <summary> /// 获取标签数据 /// </summary> /// <param name="tagName">标签名</param> /// <param name="number">应用程序ID对应的数量</param> /// <returns></returns> UcTags TagGet(string tagName, IEnumerable<KeyValuePair<string, string>> number); /// <summary> /// 添加事件 /// </summary> /// <param name="icon">图标类型,如:thread、post、video、goods、reward、debate、blog、album、comment、wall、friend</param> /// <param name="uid">Uid</param> /// <param name="userName">用户名</param> /// <param name="titleTemplate">标题模板</param> /// <param name="titleData">标题数据数组</param> /// <param name="bodyTemplate">内容模板</param> /// <param name="bodyData">模板数据</param> /// <param name="bodyGeneral">相同事件合并时用到的数据:特定的数组,只有两项:name、link,保留</param> /// <param name="targetIds">保留</param> /// <param name="images">相关图片的 URL 和链接地址。一个图片地址,一个链接地址</param> /// <returns></returns> int FeedAdd(FeedIcon icon, int uid, string userName, string titleTemplate, string titleData, string bodyTemplate, string bodyData, string bodyGeneral, string targetIds, params string[] images); /// <summary> /// 得到Feed /// </summary> /// <param name="limit">数量限制</param> /// <returns></returns> UcFeeds FeedGet(int limit); /// <summary> /// 得到应用列表 /// </summary> /// <returns></returns> UcApps AppList(); /// <summary> /// 添加邮件到队列 /// </summary> /// <param name="subject">标题</param> /// <param name="message">内容</param> /// <param name="uids">Uid</param> /// <returns></returns> UcMailQueue MailQueue(string subject, string message,params int[] uids); /// <summary> /// 添加邮件到队列 /// </summary> /// <param name="subject">标题</param> /// <param name="message">内容</param> /// <param name="emails">目标Email</param> /// <returns></returns> UcMailQueue MailQueue(string subject, string message, params string[] emails); /// <summary> /// 添加邮件到队列 /// </summary> /// <param name="subject">标题</param> /// <param name="message">内容</param> /// <param name="uids">Uid</param> /// <param name="emails">目标email</param> /// <returns></returns> UcMailQueue MailQueue(string subject, string message, int[] uids, string[] emails); /// <summary> /// 添加邮件到队列 /// </summary> /// <param name="subject">标题</param> /// <param name="message">内容</param> /// <param name="fromMail">发信人,可选参数,默认为空,uc后台设置的邮件来源作为发信人地址</param> /// <param name="charset">邮件字符集,可选参数,默认为gbk</param> /// <param name="htmlOn">是否是html格式的邮件,可选参数,默认为FALSE,即文本邮件</param> /// <param name="level">邮件级别,可选参数,默认为1,数字大的优先发送,取值为0的时候立即发送,邮件不入队列</param> /// <param name="uids">Uid</param> /// <param name="emails">目标email</param> /// <returns></returns> UcMailQueue MailQueue(string subject,string message,string fromMail,string charset,bool htmlOn,int level,int[] uids,string[] emails); } }
这份 API 是根据 UCenter API 开发手册开发的
所有的API都在里面了,不用考虑实现细节,配置好以后直接调用即可!
供 UCenter 调用的接口
这里,我们现在网站下新建一个叫 API 的文件夹(一定要叫 API)
然后再创建一个 ashx 文件(文件名和前面的配置对应即可,上面用的是 uc.ashx ,只要对应即刻,没必要用 uc.php)
结构如下:
uc.ashx 修改如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using DS.Web.UCenter.Api; namespace DS.Web.UCenter.Website.API { /// <summary> /// Summary description for uc /// </summary> public class uc:UcApiBase { public override ApiReturn DeleteUser(IEnumerable<int> ids) { throw new NotImplementedException(); } public override ApiReturn RenameUser(int uid, string oldUserName, string newUserName) { throw new NotImplementedException(); } public override UcTagReturns GetTag(string tagName) { throw new NotImplementedException(); } public override ApiReturn SynLogin(int uid) { throw new NotImplementedException(); } public override ApiReturn SynLogout() { throw new NotImplementedException(); } public override ApiReturn UpdatePw(string userName, string passWord) { throw new NotImplementedException(); } public override ApiReturn UpdateBadWords(UcBadWords badWords) { throw new NotImplementedException(); } public override ApiReturn UpdateHosts(UcHosts hosts) { throw new NotImplementedException(); } public override ApiReturn UpdateApps(UcApps apps) { throw new NotImplementedException(); } public override ApiReturn UpdateClient(UcClientSetting client) { throw new NotImplementedException(); } public override ApiReturn UpdateCredit(int uid, int credit, int amount) { throw new NotImplementedException(); } public override UcCreditSettingReturns GetCreditSettings() { throw new NotImplementedException(); } public override ApiReturn GetCredit(int uid, int credit) { throw new NotImplementedException(); } public override ApiReturn UpdateCreditSettings(UcCreditSettings creditSettings) { throw new NotImplementedException(); } } }
本来呢,ashx 继承的是 IHttpHandler ,但是呢,我们需要修改一下,让它继承 UcApiBase
它是一个抽象类,重写抽象方法即可。
但是具体怎么用呢?
这些函数不是给你调用的,是给 UCenter 调用的,你要做的就是写一些逻辑代码。比如 UCenter 告诉你 有人登陆了 (SynLogin函数)
那你应该做点什么呢? 写 Cookie ?写 Session ? 都行~
同样,当 UCenter 同步登出的时候,你也需要写一些逻辑代码,清理 Cookie 或者 Session
另外几个函数是干嘛的呢? 参考 UCenter 接口开发手册中的 API接口
前一段时间在 MVC 的网站中使用了自己的 UCenter API
但是出现了一个问题:
MVC 下可以建立静态文件,路由的时候如果存在静态文件则直接访问,包括 aspx, asxh 等文件。
像原来一样,建立了 uc.ashx 文件,但是在使用的时候却出现了一个问题:无法访问 Session
HttpContext 里的 Session 对象是 null
就算继承了 IRequiresSessionStat 接口后还是一样
那如何解决呢?
本来想从底层想办法,但是发现 Controller 差异太大,所以放弃。
后来发现,其实有个很简单的方法,直接在 Controller 里新建该对象即可。
namespace MVC.Controllers { public class APIController : Controller { public ActionResult Uc() { new UcBase().ProcessRequest(System.Web.HttpContext.Current); return Content(""); } } }