Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Tomcat+MySQL為自己的APP打造服務器(2-2)Servlet的使用

Tomcat+MySQL為自己的APP打造服務器(2-2)Servlet的使用

編輯:關於Android編程

在上一篇我們只是簡單的對 Servlet 要做的任務、在服務器中的地位有了一個大概的了解,完成了在一個全新的 WorkSpace 中創建第一個 Dynamic Web Project ,並創建第一個 Servlet ,解決期間可能遇到的常見問題,還留了不少的內容重要內容沒有完成,今天我們來繼續解決這幾個問題:

一、怎麼能訪問到一個Servlet & URL中各部分的含義

我們來回顧上一篇篇末時在浏覽器中訪問 FirstServlet 時用的地址http://localhost:8080/ServletTest/Home/FirstServlet,我們還特地用不同顏色標注了這個URL的不同部分,下面我們來看看這各個部分都是什麼:

http://localhost:8080/ServletTest/Home/FirstServlet

http:// 是一種網絡通信協議,HTTP是客戶端浏覽器或其他程序與Web服務器之間的應用層通信協議,比如還有 FTP ,HTTPS...等,你可以理解成就是網絡通信的規則,你要用 http 就要遵循它下邊指定的各種規則(這個具體我也不大懂,不妄加解釋,以免誤人子弟)。

localhost:8080 是你訪問的服務程序所在主機的地址及開放端口,即服務器的IP地址和對應的端口地址。這裡我的服務器就是自己的電腦,直接用浏覽器訪問,所以我用的就是 localhost(默認的 localhost 是127.0.0.1,就是指本地);如果你經過網絡(不管是局域網、或者是 Internet),就需要用真正的服務器 IP 地址了(命令行使用 ipconfig 命令即可查看電腦IP),*注意* 只有具有公網IP 的才能通過 Internet 訪問到,否則只能通過局域網使用,關於這個問題,我們後邊還會涉及到,到時候再詳細解釋。

/ServletTest/Home/FirstServlet 是你所訪問的程序在服務器上部署的路徑。其實這裡這個路徑是兩個部分,/ServletTest/Home/FirstServlet 中/ServletTest 是工程項目名稱(和自己的項目名稱一致,你和我不一定一樣),/Home/FirstServlet 是在創建Servlet的時候指定的 URL-mapping。項目名和url-mapping組合成你的 Servlet 路徑。

當我們請求這樣一個地址時(之前的例子是在浏覽器中通過 GET 方式訪問這個地址,之後我們加上 POST 請求的例子),根據 Http 協議的規則會去訪問對應的 IP 的主機,啟動後的 Tomcat 會監聽設置的端口(如果沒有更改,默認的端口號就是8080),監聽到對本地主機這個端口的訪問後根據路徑——項目工程名 + url-mapping映射 來匹配所請求地址對應的 Servlet。這樣,我們的請求就發送到了想要到達的位置,接下來看 Servlet 怎麼響應這個請求。

二、Servlet對請求的響應

我們最常用的 Servlet 都是繼承自HttpServlet,這種 Servlet 能夠響應 GET、POST兩種請求 。還記得我們上一篇中創建 FirstServlet 時候提醒你留意看一下的那個圖嗎,再貼一下:

\

創建 Servlet 時 Eclipse 默認要重寫父類方法 doGet、doPost。doGet 方法就是專門用來響應 GET 請求(從我們開始說 Servlet 開始,一直用的都是 GET 請求,POST 請求我們會在之後出現,並給出我的一個 Android 和服務器進行POST交互的完整例子);doPost 方法專門用來響應 POST 請求的。GET、POST是兩種最常用的網絡請求方式,只是使用的方法不同,各有優劣罷了,沒有這方面基礎知識的同學直接去百度,比較容易理解。

由於還沒有說到 POST,我們就先以 GET 請求為例來說說 Servlet 是怎麼響應我們的請求的:

在上邊的問題中我們已經知道怎麼請求到一個特定的 Servlet,下面我們來完成 Servlet 的響應。我們新創建的 FirstServlet 的原始doGet 方法如下:

 

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
	}

可以看到,doGet 方法有兩個入參(doPost方法也一樣):一個HttpServletRequest 的實例對象、一個HttpServletResponse 的實例對象。其中HttpServletRequest 就是我們發到 Servlet 的 GET請求(同理,doPost 方法的入參HttpServletRequest 對象就是發送的 POST 請求),從中可以獲取我們發起請求時設置的參數;HttpServletResponse 就是將要返回給客戶端或浏覽器的響應(至於這個 response 怎麼就從這裡返回給了請求源,我們在這個問題完了再說),我們在 Servlet 中要做的就是從 request 中獲取請求參數,根據業務邏輯進行計算處理,得出結論後把結果賦值到 response 中返回給客戶端。至此,一次完整的網絡交互就完成了,下面簡單舉個例子吧:

在下面的代碼中,我們模擬了一個最簡單的登陸驗證的處理過程:

 

/**
 * Servlet implementation class FirstServlet
 */
@WebServlet("/Home/FirstServlet")
public class FirstServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	/**
	 * Default constructor.
	 */
	public FirstServlet() {
		
	}

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String account = request.getParameter("account"); // 從 request 中獲取名為 account 的參數的值
		String password = request.getParameter("password"); // 從 request 中獲取名為 password 的參數的值
		System.out.println("account:" + account + "\npassword:" + password); // 打印出來看一看

		String result = "";
		if("abc".equals(account) 
				&& "123".equals(password)){ // 模擬登陸賬號、密碼驗證
			result = "Login Success!";
		}else {
			result = "Sorry! Account or password error.";
		}
		/* 這裡我們只是模擬了一個最簡單的業務邏輯,當然,你的實際業務可以相當復雜 */
		
		PrintWriter pw = response.getWriter(); // 獲取 response 的輸出流
		pw.println(result); // 通過輸出流把業務邏輯的結果輸出
		pw.flush();
		
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response); // 默認的代碼,意思就是說做doPost和doGet一樣
								  // ***但實質上可行不可行呢?我們接下來或者下一篇就會說到
	}

}

 

