VFPCGI Day7

出自VFP Wiki

(修訂版本間差異)
跳轉到: 導航, 搜尋
第1行: 第1行:
[[Category:VFPCGI]]
[[Category:VFPCGI]]
-
=====VFPCGI的第七天=====
+
=====VFPCGI的第八天=====
-
那麼,要怎麼接收參數呢?
+
上次還沒實做關於 POST 的部份,所以這次延續上次的例子,並且做了一些擴充,這樣可以讓你很清楚怎麼取 GET 與 POST 來的資料。
-
Web application 如果不能收參數,那就遜掉了~程式也會難寫很多吧~
+
-
接收參數有兩種方式,一種是 GET,一種則是 POST
+
這裡我添加了一個 form,這是用來放表單資料的,再按下 submit 之後,你會在表單的下方看到讀到的結果。這邊也利用了 text...endtext 來作為一個 template 產生器,你可以看到 text...endtext 可以很方便的同時寫 html,並且在裡面嵌入變數(用<<與>>把變數括起來)。
-
差別在哪裡?
+
-
使用者看得到的,最大的差別就在於網址列。
+
-
如果網址列有像是 http://localhost/default.aspx?name1=value1&name2=value2 這種的,就表示是以 GET 方式,預設的存取方式其實也是 GET。
+
-
對 CGI 應用程式來說,最大的差別在於讀取資料的方式:如果是 GET,那就取得 QUERY_STRING 這個環境變數的內容;如果是 POST,那麼就讀取 STDIN (標準輸入) 的內容。
 
-
 
-
那麼,CGI 應用程式又要怎麼知道現在是 GET 還是 POST 呢?
 
-
同樣地,還是透過環境變數,這個環境變數的名字就叫做 REQUEST_METHOD。
 
-
 
-
ok,讓我們根據以上的原則來寫寫程式,請新增一個 cgi04.prg,然後將她設置為 main (set as main)
 
<pre>
<pre>
*
*
-
* cgi04
+
* cgi05
*
*
 +
SET PROCEDURE TO cgilib
SET PROCEDURE TO cgilib
 +
DECLARE INTEGER GetStdHandle in Win32API integer nHandleType
 +
declare integer ReadFile in Win32API integer hFile, string @ cBuffer,;
 +
integer nBytes, integer @ nBytes2, integer @ nBytes3
LOCAL oResponse
LOCAL oResponse
oResponse = CREATEOBJECT( "RESPONSE" )
oResponse = CREATEOBJECT( "RESPONSE" )
-
LOCAL cRequestMethod
+
LOCAL cRequestMethod, cInput
-
cRequestMethod = GETENV( "REQUEST_METHOD" )
+
cRequestMethod = UPPER( GETENV( "REQUEST_METHOD" ) )
-
oResponse.Write( "&lt;p&gt;REQUEST_METHOD=" + cRequestMethod + "&lt;/p&gt;")
+
cInput = "&lt;p&gt;REQUEST_METHOD=" + cRequestMethod + "&lt;/p&gt;"
DO CASE  
DO CASE  
CASE cRequestMethod == "GET"
CASE cRequestMethod == "GET"
-
oResponse.Write( "&lt;p&gt;QUERY_STRING=" + GETENV( "QUERY_STRING" ) + "&lt;/p&gt;" )
+
cInput = cInput + GETENV( "QUERY_STRING" )
CASE cRequestMethod == "POST"
CASE cRequestMethod == "POST"
-
* not implemented.
+
&&lenght of input string in STDIN is in this environment variable
 +
LOCAL lcContentLength, lnContentLength
 +
LOCAL lnHandle
 +
LOCAL lcInput
 +
LOCAL lnOverlappedIO
 +
 +
lcContentLength = GETENV("CONTENT_LENGTH")
 +
IF LEN( lcContentLength ) &gt; 0 THEN
 +
lnContentLength=VAL( lcContentLength )
 +
ELSE
 +
lnContentLength = 0
 +
ENDIF
 +
 
 +
IF lnContentLength &gt; 0 THEN
 +
&&get the input from STDIN
 +
lnInHandle=GetStdHandle(-10)
 +
lcInPut=REPLICATE(' ', lnContentLength )
 +
lnOverlappedIO=0
 +
