VFP 做B/S的新方法(二)
前言:以前,我也和其他VFP爱好者一样,认为VFP的最大不足是不能做B/S应用开发,
现在我已改变了看法,VFP完全可以胜任B/S应用开发,而且性能非常好,也很容易
使用,问题的关键是你是否掌握好的开发方法。自从掌握ASP + COM编程方法后,我一直
在探索VFP做B/S应用更好的开发方法,特别是寻找一种方法解决COM对象在Application级启动
无法使用ASP内置对象的问题,在没有资料参考的情况下,我试了几种方法,有的也确实解决
了问题,但都有缺点。在一次大胆的想法并编程设验后,我成功地找到了这种方法,这让我
欣喜若狂。现在我把它公开,让用VFP的朋友分享,也听听你们宝贵的意见。
和上一篇介绍的一样,总的方法还是使用ASP + VFP编写COM组件实现,但在传递引用ASP
内置对象上却采用新的方法,上一篇使用的是COM+的上下文对象引用ASP内置对象,这次让ASP
直接传递,不要小看这个改进,所谓“外行看热闹,内行看门道”,它带来的好处是很明显的,
下面以例子说明。当然,能够采用这样的方法是拜VFP9.0在COM编程方面的功能增强所赐的
(VFP8.0我不清楚,但VFP7.0及以下版本是不能这样用的)。
下面是一个简单的应用示例,cplr.asp页面实现将输入的产品名称、价格存入或更新到数据表,
按“转到产品查询页面”按钮可以转到产品查询页面;cpcx.asp页面实现按输入的产品名称查询
数据表,并将查询结果显示在表格中,按“转到产品录入页面”按钮可以转到产品录入页面。
一、ASP文件和Global.asa 文件,这三个文件放在Web根目录下。
1、cplr.asp文件,内容只一行,传递ASP内置对象,并执行COM组件的cplr方法。
ob.cplr server,application,session,request,response
%>
2、cpcx.asp文件,内容只一行,传递ASP内置对象,并执行COM组件的cpcx方法。
ob.cpcx server,application,session,request,response
%>
3、Global.asa 文件,在Global.asa 文件末尾加上这一行,使com组件对象为Application级。
说明:别人一般都是将表单输入的内容作为参数传递给com组件对象的方法,而且每个ASP文件
里每一个方法的参数都可能不同,差别大,不容易写。这里却是将ASP内置的五个对象作参数传递,
因为参数相同,顺序都设同一样,这样的无区别参数使用起来非常容易,相当于无参数;asp文件也
简化到最小,仅执行一个方法,编好后就不再需要修改和维护了;每个ASP文件在COM组件对象里都有
一个方法和HTM模板文件相对应,采用同名机制可以方便记忆和使用,如cplr.asp对应cplr方法和
cplr.htm输出模板文件。
二、输出模板文件
建一新目录,就C:foxBS 吧,当然其他名称也没问题,下面的所有文件都放在此目录下。
1、cplr.htm 文件内容为
产品录入页面请输入产品名称:
请输入产品价格:
[[cmsg]]
2、cpcx.htm 文件内容为
产品查询页面请输入要查询的产品名称:
查询结果显示
产品名称 | 产品价格 | 录入日期 |
[[cCPMC]] | [[cCPJG]] | [[cRQ]] |
说明:采用输出模板文件的好处是非常多的,甚至是必须的。第一、它实现界面设计与
代码设计相分离,变动它们时互不影响;第二,它简化了代码的设计,代码的设计只需认真考虑
业务处理逻辑,生成需置换的变量和字段,然后用textmerge()函数就求得html输出字符串了;
第三、它也简化了界面的设计,输出HTML文本可不再手工编写,用网页制作工具可方便制作好看
的界面,在相应输出的地方写上变量域或字段域就好了。在我的例子里,不使用VFP默认的域界
符<< >>,而使用[[ ]],是为了与HTML的< >相区分。
三、com组件编写
1、产品表.dbf
在表设计器里设计,三个字段分别为
产品 C 20 建索引
价格 N 9 :2
日期 D
记得在“产品”字段建索引。
2、在VFP新建一项目,项目名称是foxBS,编译时将生成foxBS.dll,名称与Global.asa文件中
progid="foxbs.foxbs" 左边的foxbs相同,右边的foxbs是类名。
3、在项目中新建一程序文件bsclass.prg ,其他名称也行,内容为
DEFINE CLASS foxBS as Session OLEPUBLIC
PROTECTED cPath,cpcx_htm,cplr_htm,CRLF
*!* -----------------------------------------------------------------------
*!* init过程,启动时运行
*!* 赵贤书原创于海口,2006年1月26日首发于梅子论坛 *!*
PROCEDURE init
SET EXACT ON
SET DELETED ON
this.CRLF=CHR(13)+CHR(10)
this.cPath=justpath(application.ServerName)+"" && 取得VFP COM对象的路径
USE (this.cPath+"产品表") ORDER 产品 SHARED && 共享方式打开表
this.cpcx_htm=FILETOSTR(this.cPath+"cpcx.htm")
this.cplr_htm=FILETOSTR(this.cPath+"cplr.htm")
=STRTOFILE("foxBS.init 运行"+this.CRLF,this.cpath+"debug.txt",.t.)
ENDPROC
*!* -----------------------------------------------------------------------
*!* cplr过程 , 处理 cplr.asp , 模板cplr.htm
PROCEDURE cplr(oServer,oApplication,oSession,oRequest,oResponse)
LOCAL outhtml,cname,cJG,nJG
cMSG=""
*!*---------------------------------
*!* 初次打开CPLR.ASP页面时执行的代码
IF oRequest.form("cname").count=0
outhtml=textmerge(this.cplr_htm,.f.,"[[","]]")
oResponse.write(outhtml)
=STRTOFILE("foxBS.cplr cplr.asp 初次打开"+this.CRLF,this.cpath+"debug.txt",.t.)
RETURN
ENDIF
*!* ---------------------------------
*!* 按下 转到产品查询页面 按钮时执行的代码
IF oRequest.form("B3").count>0
oResponse.Redirect("cpcx.asp")
RETURN
ENDIF
*!* ---------------------------------
*!* 按下 确定输入 按钮时执行的代码
cNAME=oRequest.form("cname").item(1)
cJG=oRequest.form("cJG").item(1)
nJG=VAL(cJG)
IF ALLTRIM(cNAME)<>"" and nJG<>0
SELECT 产品表
SEEK cNAME
IF FOUND()
nYJG=价格
REPLACE 价格 WITH nJG,日期 WITH DATE()
cMSG="产品 "+cNAME+" 原价格"+STR(nYJG,9,2)+" 新价格"+STR(nJG,9,2)+" 已更新至数据表中!"
ELSE
INSERT INTO 产品表 VALUES (cNAME,nJG,DATE())
cMSG="产品 "+cNAME+" 价格"+STR(nJG,9,2)+" 已录入数据表中!"
ENDIF
ELSE
cMSG="请输入产品名称和价格!"
ENDIF
outhtml=textmerge(this.cplr_htm,.f.,"[[","]]")
oResponse.write(outhtml)
=STRTOFILE("foxBS.cplr 运行"+this.CRLF,this.cpath+"debug.txt",.t.)
*!* -----------------------------
ENDPROC
*!* -------------------------------------------------------------------------
*!* cpcx过程 , 处理 cpcx.asp ,模板cpcx.htm
PROCEDURE cpcx(oServer,oApplication,oSession,oRequest,oResponse)
LOCAL outhtml,cname,cCPMC,cCPJG,cRQ
cCPMC=CHR(41377) && CHR(41377) 为中文全角空格
cCPJG=CHR(41377)
cRQ=CHR(41377)
*!* ------------------------------
*!* 初次打开CPCX.ASP页面时执行的代码
IF oRequest.form("cname").count=0
outhtml=textmerge(this.cpcx_htm,.f.,"[[","]]")
oResponse.write(outhtml)
=STRTOFILE("foxBS.cpcx cpcx.asp 初次打开"+this.CRLF,this.cpath+"debug.txt",.t.)
RETURN
ENDIF
*!* ---------------------------------
*!* 按下 转到产品录入页面 按钮时执行的代码
IF oRequest.form("B3").count>0
oResponse.Redirect("cplr.asp")
RETURN
ENDIF
*!* ---------------------------------
*!* 按下 产品查询 按钮时执行的代码
cname=oRequest.form("cname").item(1)
IF ALLTRIM(cname)<>""
SELECT 产品表
SEEK cname
IF FOUND()
cCPMC=产品
cCPJG=STR(价格,9,2)
cRQ=DTOC(日期,1)
ELSE
cCPMC="无区配记录"
cCPJG=CHR(41377)
cRQ=CHR(41377)
ENDIF
ELSE
cCPMC="名称不能为空"
cCPJG=CHR(41377)
cRQ=CHR(41377)
ENDIF
outhtml=textmerge(this.cpcx_htm,.f.,"[[","]]")
oResponse.write(outhtml)
=STRTOFILE("foxBS.cpcx 运行"+this.CRLF,this.cpath+"debug.txt",.t.)
*!* -----------------------------
ENDPROC
*!* ------------------------------------------------------------------------
*!* destroy过程,退出时运行
PROCEDURE destroy
SELECT 产品表
USE
=STRTOFILE("foxBS.destroy 运行"+this.CRLF,this.cPath+"debug.txt",.t.)
ENDPROC
ENDDEFINE
项目建好后,在编译时选择Multi-threaded COM Server(dll)(多线程COM组件),就可生成
COM组件并自动注册了。现在,在IE里浏览cplr.asp 或 cpcx.asp 就可以运行此B/S应用。
说明:
1、this.cPath=justpath(application.ServerName)+"" 得到的是COM组件foxBS.dll
文件所在的路径,这样写而不直接用 this.cPATH="c:foxbs" 是为了com组件实现路径的无
关性,application.ServerName 中的application也不是ASP中的application,而是VFP中的
application--应用程序对象,这里即COM组件本身。之所以要解释这么多,是因为有一网友在
上篇中误会,以为该路径是指向WEB根目录。
2、方法过程中的参数顺序一定要和ASP文件中的参数顺序一致。
3、cCPMC=CHR(41377)中使用中文全角空格是为了表格在没有数据时更好地显示。
四、 下面总结这种方法的好处:
1、实现在Application级com组件对象对ASP内置对象的引用。
2、也同样实现Session级和页面级COM对象对ASP内置对象的引用,在引用方法变化时,无需
更改COM组件,因此,很容易根据性能、扩展性要求随意变化,实现高扩展性、可伸缩性。
3、ASP文件简化到最低程度,仅相关于开关指示作用,编写后不再需要修改和维护,而所有的
界面、功能编写集中在html文件和COM组件。
4、使用输出模板,可以实现界面设计、代码设计相分离,互不影响,界面设计、代码设计也
更加容易,因此可以适应编写大应用系统的要求。
5、Application级com组件对象只需一个实例就可为所有的ASP文件请求和用户提供服务,在一个
不太大的系统中,这样性能最好,稳定性也高。
当然,也有缺点,如;com组件特别是用于ASP的COM组件难于调试,重新编译COM组件需暂停Web
服务,这些缺点都不是VFP的COM组件所独有,所有的语言使用ASP+COM做B/S应用的都存在这样的麻烦,
但这些问题对一个有经验的编程人员来说不算什么,方法总比困难多。
后记:以上的内容是做服务器端组件的方法,但在B/S应用中仅有服器端组件是不够的,客户端与用户
的互动就可能需要VB脚本、JAVA脚本。如果foxer们也想用VFP怎么办?告诉你答案:就是用VFP编写客户端
组件,这样就不需要VB脚本和JAVA脚本了,特别是客户端要打印特定格式的内容,VB脚本和JAVA脚本都无法
解决,使用VFP客户端组件却可轻松搞定。遗憾的是,在客户端组件编写方面我还没经验,不能告诉大家好
的方法,希望大家也去探索探索,告诉我好的方法。 下一步,我打算将ASP内置组件在VFP内的语法整理
一下,这将又是一个原创的好贴子,让Foxer 们也不用走太多弯路。希望大家积极支持我,不要让好的、
原创的贴子沉下去,让更多Foxer看、学习。