老韩
8三/100

为Repoze.who开发的SSO插件

  在架设ClueMapper的过程当中,由于ClueMapper是基于repoze.who做为登录认证模块的,所以为了适应需求,对repoze.who做了不少的改造,比如之前说过的给repoze.who加上LDAP认证即是改进之一。当然,在后来使用的过当中,并没有完全依赖于LDAP的谁机制,而是使用了SSO认证机制,这是目前很多公司内部普遍使用的认证机制之一。

  其实这是第一次尝试写Repoze.who的中间件,在写之前仔细看了一遍repoze.who的代码,发现了不少有意思的东西,比如中间件的机制,等回头有时间,可以专门写一篇repoze.who解读。当然这是题外话,现在要说的,是这个SSO插件。

  代码很简单,由于现在我们使用的SSO认证仅仅基于HTTP协议,所以只用urllib2就够了。下面是代码:

  1. # -*- coding: utf-8 -*-
  2. """
  3. SSO Plugins for Repoze.who
  4.  
  5. Author: handaoliang <handaoliang@gmail.com>
  6.  
  7. Basic usage:
  8.  
  9. >>> from repoze.who.plugins import sso
  10. >>> sso_auth = sso.SSOAuthenticatorPlugin(SSO_URI,SSO_ORG_ID,SSO_SUB_ID,REMOTE_ADDR)
  11. >>> authenticators = [('sso_auth', sso_auth)]
  12. >>> pam = middleware.PluggableAuthenticationMiddleware(
  13.         authfilter,
  14.         identifiers,
  15.         authenticators,
  16.         challengers,
  17.         mdproviders,
  18.         classifier,
  19.         challenge_decider,
  20.         )
  21.  
  22. """
  23.  
  24. from zope.interface import implements
  25. import hashlib
  26. import urllib2
  27.  
  28. from repoze.who.interfaces import IAuthenticator, IMetadataProvider
  29.  
  30. class SSOAuthenticatorPlugin(object):
  31.  
  32.     implements(IAuthenticator)
  33.  
  34.     def __init__(self, ssoURI, orgID, subID,ipADDR):
  35.         if ssoURI is None:
  36.             raise ValueError('The SSO URL must be specified')
  37.         self.sso_uri = ssoURI
  38.         self.org_id = orgID
  39.         self.sub_id = subID
  40.         self.ip_address = ipADDR
  41.  
  42.     def authenticate(self, environ, identity):
  43.         try:
  44.             user_name = identity['login']
  45.             password = identity['password']
  46.         except (KeyError, TypeError, ValueError):
  47.             return None
  48.  
  49.         auth_password = hashlib.md5(password).hexdigest().upper()
  50.  
  51.         auth_url = "%s?orgname=%s&sub=%s&user=%s&pwd=%s&ip=%s" \
  52.                 % (self.sso_uri,self.org_id,self.sub_id,user_name,auth_password,self.ip_address)
  53.        
  54.         #connecting....
  55.         try:
  56.             request_handle = urllib2.Request(auth_url)
  57.             contents_handle = urllib2.urlopen(request_handle)
  58.             return_data = contents_handle.read()
  59.  
  60.             _status = return_data.split("|")
  61.             if int(_status[0]) == 1:
  62.                 return user_name
  63.             else:
  64.                 return None
  65.         except Exception,e:
  66.             #catched this error:
  67.             return None
  68.   
  69.     def __repr__(self):
  70.         return '<%s %s>' % (self.__class__.__name__, id(self))

使用方法:

  1. from repoze.who.plugins import sso
  2.  
  3. SSO_URI = "http://192.168.0.1:8000/SsoCertify";
  4. SSO_ORG_ID = 2
  5. SSO_SUB_ID = 992
  6. REMOTE_ADDR = '127.0.0.1'
  7.  
  8. sso_auth = sso.SSOAuthenticatorPlugin(SSO_URI,SSO_ORG_ID,SSO_SUB_ID,REMOTE_ADDR)
  9. authenticators = [('sso_auth', sso_auth)]
  10. pam = middleware.PluggableAuthenticationMiddleware(
  11.     authfilter,
  12.     identifiers,
  13.     authenticators,
  14.     challengers,
  15.     mdproviders,
  16.     classifier,
  17.     challenge_decider,
  18.     )