ReadFile(lnInHandle, @lcInPut, lnContentLength, @lnContentLength, @lnOverlappedIO)
 +
ELSE
 +
lcInput=''
 +
ENDIF
 +
cInput = cInput + lcInput
 +
OTHERWISE
 +
cInput = ""
ENDCASE  
ENDCASE  
 +
 +
lcHtml = ""
 +
TEXT TO lcHtml NOSHOW ADDITIVE TEXTMERGE
 +
&lt;script language="javascript"&gt;
 +
function method_change( form )
 +
{
 +
switch ( form.cboMethod.value ) {
 +
case "GET": form.method = "GET"; break;
 +
case "POST": form.method = "POST"; break;
 +
}
 +
}
 +
&lt;/script&gt;
 +
&lt;form method="get" action="vfpcgi.exe"&gt;
 +
RequestMethod: &lt;select name="cboMethod" onChange="return method_change(this.form);"&gt;
 +
&lt;option value="GET" &lt;&lt;IIF(cRequestMethod=="GET", "selected", "")&gt;&gt; &gt;GET&lt;/option&gt;
 +
&lt;option value="POST" &lt;&lt;IIF(cRequestMethod=="POST", "selected", "")&gt;&gt; &gt;POST&lt;/option&gt;
 +
&lt;/select&gt;&lt;br/&gt;
 +
&lt;input type="text" name="txt" value=""/&gt;
 +
&lt;select name="cbo"&gt;
 +
&lt;option value="0" selected&gt;0&lt;/option&gt;
 +
&lt;option value="1"&gt;1&lt;/option&gt;
 +
&lt;option value="2"&gt;2&lt;/option&gt;
 +
&lt;/select&gt;&lt;br/&gt;
 +
&lt;input type="submit" value="Submit"/&gt;
 +
&lt;input type="reset" value="Reset"/&gt;
 +
&lt;/form&gt;
 +
&lt;pre&gt;
 +
&lt;&lt;cInput&gt;&gt;
 +
&lt;/pre&gt;
 +
ENDTEXT
 +
 +
oResponse.Write( lcHtml )
</pre>
</pre>
-
同樣,編譯好,丟到正確的位置之後(如果你還不知道,請參考前面),輸入:
+
同時,也對上次的 Response 類別作一點修正,在上次 Response 類別裡面,用來作為 cache 的 cursor,其資料欄位是 varchar(254),換言之,每次 Write 時,只能塞入 254 個字元,如果超過的話,就完蛋了。這次我把她修正為 M,也就是 Memo 型態。
 +
 
<pre>
<pre>
-
http://localhost/vfpcgi/vfpcgi.exe?name1=value1&name2=value2
+
DEFINE CLASS RESPONSE as Custom
 +
bDirty = .F.
 +
bHeaderOut = .F.
 +
ADD OBJECT Headers AS collection
 +
 +
PROCEDURE Init
 +
CREATE CURSOR outputCache ( outLine M )
 +
ENDPROC
 +
 +
PROCEDURE destroy
 +
IF this.bDirty == .T.
 +
this.flush()
 +
ENDIF
 +
USE IN outputCache
 +
ENDPROC
 +
 +
PROCEDURE Write
 +
LPARAMETERS theHtml
 +
LOCAL lcOutput
 +
 +
INSERT INTO outputCache values( theHtml )
 +
this.bDirty = .t.
 +
RETURN
 +
ENDPROC
 +
 +
HIDDEN PROCEDURE InternalWrite
 +
LPARAMETER lcOutput
 +
 
 +
DECLARE INTEGER GetStdHandle in Win32API integer nHandleType
 +
declare integer WriteFile    in Win32API integer hFile, string @ cBuffer,;
 +
integer nBytes, integer @ nBytes2, integer @ nBytes3
 +
 
 +
LOCAL lnOutHandle
 +
LOCAL lnBytesWritten
 +
LOCAL lnOverLappedIO
 +
 +
lnOutHandle=GetStdHandle(-11)
 +
lnBytesWritten=0
 +
lnOverLappedIO=0
 +
WriteFile(lnOutHandle, @lcOutput, len(lcOutput), @lnBytesWritten, @lnOverLappedIO)
 +
ENDPROC
 +
 +