Run AS > Run on Server 運行在 Tomcat 中。

接下來,我們在浏覽器中拼接一個理想的 GET 請求:

http://localhost:8080/ServletTest/Home/FirstServlet?account=abc&password=123

運行結果:

正確GET結果

終端 Console 記錄的請求參數:

\

我們再來一個反例:

反例

終端記錄:

記錄

怎麼樣,你成功了嗎?磨刀不誤砍柴工,我們要認真踐行標題上說的一步一個腳印,POST 方法放到這個話題完了再詳細說。

三、Servlet的工作流程

如果你是一個完全的服務端的小白,做到這裡是不是有點小激動呢?終於打通了任督二脈!但是作為一個需要不斷學習不斷思考的程序猿,你有沒有想到 Servlet 怎麼就能接收 GET 請求,怎麼就能接收 POST 請求呢?怎麼就能把 response 返回給請求源呢?下面我們就來看 Servlet 的工作流程。

HttpServlet 中有一個極其重要的方法—— service(HttpServletRequest request, HttpServletResponse response),在 service 方法中獲取 request 的請求方式,將 GET 、POST 不同的請求分發給對應的 doGet、doPost 方法,Servlet 的一般流程如下圖:

Servlet一般流程

需要注意的是:兩個參數HttpServletRequest request 和 HttpServletResponse response 都是 Server 持有的對象,doGet、doPost 方法都是在更改它的內容(所以這兩個方法的返回類型都是 void),完成後 Server 將操作完成的 response 返回給請求源。這是 Servlet 的一般流程!

不同的如下圖:

總流程

Servlet 有一個創建過程,會激發其 init() (這個是 HttpServlet 父類 GenericServlet 的方法)進行初始化。有兩個條件都可以激發,這個是我們可以自己設定的,默認是(2)第一次請求 Servlet 並且該 Servlet 還未有實例時進行 init(),也可以在在 web.xml 中 標簽下配置 標簽,配置的值為整型,值越小 Servlet 的啟動優先級越高。

對於更多的客戶端請求,Server 創建新的請求和響應對象,仍然激活此 Servlet 的 service() 方法,將這兩個對象作為參數傳遞給它。如此重復以上的循環,但無需再次調用 init() 方法。一般 Servlet 只初始化一次(只有一個對象),當 Server 不再需要 Servlet 時(一般當 Server 關閉時),Server 調用 Servlet 的 destroy() 方法。

四、亂碼問題

在上一篇最後的例子中,我們留了一個問題,就是中文亂碼(如果你是處女座,不折騰你了,貼心的給你個鏈接,不用費勁找了)的問題,還記得嗎?有沒有發現本文在此之前都在避免用中文?下面我們就來搞搞這個問題:

我們把 doGet 中賬號密碼加點中文,在響應中也加點中文;

 

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String account = request.getParameter("account"); // 從 request 中獲取名為 account 的參數的值
		String password = request.getParameter("password"); // 從 request 中獲取名為 password 的參數的值
		System.out.println("account:" + account + "\npassword:" + password); // 打印出來看一看

		String result = "";
		if("王x".equals(account) 
				&& "傑x".equals(password)){ // 添加中文
			result = "Login Success!" + "成功了!"; // 響應也加點中文
		}else {
			result = "Sorry! Account or password error." + "有點問題!"; // 響應也加點中文
		}
		/* 這裡我們只是模擬了一個最簡單的業務邏輯,當然,你的實際業務可以相當復雜 */
		
		PrintWriter pw = response.getWriter(); // 獲取 response 的輸出流
		pw.println(result); // 通過輸出流把業務邏輯的結果輸出
		pw.flush();
	}
確保服務器程序更新(一般情況下第一次運行成功後,之後的代碼更改在保存後會自動執行)後,在浏覽器請求

 

http://localhost:8080/ServletTest/Home/FirstServlet?account=王x&password=傑x

帶中文的請求和響應

我們可以看到,相應結果英文部分是對的——判斷的結果是 true,說明從請求到邏輯處理是理想的,作為輔證,我們看服務端收到的請求參數記錄:

中文請求參數記錄

到這裡,我們就找出了亂碼問題的根源——就是響應過程中出現問題,而中文亂碼問題一般都是編碼格式引發的。所以我們這裡在添加一行:

 

		// ......之前的代碼片就不用貼了
		
		response.setContentType("text/html;charset=utf-8"); // 設置響應報文的編碼格式
		PrintWriter pw = response.getWriter(); // 獲取 response 的輸出流
		pw.println(result); // 通過輸出流把業務邏輯的結果輸出
		pw.flush();
更改成功生效後,再次請求:

 

\

\

*注意* 一般我們在正常使用中,都會直接設置 request、response 的編碼格式,以防由於編碼問題引發的不必要的麻煩,如下:

 

	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		/* 先設置請求、響應報文的編碼格式  */
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html;charset=utf-8");
		
		// .....再進行我們的邏輯處理
	}
是不是有同學會想到,如果每個 Servlet 都要這樣寫,doGet、doPost 都要手動寫,是不是很麻煩也很煩人吶?是的!是挺煩人的。有沒有什麼好的辦法解決這個問題呢?答案肯定是有的,那就是 Servlet 過濾器 Filter(包 javax.servlet.Filter 下),這個我們後邊會說到。本篇先到這裡啦!
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved