注册 登录
编程论坛 VFP论坛

foxpro web 开发原理(三)

sam_jiang 发布于 2025-05-16 23:45, 813 次点击
上次我们实现了cgi转发功能,不论我们请求哪个fpx文件,cgi程序都会向iis发送一个问候:“hello”

今天我们来探索IIS服务器是如何把请求的路径和参数发给我们cgi程序的。

当我们向浏览器发送请求:http://localhost/sample.fpx?name=samjiang时,我设想此时IIS收到请求,会把网址以参数的形式发回给我们的cgi程序,于是我为control.prg设置了2个参数,lcpath 和lcqureystring,编译试试~
程序代码:

parameters lcpath,lcqureystring
if parameters()=0
    lcoutput="没有参数被传递!"
else
    。。。
endif


很显然,这条路行不通,IIS并没有向我们的CGI程序control传递任何参数

那就说明IIS把我们的请求以环境变量的形式保存在服务器上了。

我虽不知道保存的环境变量名是什么,但这难不倒我,嘿嘿,写个foxweb脚本,看看当我们发出一个请求时,服务器会自动生成哪些环境变量。

foxweb代码:
程序代码:

<%
*本脚本showvars.fwx用来显示所有环境变量
dime aServerVars[1] &&定义一个服务器变量数组
nTotVars = Request.ServerVariablesArray(@aServerVars)
for i=1 to ntotvars
    response.write(aServerVars[i]) &&在浏览器中显示所有环境变量
    response.write("<br>")
endfor     
%>

浏览器显示如下:
只有本站会员才能查看附件,请 登录


发现2个有用的环境变量PATH_TRANSLATED和QUERY_STRING,我们的CGI程序要执行自定义脚本sample.fpx时,必须知道我们的脚本保存的物理路径,以及它的请求参数(当然可以没有参数)
在这篇文章里,我们依然没有对sample.fpx脚本进行解释执行,只是为了了解IIS如何将请求传递给我们的CGI程序control。

现在我们可以对control做出更改,编译后执行试试看~~~
control.prg代码如下:
程序代码:

* CGI处理示例
* 定义 Windows API 常量
#DEFINE STD_INPUT_HANDLE   -10
#DEFINE STD_OUTPUT_HANDLE  -11

* 声明 Windows API
DECLARE Sleep IN kernel32 INTEGER dwMilliseconds
DECLARE INTEGER GetStdHandle IN kernel32 INTEGER nStdHandle
DECLARE INTEGER ReadFile IN kernel32 ;
    INTEGER hFile, ;
    STRING @lpBuffer, ;
    INTEGER nNumberOfBytesToRead, ;
    INTEGER @lpNumberOfBytesRead, ;
    INTEGER lpOverlapped

DECLARE INTEGER WriteFile IN kernel32 ;
    INTEGER hFile, ;
    STRING lpBuffer, ;
    INTEGER nNumberOfBytesToWrite, ;
    INTEGER @lpNumberOfBytesWritten, ;
    INTEGER lpOverlapped

* CGI 环境变量常量
CGI_CONTENT_LENGTH = "CONTENT_LENGTH"

* 主程序
CLEAR
SET TALK OFF
SET CONSOLE OFF

* 获取标准输入输出句柄
hStdIn = GetStdHandle(STD_INPUT_HANDLE)
hStdOut = GetStdHandle(STD_OUTPUT_HANDLE)

* 调试用暂停(如果需要)

 =Sleep(6000)  && 暂停60秒等待调试器附加

* 读取POST内容
lcContentLength = GETENV(CGI_CONTENT_LENGTH)
lnContentLength = VAL(lcContentLength)

IF lnContentLength > 0
    * 准备缓冲区
    lcReadBuffer = REPLICATE(CHR(0), lnContentLength)
    lnBytesRead = 0
   
    * 读取输入
    =ReadFile(hStdIn, @lcReadBuffer, lnContentLength, @lnBytesRead, 0)
    lcReadBuffer = LEFT(lcReadBuffer, lnBytesRead)
   
    * 解析参数
    lnPos = AT("=", lcReadBuffer)
    IF lnPos > 0
        lcValue = SUBSTR(lcReadBuffer, lnPos + 1)
    ELSE
        lcValue = lcReadBuffer
    ENDIF