PROCEDURE flushHeader
 +
LOCAL lcDefaultOutput
 +
LOCAL lcOutput
 +
LOCAL lcKey
 +
local i
 +
 +
lcDefaultOutput=""
 +
lcOutput = ""
 +
 
 +
FOR i = 1 TO this.Headers.Count
 +
lcKey = this.Headers.GetKey( i )
 +
IF !EMPTY( lcKey  ) THEN
 +
lcOutput = lcOutput + lcKey + ": " + this.Headers.Item(i) + CHR(13) + CHR(10)
 +
ENDIF
 +
NEXT
 +
 +
IF this.Headers.getKey( "Content-type" ) == 0 THEN
 +
lcOutput = lcOutput + "Content-type: text/html"+chr(13)+chr(10)
 +
ENDIF
 +
 
 +
lcOutput = lcOutput + chr(13)+chr(10)
 +
this.InternalWrite( lcDefaultOutput + lcOutput )
 +
this.bHeaderOut = .T.
 +
ENDPROC
 +
 +
PROCEDURE flush
 +
IF this.bDirty == .f. then
 +
RETURN
 +
ENDIF
 +
 
 +
LOCAL lcAlias
 +
LOCAL lcOutput
 +
 +
IF this.bHeaderOut == .F. THEN
 +
this.flushHeader()
 +
ENDIF
 +
 +
lcAlias = ALIAS()
 +
SELECT outputCache
 +
GO top
 +
SCAN
 +
lcOutput = outputCache.outLine
 +
this.InternalWrite( lcOutput )
 +
ENDSCAN
 +
SELECT( lcAlias )
 +
ENDPROC
 +
ENDDEFINE
</pre>
</pre>
-
就可以看到結果了。
+
同樣的,新增 cgi05 以後,將她設置為主程式,重新編譯之後,再丟到對應路徑即可。
-
這邊只演示了 GET 的部份,明天再來搞 POST 的部份...
+

在2006年11月22日 (三) 14:58所做的修訂版本

VFPCGI的第八天

上次還沒實做關於 POST 的部份,所以這次延續上次的例子,並且做了一些擴充,這樣可以讓你很清楚怎麼取 GET 與 POST 來的資料。

這裡我添加了一個 form,這是用來放表單資料的,再按下 submit 之後,你會在表單的下方看到讀到的結果。這邊也利用了 text...endtext 來作為一個 template 產生器,你可以看到 text...endtext 可以很方便的同時寫 html,並且在裡面嵌入變數(用<<與>>把變數括起來)。

*
* cgi05
*

SET PROCEDURE TO cgilib
DECLARE INTEGER GetStdHandle in Win32API integer nHandleType
declare integer ReadFile in Win32API integer hFile, string @ cBuffer,;
	integer nBytes, integer @ nBytes2, integer @ nBytes3

LOCAL oResponse
oResponse = CREATEOBJECT( "RESPONSE" )

LOCAL cRequestMethod, cInput
cRequestMethod = UPPER( GETENV( "REQUEST_METHOD" ) )

cInput = "<p>REQUEST_METHOD=" + cRequestMethod + "</p>"

DO CASE 
	CASE cRequestMethod == "GET"
		cInput = cInput + GETENV( "QUERY_STRING" )
	CASE cRequestMethod == "POST"
		&&lenght of input string in STDIN is in this environment variable
		LOCAL lcContentLength, lnContentLength 
		LOCAL lnHandle 
		LOCAL lcInput
		LOCAL lnOverlappedIO
		
		lcContentLength = GETENV("CONTENT_LENGTH")
		IF LEN( lcContentLength ) > 0 THEN 
			lnContentLength=VAL( lcContentLength )
		ELSE
			lnContentLength = 0
		ENDIF 

		IF lnContentLength > 0 THEN 
			&&get the input from STDIN
			lnInHandle=GetStdHandle(-10) 
			lcInPut=REPLICATE(' ', lnContentLength )
			lnOverlappedIO=0
			ReadFile(lnInHandle, @lcInPut, lnContentLength, @lnContentLength, @lnOverlappedIO)
		ELSE
			lcInput=''
		ENDIF
		cInput = cInput + lcInput
	OTHERWISE 
		cInput = ""
ENDCASE 