本人较懒,没有做安装包,需要安装包的可以联系我。如果有多人需要,将考虑将它更完善。

23二/101

取得某个文件夹下所有同类型文件

PHP有一个模块叫做mime_magic,用来判断文件的类型非常有效。使用它的mime_content_type函数,结合递归可以很方便的把某一个文件夹下的所同类型文件找出来,比如我因为某种需求要取出某个文件夹下所有的纯文本文件,即是写了一小段代码来递归(代码片段见下文)。

当然,这里并不是展示什么技巧性的东西,只是介绍性的说明一下有这样一个模块。记得很多年前初学写程序时,是根据文件的后缀来判断文件类型的,显然这是一件多么不靠谱的事情,如果现在还有人这么干,我想是要被耻笑的吧?脚本语言讨人喜爱之处就是它总是为我们提供很多很实用的库,这大大降低了我们的开发成本以及提高了开发效率,虽然,也因此增加了选择成本。比如我们刚刚所说的判断文件类型,即有人建议用PECL的FileInfo库来做。

需要连带说明一下,在Python中对MIME类型的处理,也有内置的一个模块叫mimetypes,可以很方便的取得一个文件的类型。

  1. >>> import mimetypes
  2. >>> mimetypes.guess_type('/home/handaoliang/bodybg.jpg')
  3. ('image/jpeg', None)

PHP取得某文件夹下所有的纯文本文件:

  1. #!/var/iapps/php/bin/php
  2. <?php
  3. class listFiles{
  4.     public $fileLists;
  5.  
  6.     public function __construct(){
  7.         $this->fileLists = array();
  8.     }
  9.  
  10.     public function excuteList($baseDirName)
  11.     {
  12.         $myDirObj = dir($baseDirName);
  13.  
  14.         while($fileName = $myDirObj->read()){
  15.             //如果获取到的文件属性是目录,并且不是.或者..,则再遍历一次。
  16.             if((is_dir($baseDirName."/".$fileName)) && ($fileName != ".") && ($fileName != "..")){
  17.                 listFiles::excuteList("$baseDirName/$fileName");
  18.             }else if(($fileName != ".") && ($fileName != "..")){
  19.                 //判断类型,将文本文件放到数组里。
  20.                 if("text/plain" == mime_content_type($baseDirName."/".$fileName)){
  21.                     array_push($this->fileLists,$baseDirName."/".$fileName);
  22.                 }
  23.                 //echo mime_content_type($baseDirName."/".$fileName)."\n";
  24.                 //$this->getFileType($baseDirName."/".$fileName);
  25.                 //echo "\n";
  26.                 //echo $baseDirName."/".$fileName."\n";
  27.             }
  28.         }
  29.  
  30.         $myDirObj->close();
  31.     }
  32. }
  33.  
  34. $fileListObj = new listFiles();
  35. $fileListObj->excuteList("/home/handaoliang/meeuu.com");
  36. $fileLists = $fileListObj->fileLists;
  37. print_r($fileLists);
  38. ?>
分类: PHP, Python 1个评论
22二/100

给ClueMapper加上LDAP验证功能

一、什么是ClueMapper?

