很早就对开发微博应用有兴趣了,不过苦于没有idea,所以一直搁置着。碰巧前几天看到乌云说微博的提醒接口可以查看任意用户的未读信息数,因此决定开发一个相关应用练手,用了两天时间开发。当然这不可能是正常的应用,毕竟不知这是否漏洞,新浪会否修复,况且这应用也没什么用……

 

    首先如果要使用微博接口,必须要创建应用,相关地址:http://open.weibo.com/development  创建一个站内应用就可以了。既然是站内应用,我也要有一个属于自己的站点,因此我使用新浪SAE搭建了自己的服务器,相关地址:http://sae.sina.com.cn/?m=appwizard 。新建站点前要要先安装一款应用或框架,然后就可以通过上传文件或SVN管理站点,也可以管理数据库和在线编辑文件。在应用商店中安装一款开发框架后,这样就拥有一个已经包含DEMO的站点,并且也包含了一些微博类和数据库操作类。直接include_once( 'saetv2.ex.class.php' );就可以通过这个类调用微博API了,关于API文档请浏览 http://open.weibo.com/wiki/API%E6%96%87%E6%A1%A3_V2。SAE里也可以安装web应用例如WP,不过我认为那不是用来开发微博应用的,并且安装时消耗较多的内存和磁盘空间,因此安装框架才能真正搭建自己打造的站点。(详细关于建立SAE站点和微博应用请参考文章末尾的相关链接)

    然后在应用的基本信息中设置应用实际地址为刚刚搭建的站点地址,在高级信息中添加测试用户。最后修改DEMO输出 $_SESSION['oauth2']['oauth_token'] 的值,这个值也称为access_token,说明该测试用户授权给你的应用,通过这个值就可以使用OAuth2.0调用微博API了。因此有了access_token就可以把测试环境设置在本地而不浪费SAE资源。不过注意OAuth2.0中的access_token默认过期时间是1天,也就是说只要在程序中直接使用这个值就可以让任何人不授权也能使用应用,不过有效时间为一天。

    注意,以上直接使用方法不是正常应用使用的方法,正常应用开发完成后,应该提交并审核,通过后其它微博用户就能授权并且使用这个应用,不同的用户授权获得不同的access_token,通过这个值使用API就能查看对应用户的信息或者进行各种操作了。程序中只要直接使用$_SESSION['oauth2']['oauth_token']作为微博类的构造参数即可。简而言之,我的这个应用之所以免授权,原理是通过添加的测试用户授权而得到access_token然后手工赋值,最后才能给别人试用的。

    而微博未读信息提醒接口的说明在 http://open.weibo.com/wiki/2/remind/unread_count ,由于要输入uid作为参数,而查询用户输入的是微博昵称,因此需要进行转换,所以用到了这个接口 http://open.weibo.com/wiki/2/users/show

 

    剩下的就是界面设计、数据库设计和编写代码。我只用了一个脚本,并且与DEMO的文件无关除了配置文件,说一下要注意的地方吧。

    include配置文件和微博设置access_token,并且使用htmlspecialchars处理输入数据。

  1. include_once'config.php' ); 
  2. include_once'saetv2.ex.class.php' ); 
  3.  
  4. $access_token = "2.00SaOjjC2PrzYBc99be8c025xsHYpB"
  5. $name = htmlspecialchars( trim($_POST['username']) ); 

    由于这是免授权应用,因此要手动设定$access_token并作为参数输入,SaeTClientV2包含在saetv2.ex.class.php中,通过这个类就可以调用微博API了。show_user_by_name()能通过微博昵称获取用户信息。

  1. $c = new SaeTClientV2( WB_AKEY , WB_SKEY , $access_token  ,'' );  
  2. $users = $c->show_user_by_name( strval($name) ); 

     表单的制作也很简单,就一个输入框和按钮。由于表单设置成POST给自身,因此必须添加一个hidden,以此判断是否提交表单,譬如使用if( $_POST['action'] == 'unread' )。特别当输入空值时能分清是刚打开页面还是已经提交了表单不过输入为空。另外输入空值和输入空白字符是不同概念。

  1. <div style="padding: 10px;background: #e9e9e9;text-align: center;"> 
  2. <form action="" method="POST"> 
  3. 用户昵称:<input type="text" id="username" name="username" class="input" maxlength="32" value="<?=$name?>"/> 
  4. &nbsp;<input type="submit" value="查询" class="bt"/> 
  5. <input type="hidden" name="action" value="unread" /> 
  6. </form></div> 

    如果通过users/show接口顺利获得用户ID,就再使用该ID调用remind/unread_count接口获取并显示。否则出现错误就通过返回的error_code判断错误原因,暂时知道以下四个错误。

  1. if( isset($users['id']) ){ 
  2.   //这里就是输出查询结果,代码省略
  3. elseif( isset($users['error_code']) ){ 
  4.     if$users['error_code'] == 10006 )//source paramter(appkey) is missing 
  5.         p("此应用已过期,如需继续使用,请联系作者,谢谢!"); 
  6.     elseif$users['error_code'] == 10008 ) 
  7.         p("请输入用户昵称!"); 
  8.     elseif$users['error_code'] == 20003 ) 
  9.         p("用户“".$name."”不存在!"); 
  10.     elseif$users['error_code'] == 21327 )//expired_token 
  11.         p("access_token已过期,如需继续使用,请联系作者,谢谢!"); 
  12.     else 
  13.         p("出现错误:".$users['error_code']); 
  14.     $result = json_encode($users); 
  15. else {
  16.     p("出现未知错误,请重新进行查询!"); 
  17.     $result = json_encode($users); 

    为了提高用户体验,最后使用JS把输入焦点设置在输入框,方便用户输入和多次查询。

  1. <script type="text/javascript"
  2. document.getElementById('username').focus(); 
  3. </script> 

    关于数据库操作使用SAE封装好的类来操作,而数据表早已通过SAE设计好。主键id自增,name如果查询成功是微博昵称否则是用户输入的名字,uid如果查询成功返回微博用户ID否则为0(此数据除了避免用户改昵称,还可以分辨查询操作是否成功),ip判断使用人数,time是必须的而我使用时间戳来记录可读性不强,result就是接口返回的json结果,无论成功与否都会记录下来。

  1. $mysql = new SaeMysql(); 
  2. $mysql->runSql("INSERT INTO {$dbtable} (id, name, uid, ip, time, result) VALUES ('', '$name', '$uid', '$ip', '$timestamp', '$result')"); 
  3. $mysql->closeDb(); 

 

    最后是该应用的示范截图,界面比较简洁,通过两个接口获取的消息也不多。

 

编写代码总结:

表单要通过hidden的input判断进行何种操作;

要处理好用户的任何输入并且及时反馈;

每次使用函数都要注意失败的情况;

关于界面居中问题,主div使用margin: 0 auto;居中,它指的是让容器自己居中。表单和标题使用text-align: center;,它指的是让容器中的元素居中。当然可以对块状元素使用margin: 0 auto;居中,不过前提是这个块的宽度小于父容器,并且不代表块内元素会居中。

如果SaeTClientV2类没有新接口的相关函数(譬如unread_count未读提醒接口),可以直接使用SaeTOAuthV2类的get()函数,为了统一变量,因此其实以下代码才是我实际上的写法,而不是像上面所说那样使用SaeTClientV2类。

  1. $o = new SaeTOAuthV2( WB_AKEY, WB_SKEY, $access_token, NULL ); 
  2.  
  3. //通过$_POST['action']判断是否提交表单,而不是判断$_POST['username']是否为空 
  4. if$_POST['action'] == 'unread' ){ 
  5.     $showparams = array(); 
  6.     $showparams['screen_name'] = strval($name); 
  7.     $users = $o->get('users/show'$showparams); 
  8.      
  9.     if( isset($users['id']) ){ 
  10.         $unreadparams = array(); 
  11.         $unreadparams['uid'] = $users['id'];; 
  12.         $remind = $o->get('remind/unread_count'$unreadparams); 
  13.         if($remind){ 
  14. //输出查询结果 

 

最后是在微博公开应用,时间是2月9日21:16,经过24小时后统计了一些信息。

使用次数353,包括测试数据若干:

  1. SELECT count(*) FROM `temp_unread` WHERE time < 1328879760 

使用人数33:

  1. SELECT count(*) FROM ( SELECT DISTINCT ip FROM `temp_unread` WHERE time < 1328879760 ) AS t 

被搜索的名字一共有87个,包括输入错误情况:

  1. SELECT count(*) FROM ( SELECT DISTINCT name FROM `temp_unread` WHERE time < 1328879760 ) AS t 

不包括错误情况,共有74个:

  1. SELECT count(*) FROM ( SELECT DISTINCT uid FROM `temp_unread` WHERE time < 1328879760 AND uid != 0 ) AS t 

通过数据库的记录,发现两个不同用户同时使用这个应用时,有可能返回null,我不知这是新浪的问题还是我程序不够完善,也不知如何应付这种情况。

其它统计信息:

HTTP流入354.01 KB

HTTP流出1.27 MB

MySQL磁盘消耗7.35 KB

SAE资源消耗0.29云豆。

 

相关文档:

站内应用开发指南

SAE入门指引

Oauth2说明

SaeTOAuthV2类说明

SaeTClientV2类说明

SaeMysql类说明