ELSE
    lcValue = ""
ENDIF

cpath=GETENV("PATH_TRANSLATED")
cqureystr=GETENV("QUERY_STRING")
* 构建响应
    lcResponse = "HTTP/1.0 200 OK" + CHR(13) + CHR(10) + ;
                "Content-Type: text/html" + CHR(13) + CHR(10) + ;
                CHR(13) + CHR(10) + ;
                "请求路径为: " + cpath+"<br>"+;
                "请求参数为:" +IIF(EMPTY(cqureystr),"没有参数!",cqureystr)
*    lcResponse [color=#808080]= "HTTP/1.0 200 OK" + CHR(13) + CHR(10) + ;[/color]
                "Content-Type: text/html" + CHR(13) + CHR(10) + ;
                CHR(13) + CHR(10) + ;
                "Hello " + lcValue        
* 发送响应
lnBytesWritten = 0
=WriteFile(hStdOut, lcResponse, LEN(lcResponse), @lnBytesWritten, 0)

* 清理
CLEAR DLLS
QUIT


在浏览器中运行效果如下:
1,没有参数的情况:
只有本站会员才能查看附件,请 登录

2,有参数的情况:
只有本站会员才能查看附件,请 登录


完美。。。

预告一下~~~ 下一篇,我们就可以执行我们自己的脚本了

10 回复
#2
sam_jiang2025-05-17 09:16
浏览器输出的环境变量有问题,生成的数组是二维数组, 我当成普通数组了,不过,不影响CGI功能的实现。

修改后的showvars.fwx代码如下:
程序代码:

<%
dime aServerVars[1,2]
nTotVars = Request.ServerVariablesArray(@aServerVars)
for i=1 to ntotvars
    response.write(padr(aServerVars[i,1],50,"_") + aServerVars[i,2])
    response.write("<br>")
endfor     
%>


效果如下:
只有本站会员才能查看附件,请 登录
#3
iswith2025-05-17 11:55
现在还有多少Web应用是在IIS上的!算了吧!PC 就用C# + VFP  移动端就用JAVA/PHP要从源头放弃系统转向其它!代价比较大就算了吧!
在VFP“开箱即用”逻辑思维下这一切太过于繁琐,玩Web就放弃VFP,转PHPJAVA成本都要低很多。
#4
sam_jiang2025-05-17 12:45
回复 3楼 iswith
这大概就是我们老一辈foxer的坚持吧?能用vfp搞定的,就坚决不用其他语言。。。

我希望我的文章能给其他foxer一些启发,当然在诸位大神面前这是班门弄斧了。。。

本编程版块也就我们vfp论坛还保存这一丝热闹了。

加油,foxer们!
#5
iswith2025-05-17 13:29
用web调用VFP处理数据确实是一个正二八经的路,关键不是VFP,而是调用者的封装,一般来说用C或go最好~,当然你自己封装就等于干了MS或其它大厂的工作!其实底层逻辑真是一模一样的。

#6
schtg2025-05-17 14:41
#7
sam_jiang2025-05-17 16:25
回复 5楼 iswith
现在的网站绝大多数都是数据驱动的网站,vfp在处理数据方面有得天独厚的优势,这大概就是vfp经久不衰的原因吧?

别人我不知道,反正我对vfp情有独钟
#8
jmkwok2025-05-17 18:02
其實學一下web也用不了多少時間, 我現在是桌面仍然vfp+mysql, Web就是javascript+php+mysql, 非常方便!
#9
hsfisher2025-05-17 19:59
#10
trial032025-05-21 10:29
谢谢大大们无私的贡献
#11
sam_jiang2025-05-23 22:33
在我们的CGI程序能解释脚本之前,我们对control.exe进行升级,验证一下可行性:
1,对请求的路径进行验证检查,并给出不同响应
2,对POST和GET请求做出不同的响应
代码如下,已编译通过:
程序代码:

* CGI处理示例
* 定义 Windows API 常量
#DEFINE STD_INPUT_HANDLE   -10
#DEFINE STD_OUTPUT_HANDLE  -11

* 声明 Windows API
DECLARE Sleep IN kernel32 INTEGER dwMilliseconds
DECLARE INTEGER GetStdHandle IN kernel32 INTEGER nStdHandle
DECLARE INTEGER ReadFile IN kernel32 ;
    INTEGER hFile, ;
    STRING @lpBuffer, ;
    INTEGER nNumberOfBytesToRead, ;
    INTEGER @lpNumberOfBytesRead, ;
    INTEGER lpOverlapped

DECLARE INTEGER WriteFile IN kernel32 ;
    INTEGER hFile, ;
    STRING lpBuffer, ;
    INTEGER nNumberOfBytesToWrite, ;
    INTEGER @lpNumberOfBytesWritten, ;
    INTEGER lpOverlapped

* CGI 环境变量常量
CGI_CONTENT_LENGTH = "CONTENT_LENGTH"
cmethod=GETENV("REQUEST_METHOD")

* 主程序
CLEAR
SET TALK OFF
SET CONSOLE OFF

* 获取标准输入输出句柄
hStdIn = GetStdHandle(STD_INPUT_HANDLE)
hStdOut = GetStdHandle(STD_OUTPUT_HANDLE)

* 调试用暂停(如果需要)
* [color=#808080]=Sleep(6000)  && 暂停60秒等待调试器附加[/color]

DO CASE
    CASE cmethod="POST"
        * 读取POST内容
        lcContentLength = GETENV(CGI_CONTENT_LENGTH)
        lnContentLength = VAL(lcContentLength)

        IF lnContentLength > 0
            * 准备缓冲区
            lcReadBuffer = REPLICATE(CHR(0), lnContentLength)
            lnBytesRead = 0
            
            * 读取输入
            =ReadFile(hStdIn, @lcReadBuffer, lnContentLength, @lnBytesRead, 0)
            lcReadBuffer = LEFT(lcReadBuffer, lnBytesRead)
            
            * 解析参数
            lnPos = AT("=", lcReadBuffer)
            IF lnPos > 0
                lcValue = SUBSTR(lcReadBuffer, lnPos + 1)
            ELSE
                lcValue = lcReadBuffer
            ENDIF
        ELSE
            lcValue = ""
        ENDIF
        * 构建响应        
        lcResponse = "HTTP/1.0 200 OK" + CHR(13) + CHR(10) + ;
                "Content-Type: text/html" + CHR(13) + CHR(10) + ;
                CHR(13) + CHR(10) + ;
                "Hello " + lcValue
   
    CASE cmethod="GET"
        cpath=GETENV("PATH_TRANSLATED")
        cquerystr=GETENV("QUERY_STRING")
        IF FILE(cpath)
            * 构建响应
            lcResponse = "HTTP/1.0 200 OK" + CHR(13) + CHR(10) + ;
                    "Content-Type: text/html" + CHR(13) + CHR(10) + ;
                    CHR(13) + CHR(10) + ;
                    "请求路径为: " + cpath+"<br>"+;
                    "请求参数为:" +IIF(EMPTY(cquerystr),"没有参数!",cquerystr)
         ELSE
              lcResponse = "HTTP/1.0 200 OK" + CHR(13) + CHR(10) + ;
                "Content-Type: text/html" + CHR(13) + CHR(10) + ;
                CHR(13) + CHR(10) + ;
                "错误:请求的脚本不存在或已删除!"
         ENDIF                  

    OTHERWISE
        lcResponse = "HTTP/1.0 200 OK" + CHR(13) + CHR(10) + ;
                "Content-Type: text/html" + CHR(13) + CHR(10) + ;
                CHR(13) + CHR(10) + ;
                "错误:目前只支持GET和POST方法!"
ENDCASE                                            

* 发送响应
lnBytesWritten = 0
=WriteFile(hStdOut, lcResponse, LEN(lcResponse), @lnBytesWritten, 0)

* 清理
CLEAR DLLS
RETURN  

服务器上并没有sample1.fpx,所以IIS返回如下:
只有本站会员才能查看附件,请 登录
1