关于ClueMapper(官方网站:http://www.cluemapper.org)是什么东东,还是直接翻译它自己的文档吧:ClueMapper是一套基于WEB的、用于软件或者软件项目管理的应用程序,目前使用Trac来管理单个项目,同时增加了新建Trac实例、支持配置SVN等功能(ClueMapper is a web-based application for managing software and software-based consulting projects. Currently it uses Trac to handle individual projects but adds the ability to create new trac instances, svn configurations, etc.)简而言之:ClueMapper是一个基于Trac的(最新版使用的Trac版本是:Trac-0.11.1)、并且可新增Trac实例的多项目管理软件,并且支持SVN。因为基于Trac,所以ClueMapper也是Python语言环境下运行的软件。

ClueMapper的认证机制并没有延用Trac的机制,而是自己独立做了一系列的改进,主要是加入了repoze.who这个WSGI中间件,这样的话就意味着,只要repoze.who支持的登录方式,ClueMapper都支持,比如basicauth、htpasswd等。

然而不幸的是,在repoze.who支持的诸多认证方式中,恰恰没有ldap的验证支持,这就使得我们必须对ClueMapper的代码进行改进,以加进Ldap的认证支持进去。

  
二、安装和准备

1、安装ClueMapper
关于ClueMapper的安装,只简单的介绍几句,参照ClueMapper的安装文档可以很快上手:
去SVN取得CueMapper的源代码,编译即可。当然,在编译之前,需要先确定是否安装了Python的libxslt和sqlite3支持:

  1. svn co http://www.cluemapper.org/svn/buildout/trunk cluemapper
  2. cd cluemapper
  3. python bootstrap.py
  4. bin/buildout

buildout的过程会稍微慢一点,因为要下载一些包,等到Buildout完成,运行:
./bin/clue-server
不出意外的话,应该会看到如下提示:
2010-02-22 15:39:14,245 INFO [cluemapper] ClueMapper v0.8.4dev-r0-20100222 starting...
2010-02-22 15:39:14,246 INFO [cluemapper] Using configuration at 'etc/cluemapper/cluemapper.ini'
2010-02-22 15:39:14,246 INFO [cluemapper] Listening on ALL INTERFACES, port 8080
2010-02-22 15:39:14,246 INFO [cluemapper] Browse at http://127.0.0.1:8080

此时ClueMapper监听在8080端口。当然,如果系统的Python环境是2.6以上版本,则会碰到类似“DeprecationWarning: the md5 module is deprecated; use hashlib instead import md5”的Warning,这是因为Python2.6以后,不建议直接import md5模块的原因所导致,如果要去掉这个warning,只需要在所有import md5的地方,将之替换为import hashlib模块即可:
hashlib的MD5 Hash函数用法:

  1. import hashlib
  2. _s = hashlib.md5("hash string").hexdigest();

2、安装LDAP
其实自己编译安装ldap,是最靠谱的方法,虽然之前写过介绍Ubuntu下使用apt-get install方法安装ldap的文章,但个人其实非常反感apt-get安装的方式,更喜欢手动Configure安装(貌似是BSD系技术人员的通病?呵呵),安装Ldap之前,需要先安装BDB,我推荐使用BDB做为LDAP的支援数据库。

  1. wget ftp://ftp.openldap.org/pub/OpenLDAP/openldap-release/openldap-2.4.21.tgz
  2. tar xzvf openldap-2.4.21.tgz
  3. ./configure --prefix=/var/iapps/ldap
  4. make && make depend
  5. make install

配置ldap的Conf文件:

  1. $sudo vi /var/iapps/ldap/etc/openldap/slapd.conf

在头部加上:

  1. include     /var/iapps/ldap/etc/openldap/schema/core.schema
  2. # added by handaoliang...
  3. include /var/iapps/ldap/etc/openldap/schema/cosine.schema
  4. include /var/iapps/ldap/etc/openldap/schema/misc.schema
  5. include /var/iapps/ldap/etc/openldap/schema/inetorgperson.schema
  6. include /var/iapps/ldap/etc/openldap/schema/openldap.schema
  7. include /var/iapps/ldap/etc/openldap/schema/nis.schema

在最后加上:

  1. database    bdb
  2. suffix      "dc=handaoliang,dc=com"
  3. rootdn      "cn=Manager,dc=handaoliang,dc=com"
  4. # Cleartext passwords, especially for the rootdn, should
  5. # be avoid.  See slappasswd(8) and slapd.conf(5) for details.
  6. # Use of strong authentication encouraged.
  7. rootpw      iloveyou
  8. # The database directory MUST exist prior to running slapd AND
  9. # should only be accessible by the slapd and slap tools.
  10. # Mode 700 recommended.
  11. directory   /var/iapps/ldap/var/openldap-data
  12. # Indices to maintain
  13. index   objectClass eq

启动Ldap:

  1. $sudo /var/iapps/ldap/libexec/slapd

导入用户,测试Ldap安装成功。

3、安装repoze.who的ldap扩展:
安装repoze.who的ldap扩展之前,需要先安装python-ldap(http://pypi.python.org/pypi/python-ldap/2.3.10):

  1. wget -c http://pypi.python.org/packages/source/p/python-ldap/python-ldap-2.3.10.tar.gz
  2. tar -xzvf python-ldap-2.3.10.tar.gz
  3. python setup.py install

安装成功,写一段代码测试一下用Python来连Ldap:

  1. #!/usr/bin/python
  2. #-*- coding:utf-8 -*-
  3.  
  4. import ldap
  5.  
  6. try:
  7.     conn = ldap.open("localhost")
  8.     conn.protocol_version = ldap.VERSION3
  9.     username = "Manager"
  10.     password = "{SSHA}2JPeP6Y+3/N2n8orp+jwKZj1TN310DBw"
  11.     conn.simple_bind(username,password)
  12. except ldap.LDAPError, e:
  13.     print e
  14.  
  15. baseDN = "ou=People,dc=handaoliang,dc=com"
  16. searchScope = ldap.SCOPE_SUBTREE
  17.  
  18. retrieveAttributes = ["cn","userPassword","loginShell","uid"]
  19. searchFilter = "cn=dev"
  20.  
  21. try:
  22.     ldap_result_id = conn.search(baseDN,searchScope,searchFilter,retrieveAttributes)
  23.     result_set = []
  24.     while 1:
  25.         result_type, result_data = conn.result(ldap_result_id, 0)
  26.         if result_data == []:
  27.             break
  28.         else:
  29.             if result_type == ldap.RES_SEARCH_ENTRY:
  30.                 result_set.append(result_data)
  31.  
  32.         #print result_set[0][0][1]['o'][0]
  33.         print result_set
  34.  
  35. except ldap.LDAPError, e:
  36.     print e

返回正确结果,说明环境已经没有问题。然后安装repoze.who的ldap扩展,注意这里不从源码包里运行setup.py安装,而是直接把源码包里的ldap代码拷贝到ClueMapper的repoze.who eggs里,这样的话就避免了安装在Python的系统Libs里,因而方便修改它的源文件,后面会讲到:
到这里下载:http://pypi.python.org/pypi/repoze.who.plugins.ldap/1.0,取得1.0版本的tar.gz包,并解压:

  1. tar xzvf repoze.who.plugins.ldap-1.0.tar.gz
  2. cp -r repoze.who.plugins.ldap-1.0/repoze/who/plugins/ldap/ /home/handaoliang/cluemapper/eggs/repoze.who-1.0.18-py2.6.egg/repoze/who/plugins/

运行bin下面的python脚本:
$./bin/python
进入到python命令行:

  1. from repoze.who.plugins import ldap as repoze_ldap
  2. help(repoze_ldap)

如果import没有出错,并且看到File为:/home/handaoliang/cluemapper/eggs/repoze.who-1.0.5-py2.6.egg/repoze/who/plugins/ldap/__init__.py,则说明安装成功。

三、对repoze.who.plugins.ldap和ClueMapper的验证文件进行修改:
1、对repoze.who.plugins.ldap的修改:

先是repoze.who的ldap扩展,此时需要编辑/home/handaoliang/cluemapper/eggs/repoze.who-1.0.5-py2.6.egg/repoze/who/plugins/ldap/plugins.py文件:
查找:import ldap
在下面加上一句:ldap.set_option(ldap.OPT_REFERRALS, 0),变成:

  1. import ldap
  2. ldap.set_option(ldap.OPT_REFERRALS, 0)

查找:self.ldap_connection.simple_bind_s(dn, password)
将下面的return dn注释掉,改成:return identity['login']
修改后的代码如下:

  1. try:
  2.     self.ldap_connection.simple_bind_s(dn, password)
  3.     # The credentials are valid!
  4.     # bug fixed by handaoliang...Return userid but not dn.
  5.     return identity['login']
  6.     #return dn
  7. except ldap.LDAPError,e:
  8.     #by handaoliang:catched this error:
  9.     #sys.stderr.write("%s\n" % e)
  10.     return None

2、对ClueMapper的验证文件进行修改,编辑src/ClueMapper/src/clue/secure/auth.py文件:

  1. $vi /home/handaoliang/cluemapper/src/ClueMapper/src/clue/secure/auth.py

查找:from repoze.who.plugins import basicauth,在后面把repoze.who.plugins.ldap包含进来:

  1. from repoze.who.plugins import basicauth
  2. #added by handaoliang
  3. from repoze.who.plugins import ldap as repoze_ldap

查找:assert passwdfile,在后面加上如下语句:

  1. #added by handaoliang
  2. #LDAP Config
  3. LDAP_BASE_HOST = 'ldap://localhost'
  4. LDAP_BASE_DN = 'ou=People,dc=handaoliang,dc=com'
  5.  
  6. #Add Plugins to repoze.who.
  7. ldap_auth = repoze_ldap.LDAPAuthenticatorPlugin(LDAP_BASE_HOST,LDAP_BASE_DN)

修改authenticators = [('ldap_auth', ldap_auth)]为authenticators = [('htpasswd', passwd),('ldap_auth', ldap_auth)]

  1. #Modified by handaoliang..
  2. #authenticators = [('ldap_auth', ldap_auth)]
  3. authenticators = [('htpasswd', passwd),('ldap_auth', ldap_auth)]

重新启动ClueMapper,访问:http://localhost:8080/此时即应该可以使用LDAP的账号密码登录。

您可以从这里下载到经过我修改的源码包:http://www.handaoliang.com/tools/fix_cluemapper.tar.gz

您可参照里面的README文件来进行安装:

#-*- coding:utf-8 -*-
INSTALL:
1.把auth.py放到$ClUEMAPPER_PATH/src/ClueMapper/src/clue/secure下面,覆盖原文件,覆盖前请备份原文件。
2.修改LDAP配置(编辑auth.py):
  A.更改LDAP_BASE_HOST为LDAP数据库的HSOT连接,如ldap://localhost
  B.更改LDAP_BASE_DN为LDAP的Base dn
  C.保存文件。
3.将ldap文件夹拷贝到repoze.who-1.0.5-py2.6.egg下的repoze/who/plugins下面。其它的无须更改。

==============================
参考文档:
http://code.gustavonarea.net/repoze.who.plugins.ldap/Using.html
http://www.packtpub.com/article/installing-and-configuring-the-python-ldap-library-and-binding-to-an-ldap-directory
http://wiki.pylonshq.com/display/pylonscookbook/Authentication+and+Authorization+with+%60repoze.who%60
http://www.openldap.org/doc/admin24/quickstart.html

14十一/093

尝试Python For S60手机开发

  生活中,我们似乎总能碰到那么一群人:爱好新奇事物、敢于探索和研究、不计回报分享经验。通常,我们称这种人叫“奇客”或者“极客”,他们有一个英文专用名词叫“Geek”--如你所见,这个词已然脱离了它的原义,不再是贬义的“畸形人”或者“野人”,甚至是“傻瓜”、“呆子”,而是成了对大多数能熟练应用各种开发工具、对新鲜事物有着相当的敏感、热衷于分享以及对开源事业有着近乎虔诚信仰的牛人的尊称。IT圈子里,如果有人能够称你为“Geek”,那绝对是一种荣耀,且不管在你的思维里是骂人抑或是有意贬低。这种人我身边不少,典型的比如QYB同学以及Alvayang同学。这俩人爱好新事物已经到了近乎BT的地步。同时技术也的确高超,经常有事没事给我灌输一些闻之未闻的东西,比如前一段时间,这俩家伙在办公室拿着笔记本电脑玩起了乒乓球,羡煞我们,纠结半天原来是他们利用笔记本的硬盘保护技术原理,写了一个模拟自由落体的重力感应程序,籍此来欺骗我们幼小而纯洁的心灵。
  这里先交代这么一段,事实上和本文并没有太大的关系,写下来因为想说明,我之所以会去研究这么一些杂七杂八的东西,或多或少是受了他们的影响,事实上我对这些东西是不怎么感冒的,因为在我看来这些纯粹是过把瘾就歇菜的体验。虽说科学技术是第一生产力,但在我看来它还取决于两个前提:一是你研究的科学技术是否有实用价值,二是你研究的技术假设能转化成生产力,需要投入多大的成本。倘若仅仅是体验式的去玩,倒不如回家背几段唐诗宋词四书五经,兴许更能体悟到“死生契阔,富贵在天”的真理(这人怎么这么扯?这二者貌似没什么关系)。
  但还是得研究呀,为什么呢?首先一个是,倘若跟他们混在一起,不懂是要被鄙视的;其次,丫的这俩人长得太帅了,老子百试不爽的阿Q思维(长得帅的没有我程序写得好、程序写得好的没有我帅)在他们面前完全不起作用。尤其是QYB同学,据江湖传闻,这家伙初进SOHU的时候,被诸多甭管已婚未婚的姑娘们追着问:你结婚了没有?那意思基本上就等同于:想不想出轨?想出轨跟我来!而老夫在SOHU两年多,却从来没有收到过女同胞类似的关怀,煞是凄凉,只恨得仰天长叹:苍天无眼呀,我帅得这么具体居然会没人看上我!(有会拉二泉映月的吗?给我来一段,眼泪酝酿中。)

  倒,废话又有点多了,下面转入正题:

  一、准备工作。
  (一)硬件:
    1、没有蓝牙的请准备一根蓝牙棒,有蓝牙设备的请启用蓝牙设备。
    2、S60V3手机或PDA,有蓝牙支持。
  (二)软件:
  1、Python for s60V3。
  下载地址是:https://garage.maemo.org/projects/pys60/,目前最新版本是1.9.7,Python内核是:2.5.4。
  在这里要鄙视一下Nokia,文档更新太慢了,搞得老夫先是从Nokia官网上跑到了SourceForge,然后从SF.net上下载到一个文本文件,在文本文件里才找到最终的项目主页地址。
  2、putty
  目前最新的版本是0.6,支持Socket远程连接,可以直接通过蓝牙的COM端口连接到pys60的Console上去进行开发。关于这个,可以参考QYB同学之前的一篇文章:《尝试了一下 Python for S60》,当然他这篇文章有一个问题,就是讲得不够详细,从他文章下面的留言就可以看出,还是很多人不知道怎么用putty来连pys60的Console,所以在接下来的过程当中,我也会图文并茂地跟大家讲解如何连上去。因为在这个过程当中,我也是折腾了半天才弄清楚的。

  二、安装:
  安装的过程就比较简单了,首先是下载到Python for s60V3,下这个文件:

  这是一个tar.gz文件,Windows下可以用WinRAR打开,打开后会看到这样一个PyS60Dependencies文件夹:

  事实上只要安装三个文件就行了:

  其中:ssl_nokia_1_5_7.SIS,是ssl lib,可不安装,Python_1.9.7.sis是主文件,必须安装,PythonScriptShell_1.9.7_3_2.sis是Python在s60下的GUI程序,要通过它来启动Python。
  你可以用蓝牙将文件传到设备上安装,或者通过Nokia的套件来安装。无论如何,我们安装好了,试着跑两行代码,在我E71上的截图如下:

  三、尝试开发
  那么到这里,我们已经成功把Python安装到S60上去了,同时也能跑,那么接下来就试着从Windows上用Putty来连接手机上的Python Colsole,毕竟在手机上敲代码实在是件不靠谱的事情。在此之前我们需要说明一下,在我的Windows XP环境下,Bluetooth在连接S60之后,默认会把Outgoing的端口打开,而Incoming的端口是需要手工添加的,这一点与有些文章里所描述的不是一样,我不知道他们当时的环境是怎么样的,也许是基于1代Bluetooth的设备,安装驱动程序后会默认打开Incoming的COM端口吧,但是至少我这边是手工添加之后才能打开Incoming端口的。

  首先设置Bluetooth:
  在Bluetooth设备上点右键,然后选“打开Bluetooth设备”(如果快捷栏没有图标,请在控制面板里找,呃,怎么搞得像是菜鸟教程一样?呵呵),在打开的面板上,选择“COM端口”,点击下面的添加按钮,然后再在弹出的面板上再选择“传入(设备初始化连接)”,当然,这是默认的,直接点确定就可以了。这时候,我们会看到面板上多了一个传入的端口,我当前机器的这个端口在COM8上。

  这一步设置成功,就意味着蓝牙的Incomming端口已经初始化成功,此时就可以用Putty去连这个端口了。我们打开Putty,新建一个Session,将Connection type设置为:Serial,此时Serial line默认会填充为COM1,把COM1修改为COM8,将这个Session命名为:NokiaE71S60,先保存一下:

  然后我们双击打开刚才的那个Session,此时Putty的界面会停留在等待响应的阶段,那么这个时候我们要在手机上打开Python,通过选择菜单打开“Bluetooth console”,选择蓝牙所在的计算机。待确定之后,就可以看到Putty已经有了响应。切换到Python的开发界面上去了:

  好了,到现在为止,我们已经连接到手机的Python上去了,开始写一段代码吧,这是一段调用PyS60自带messaging信息库来发送短信的代码,我这里要发送一个字符串给我自己的手机:

  经过测试,可以很快收到自己发的短信。在开发的过程当中,还犯了一个错误,就是:把手机号码赋值到一个Int型变量上去了,结果提示错误,事实上手机号码应该是字符串的。
  好吧,接下来,就要考虑开发一些比较好玩的东西了。

分类: Python 3 评论
22十/092

关于JS前端添加RSH会多出一个history的解决方案

  在我负责的某一个项目当中,由于使用了大量的Ajax技术,而Ajax技术由于是异步请求,所以使得浏览器无法记录相应的History,这就意味着用户无法通过点击“前进”、“后退”按钮来进行操作,显然,从用户体验的角度来说,由于点击前进后退按钮是用户的习惯性操作,因此这是很不人性化的。
  那么,为了规避用户体验不佳的问题,我们使用了Google的一个开源项目RSH来做为最终解决方案。

  先介绍一下RSH,RSH是ReallySimpleHistory,是一个用于处理Ajax/DHTML应用程序的书签以及浏览器历史记录的轻量级的JavaScript库(Really Simple History is a lightweight JavaScript library for the management of bookmarking and browser history in Ajax/DHTML applications.)。它的原理是通过在URL上添加一串经过格式化的字符串,以使得每一次Ajax操作看起来都像是点击了不同的URL(实际上就是模拟点击URL的操作)。以此来达到用户可以通过点浏览器按钮来前进或者后退的目的。
  RSH目前的最新版本是:0.6Final。项目主页在这里:http://code.google.com/p/reallysimplehistory/

  但是由于RSH在初始化的时候就会往URL后添加一个格式化的字符串,所以这就导致当用户进入到某一个页面时,明明是第一次访问,此时浏览器也会有一个history出现,此时点击“后退”按钮是会退回到一个空白页或者重复刷新一次。为了规避这个问题,通读了RSH的代码之后,在结合前端和后端的基础之上做了一点调整,最后问题完美解决。我这里后端用的是Python+Webpy,所以后端代码以Python代码为例。

  现在的解决方案是,后台location到新的URL里,即往header里插入跳转的URL。然后客户端init的时候把这个事件插入hash表,这样就不会多出一个history来,且又能实现插入history的效果。

  实现步骤:
  1、首先是后台需要location到新的URL。
  比如我现在开放给用户的访问链接是:http://www.youcompany.com,在webpy获取到用户请求的时候(GET),判断如果为首页(第一次访问,此时我这边是根据请求特性来判断的,即如果请求为空,则意味着是第一次访问),则跳转:

  1. def do_request(self,call_name,method):
  2.     if not call_name:
  3.         cur_urls = web.ctx.env.get("HTTP_X_FORWARDED_SERVER",None)
  4.         self.urls = "".join(["http://",cur_urls,"/main/#my-index-page"])
  5.         web.redirect(self.urls)

  此时新的URL为:http://www.youcompany.com/main/#my-index-page,之后所有的请求都将发送到:http://www.youcompany.com/main/下面。此是history的记录为0。

  2、客户端监听。
  由于用户访问首页时,必须要添加一个监听才能使用RSH的方法,而RSH在添加监听函数的时候,相应的也会改变URL,这样还是会产生一个垃圾history,这就需要我们对RSH做一个修改。

在add方法里,加一个形参:is_hidden_location

  1. //---------------------原来:--------------------
  2. add: function(newLocation, historyData) {
  3. //---------------------修改后:--------------------
  4. add: function(newLocation, historyData, is_hidden_location) {

查找:window.location.hash,加一个判断:

  1. if(!is_hidden_location){
  2.     window.location.hash = newLocation;
  3. }

  在首页init添加监听的时候,为add方法加上一个参数:True,这样就不会添加路径到URL上,但不会影响RSH的history事件hash表。

   下一页