我的上一篇關於 從 applet 中執行 POST 操作的技巧在讀者中引出了許多問題
其中最突出的問題是
如何顯示由 Web 服務器上的 POST CGI
bin 處理程序返回的 HTML 文檔?
在這篇技巧中
我們將探索這一問題的解決方法
並深入研究幾個很棒的服務器端的 Java 問題
注意
本技巧假定您知道讀者提出的有關通過 Java 執行 POST 操作的一些基本問題
如果您還不熟悉這些概念
請參考
Java 技巧
那麼
我們如何顯示來自 applet 的 POST 的結果呢?這一問題有四個答案
按照難易程度遞增的順序排列
依次為
無法顯示
別執行 POST 操作
使用 bean
作弊
正如在
Java 技巧
中討論的那樣
浏覽器目前所用的安全性管理器不允許在浏覽器中顯示由 applet 生成的 HTML
浏覽器僅允許我們將它指向 URL
URL 將代表我們將它顯示出來
這種情況難以令人滿意!
我們通過
難以置信!
不使用 POST 就可以避開顯示 POST 結果的這種限制
我們可以將一些信息編碼在 URL 中
然後再將編碼後的 URL 提供給 showDocument() 方法
這些信息可作為 GET 請求的參數傳遞給 Web 服務器
不幸的是
這存在一些缺陷
只能傳輸數量有限的數據
此外
在這個過程中 URL 被更改
這樣做相當笨拙
稍後我們會看到采用這種編碼方式的一個示例
最近
Sun 的 JavaSoft 分公司發布了 HTML renderer bean
(還有幾個別的商用軟件
)這樣
將這個 bean 作為 applet 的一部分並用它來顯示網頁就成為可能
有什麼缺點?大小
兼容性和成本
這個 bean 當然不小
它需要支持 bean 的浏覽器
而且不是免費的
當然
我們完全可以花時間來編寫自己的翻譯組件
但那是一種愚蠢的做法
這個問題的一種有趣而有建設性的解決方案就是作弊
在這個特例中
我們讓服務器端的代碼(例如
CGI
bin 腳本)與我們的 applet 共同作弊
基本思想很簡單
將 POST 與隨後的 GET 結合起來使用
這個過程如下所示
applet 仍然通過 POST 操作將信息發送給服務器
服務器利用 POST 信息生成 HTML
服務器將 HTML 保存到 Web 服務器上的文件中
服務器向 applet 返回一個魔力鍵
applet 將這個鍵編碼在 URL 中並返回給服務器
applet 通過在 showDocument() 調用中使用生成的 URL 來通知浏覽器顯示網頁
服務器接收 GET 請求並提取魔力鍵參數
服務器檢索與此魔力鍵相關的文件
服務器將文件中的 HTML 內容返回給浏覽器
浏覽器將 HTML 內容顯示出來
這種返回處理無疑比其他解決方案更復雜
但現在這種處理適用於客戶機和服務器的廣泛組合方式
這種處理的缺點在於
完成一個完整的事務必須執行多個 HTTP 請求
我們必須在多個請求的之間維護
狀態
信息
以便能跟蹤正在進行的事務(回憶一下
HTTP 是一種無狀態的請求/響應協議)
穩健地處理這些必要的狀態信息可能相當具有挑戰性
Tcl 腳本語言及 Sprite 分布式操作系統之父 John Ousterhout 曾經說過
在分布式計算中
狀態是第二麻煩的問題
不
它是最麻煩的問題
服務器部分最復雜
所以讓我們先來看一下 applet
:
) 這個 applet 與以前的
Java 技巧
中所用的 Happy applet 僅有幾點區別
到服務器的 POST 操作是相同的
但我們必須修改讀取服務器響應的的部分
input = new DataInputStream (urlConn
getInputStream ());
String str = null;
String firstLine = null;
while (null != ((str = input
readLine())))
{
if (null == firstLine)
firstLine = str;
System
out
println (str);
textArea
appendText (str +
\n
);
}
input
close ();
經過許可
服務器返回魔力鍵作為第一行
魔力鍵是一段狀態信息
此信息用來唯一標識 applet 所涉及的
與此服務器有關的事務
如果在處理 POST 請求的過程中遇到任何問題
服務器通過以下方式將這一情況通知 applet
返回
nil
字符串
並緊接著返回這一問題的文本描述
applet 現在所要做的唯一操作就是構建 URL
並調用 showDocument() 來顯示 HTML
if (null != firstLine)
{
url = new URL (//
+
((getCodeBase())
getHost())
toString() +
/poster?
+ firstLine);
(getAppletContext())
showDocument (url
_blank
);
}
一定要注意
URL 參數必須是 URL 編碼的
在上面的代碼段中
因為來自服務器的魔力鍵已被安全編碼
所以我們只需添加問號將基准 URL 與所傳遞的參數分隔開
現在我們已討論了 applet 部分
下面該研究服務器了
在以前有關 POST 的一篇 Java 技巧中
服務器端的代碼是用 Perl 編寫的傳統 CGI
bin 腳本
Perl 是一種不錯的解決方案
但是您難道不想用 Java 編寫服務器端的代碼嗎?我們可以用 Java 編寫 CGI
bin 腳本(請參閱參考資源部分)
但還有更好的解決辦法
那就是作為 Web 服務器本身一部分的 Java
這種服務器端的 Java 稱為 servlet
本文所提供的解決方案將是一個 servlet
是按照 Java Servlet API 編寫的
盡管通過 CGI
bin 腳本(用 Perl
Tcl
Java 或其他語言編寫)也能實現同樣的解決方案
請注意
對 servlet 編程和管理的介紹不屬於本文的討論范圍
我們僅討論與 POST 解決方案有直接關系的主要問題
PosterServlet 代碼包含大量的注釋
以便指導您閱讀代碼
代碼包含大量的錯誤處理和額外檢查
用來處理過多的可能出現的問題
拒絕服務攻擊等
但是多數時候您可以忽略這些代碼
(稍後我會更深入地討論安全性問題
)這個 servlet 是針對 Java
x API 編寫的(而 applet 代碼是針對 Java
編寫的)
doPost() 方法處理 POST 請求
即
doPost() 負責前三種服務器職責
它根據通過 POST 發送來信息生成 HTML 文檔
然後將文檔保存到一個臨時的磁盤文件中
並將魔力鍵返回給 applet
魔力鍵用來標識 HTML 文檔
並適合直接嵌入隨後的 GET 請求中
下面是代碼的核心部分
(要查看完整的代碼
請參閱實際的源文件
)實現說明
魔力鍵實際上是為此事務生成的 HTML 文檔的文件名
文件名是使用 java
util
Random 類生成的一個 Long 值
protected void doPost (HttpServletRequest request
HttpServletResponse response)
throws ServletException
IOException
{
response
setContentType (
text/plain
);
// 構建輸出文件名
String fileName = (new Long (randomizer
nextLong()))
toString();
File file = null;
try
{
file = new File (posterTempDir + File
separator +
fileName + posterTempExt);
}
catch (Exception e)
{
sendPostFailure (response
Unable to build output file path!
);
return;
}
// 打開輸出文件
PrintWriter output = null;
try
{
output = new PrintWriter
( new BufferedWriter
( new FileWriter (file)));
}
catch (IOException e)
{
sendPostFailure (response
Unable to open output file!
);
return;
}
output
println (
< html>
);
output
print (
< head>< title>Poster Servlet Generated Output
);
output
println (
< /title>< /head>
);
output
println (
< body>
);
// 現在
循環檢查請求標頭並將它們寫入文件中
String headerName = null;
Enumeration headers = request
getHeaderNames();
if (headers
hasMoreElements())
{
output
println (
< h
>CGI headers:< /h
>< hr>
);
output
println (
< ul>
);
while (headers
hasMoreElements())
{
headerName = (String) headers
nextElement();
output
print (
< li>< b>
);
output
print (headerName);
output
print (
=
);
output
print (request
getHeader (headerName));
output
println (
< /b>< /li>< br>
);
}
output
println (
< /ul>< hr>< br>
);
}
// 處理 POST 內容
if (
< request
getContentLength())
{
String line = null;
// 將所有輸入字節轉換為字符
BufferedReader in = new BufferedReader
( new InputStreamReader (request
getInputStream()));
output
println (
< h
>POST contents:< h
>< hr>
);
output
println (
< p>< pre>
);
// 讀取輸入的每一行
並將其寫入輸出文件中
HttpUtils httpUtils = new HttpUtils();
try
{
while (null != (line = in
readLine()))
{
try
{
Hashtable data = (line);
String keyName = null;
Enumeration keys = data
keys();
while (keys
hasMoreElements())
{
String[] values = null;
keyName = (String) keys
nextElement();
output
print (keyName);
From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19385.html