lcHtml = ""
TEXT TO lcHtml NOSHOW ADDITIVE TEXTMERGE 
<script language="javascript">
function method_change( form )
{
 switch ( form.cboMethod.value ) {
 case "GET": form.method = "GET"; break;
 case "POST": form.method = "POST"; break;
 }
}
</script>
<form method="get" action="vfpcgi.exe">
RequestMethod: <select name="cboMethod" onChange="return method_change(this.form);">
	<option value="GET" <<IIF(cRequestMethod=="GET", "selected", "")>> >GET</option>
	<option value="POST" <<IIF(cRequestMethod=="POST", "selected", "")>> >POST</option>
</select><br/>
<input type="text" name="txt" value=""/>
<select name="cbo">
<option value="0" selected>0</option>
<option value="1">1</option>
<option value="2">2</option>
</select><br/>
<input type="submit" value="Submit"/>
<input type="reset" value="Reset"/>
</form>
<pre>
<<cInput>>
</pre>
ENDTEXT 

oResponse.Write( lcHtml )

同時,也對上次的 Response 類別作一點修正,在上次 Response 類別裡面,用來作為 cache 的 cursor,其資料欄位是 varchar(254),換言之,每次 Write 時,只能塞入 254 個字元,如果超過的話,就完蛋了。這次我把她修正為 M,也就是 Memo 型態。

DEFINE CLASS RESPONSE as Custom 
	bDirty = .F.
	bHeaderOut = .F.
	ADD OBJECT Headers AS collection
	
	PROCEDURE Init
		CREATE CURSOR outputCache ( outLine M )
	ENDPROC
	
	PROCEDURE destroy
		IF this.bDirty == .T.
			this.flush()
		ENDIF 
		USE IN outputCache 
	ENDPROC
	
	PROCEDURE Write
		LPARAMETERS theHtml
		LOCAL lcOutput
		
		INSERT INTO outputCache values( theHtml )
		this.bDirty = .t.
		RETURN 
	ENDPROC
	
	HIDDEN PROCEDURE InternalWrite
		LPARAMETER lcOutput

		DECLARE INTEGER GetStdHandle in Win32API integer nHandleType
		declare integer WriteFile    in Win32API integer hFile, string @ cBuffer,;
				integer nBytes, integer @ nBytes2, integer @ nBytes3

		LOCAL lnOutHandle
		LOCAL lnBytesWritten
		LOCAL lnOverLappedIO 
		
		lnOutHandle=GetStdHandle(-11) 
		lnBytesWritten=0
		lnOverLappedIO=0
		WriteFile(lnOutHandle, @lcOutput, len(lcOutput), @lnBytesWritten, @lnOverLappedIO)
	ENDPROC
	
	PROCEDURE flushHeader
		LOCAL lcDefaultOutput
		LOCAL lcOutput
		LOCAL lcKey
		local i
		
		lcDefaultOutput=""
		lcOutput = ""

		FOR i = 1 TO this.Headers.Count
			lcKey = this.Headers.GetKey( i )
			IF !EMPTY( lcKey  ) THEN 
				lcOutput = lcOutput + lcKey + ": " + this.Headers.Item(i) + CHR(13) + CHR(10) 
			ENDIF 
		NEXT 
		
		IF this.Headers.getKey( "Content-type" ) == 0 THEN 
			lcOutput = lcOutput + "Content-type: text/html"+chr(13)+chr(10)
		ENDIF

		lcOutput = lcOutput + chr(13)+chr(10)
		this.InternalWrite( lcDefaultOutput + lcOutput )
		this.bHeaderOut = .T.
	ENDPROC
	
	PROCEDURE flush
		IF this.bDirty == .f. then
			RETURN
		ENDIF 

		LOCAL lcAlias
		LOCAL lcOutput
		
		IF this.bHeaderOut == .F. THEN
			this.flushHeader()
		ENDIF 
		
		lcAlias = ALIAS() 
		SELECT outputCache
		GO top
		SCAN
			lcOutput = outputCache.outLine
			this.InternalWrite( lcOutput )
		ENDSCAN
		SELECT( lcAlias )
	ENDPROC
ENDDEFINE

同樣的,新增 cgi05 以後,將她設置為主程式,重新編譯之後,再丟到對應路徑即可。