My Calendar

2015年2月10日 星期二

Servlet 檔案上傳

          要實現檔案上傳之前, 首先要了解 html中enctype這個屬性, enctype 屬性是在表單要發送數據給server之前決定用哪一種編碼。

enctype 有三種屬性:
  • application/x-www-form-urlencoded:默認編碼方式(只是處理表單裡value屬性值)
  • multipart/form-data:會以binary的編碼方式處理數據
  • text/plain :空格轉為"+",但不对特殊字元編碼
既然需要實現檔案上傳, 那我們的enctype當然要用multipart/form-data這個屬性。

index.html
<!DOCTYPE html>
<html>
    <head>
        <title>文件上傳</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    </head>
    <body>
        <div>
            <form action="upload" method="POST" enctype="multipart/form-data">
                <table>
                    <tr>
                        <td><label for="file1">文件:</label></td>
                        <td><input type="file" id="file" name="file"></td>
                    </tr>
                    <tr>
                        <td colspan="2"><input type="submit" value="上传" name="upload"></td>
                    </tr>
                </table>
            </form>
        </div>
    </body>
</html>

Servlet 2.x 的版本有兩種方法可以獲取上傳的檔案。

1.使用getInputStream()處理binary資料
package servlet.upload.demo;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/upload")
public class UploadServlet extends HttpServlet {

 class Position {

        int begin;
        int end;

  public Position(int begin, int end) {
   this.begin = begin;
   this.end = end;
  }
 }

 private static final long serialVersionUID = -4673819464292623830L;

 public UploadServlet() {
  super();
 }

 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

  byte[] content = bodyContent(request); // 讀取HTTP Body的內容
  String contentAsTxt = new String(content, "ISO-8859-1"); // 將HTTP Body內容用String表示

  String filename = getFileName(contentAsTxt); // 取得檔案名稱
  Position p = getFilePosition(contentAsTxt, request.getContentType()); // 取得檔案開始和結束位置

  writeTo(filename, content, p);
 }

 private byte[] bodyContent(HttpServletRequest request) throws IOException {

  ByteArrayOutputStream out = new ByteArrayOutputStream();
  InputStream in = request.getInputStream();

  try {
   byte[] buffer = new byte[1024];
   int length = -1;
   while ((length = in.read(buffer)) != -1) {
    out.write(buffer, 0, length);
   }
  } catch (Exception e) {
   e.printStackTrace();
  }

  return out.toByteArray();
 }

 private String getFileName(String requestBody) {
  String fileName = requestBody.substring(requestBody.indexOf("filename=\"") + 10);
  fileName = fileName.substring(0, fileName.indexOf("\n"));
  fileName = fileName.substring(fileName.indexOf("\n") + 1, fileName.indexOf("\""));

  return fileName;
 }

 private Position getFilePosition(String content, String contentType) throws IOException {
  String boundaryText = contentType.substring(contentType.lastIndexOf("=") + 1, contentType.length());
  // 取得實際上傳檔案的起始与结束位置
  int pos = content.indexOf("filename=\"");
  pos = content.indexOf("\n", pos) + 1;
  pos = content.indexOf("\n", pos) + 1;
  pos = content.indexOf("\n", pos) + 1;
  int boundaryLoc = content.indexOf(boundaryText, pos) - 4;
  int begin = ((content.substring(0, pos)).getBytes("ISO-8859-1")).length;
  int end = ((content.substring(0, boundaryLoc)).getBytes("ISO-8859-1")).length;

  return new Position(begin, end);
 }

 private void writeTo(String fileName, byte[] body, Position p) throws IOException {
  FileOutputStream fileOutputStream = new FileOutputStream("d:/" + fileName);
  fileOutputStream.write(body, p.begin, (p.end - p.begin));
  fileOutputStream.flush();
  fileOutputStream.close();
 }

}

實務上表格通常不會只有一個上傳檔案,會同時包含文字輸入,按鈕選項,下拉式選單等等。這時候如果使用request.getParameter()讀取資料,你會發現都拿到null!!!。因為表格的enctype 屬性已經設定成multipart/form-data,資料是以binary傳輸,Servlet 2.x版本的request.getParameter() 預設是不會處理binary的資料,所以得到的值都是null.

如果採用第一種方法那就需要將全部資料從binary截取,那實在太麻煩了。那我們用第三方套件幫組我們完成這件事。

2. 使用第三方套件 Commons File Upload

下載 commons-fileupload.jarcommons-io.jar 然後引入到專案中.
package servlet.upload.demo;

import java.io.File;
import java.util.List;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

@WebServlet("/upload")
public class UploadServlet2 extends HttpServlet {

 private static final long serialVersionUID = 2372213113534302476L;

 public UploadServlet2() {
  super();
 }

 protected void doPost(HttpServletRequest request, HttpServletResponse response) {
  try {
   //檢查請求是否用multipart/form-data處理
   if (ServletFileUpload.isMultipartContent(request)) {
    DiskFileItemFactory factory = new DiskFileItemFactory();
    ServletFileUpload upload = new ServletFileUpload(factory);
    //解析請求
    List formItems = upload.parseRequest(request);
    if (formItems != null && formItems.size() > 0) {
     for (FileItem item : formItems) {
      //檢查內容是檔案還是文字
      if (!item.isFormField() && item.getName() != null && !item.getName().equals("")) {
       String fileName = new File(item.getName()).getName();
       String filePath = "D:" + File.separator + fileName;
       File storeFile = new File(filePath);
       item.write(storeFile);
      } else {
       System.out.println("Key="+item.getFieldName());
       System.out.println("Value="+item.getString());
      }
     }
    }
   }
  } catch (Exception e) {
   e.printStackTrace();
  }

 }

}


3.使用Servlet 3.0 @MultipartConfig
Servlet 3.0 開始HttpServletRequest提供上傳檔案的支持。
package servlet.upload.demo;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

@MultipartConfig(location = "d:/")
@WebServlet("/upload")
public class UploadServlet3 extends HttpServlet {

 public UploadServlet3() {
  super();
 }

 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  request.setCharacterEncoding("UTF-8"); // 處理中文檔名
  Part part = request.getPart("file");
  part.write(part.getSubmittedFileName());
 }
}


2015年2月6日 星期五

使用RequestDispatcher 調派需求

在網頁應用程式中,經常需要多個Servlet完成需求,像是將一個Servlet的請求包含進來,或轉發給其他的Servlet處理。這時候可以使用 HttpServletRequest  getRequestDispatcher() 取得 RequestDispatcher,在這接口中定義了兩個方法:include() 和 forward()

RequestDispatcher disp = request.getRequestDispatcher("one.view");

使用include()
RequestDispatcher 的 include(),可以將另外一個Servlet的執行流程包含在目前Servlet的執行流程中。如下:

One.java
@WebServlet("/one.view")
public class One extends HttpServlet {
 private static final long serialVersionUID = 1L;

    public One() {
      super();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  
        PrintWriter out = response.getWriter();
        out.println("First view Start");
        RequestDispatcher disp = request.getRequestDispatcher("two.view");
        disp.include(request, response);
        out.println("First view End");
    }

}

Two.java
@WebServlet("/two.view")
public class Two extends HttpServlet {
    
    private static final long serialVersionUID = 1L;
    
    public Two() {
        super();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter out = response.getWriter();
        out.println("This is second view");  
    }

}

在網頁看到的回應順序如下:


使用forward()
RequestDispatcher 有個forward()方法,呼叫的時候一樣傳入request和response兩個物件,將請求處理轉發給其他Servlet。

One.java
@WebServlet("/hello.do")
public class One extends HttpServlet {
 private static final long serialVersionUID = 1L;

    public One() {
    }
    
 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  
  HelloModel model = new HelloModel();
  String name = request.getParameter("name");
  String message = model.sayMessage(name);
  RequestDispatcher disp = request.getRequestDispatcher("message.view");
  request.setAttribute("message", message);
  disp.forward(request, response);
   
 }

 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  doGet(request, response);
 }

}

HelloModel.java
public class HelloModel {
 
 private Map messages = new HashMap();
 
 public HelloModel() {
  messages.put("Eric","Hi");
  messages.put("Arch","Where are you?"); 
 }
 
 public String sayMessage(String user){
  return user+","+messages.get(user);
 }
}

Two.java
@WebServlet("/message.view")
public class Two extends HttpServlet {
 private static final long serialVersionUID = 1L;
       
    public Two() {
        super();
    }

 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 
  String template = "

%s

"; String html = String.format(template, (String)request.getAttribute("message")); response.getWriter().print(html); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }


執行時的結果


參考資料
  1. Servlet&JSP 教學手冊

2015年2月4日 星期三

中文編碼處理

中文編碼處理

當 Servlet 需要處理中文字時,那 Servlet 應該如何處理請求參數才能得到正確的中文字 ? 在編碼處理上基本有兩種情況處理分別是 GET 和 POST。

POST 請求參數編碼處理
如果客戶端沒有透過 Content-Type 設定編碼資訊 ( charset=UTF-8 ),那 HTTPServletRequest 的 getCharacterEncoding() 回傳值將會是 null,這時候容器將會使用預設編碼( ISO-8859-1 ) 處理,如果客戶端透過 UTF-8 發送非 ASCII 的請求參數,而 Servlet 直接使用 getParameter() 取得參數值,就會得到不正確的結果。

要正確的顯示中文字那可以使用 HttpServletRequest 的 setCharacterEncoding() 指定 POST 請求參數時使用的編碼。一定要在取得任何請求參數前執行 setCharacterEncoding() 才有作用。

GET 請求參數編碼處理
如果請求參數是以 GET 的方式傳遞那要處理 UTF-8 的編碼該如何呢? 我相信到這很多人應該會說用 setCharacterEncoding() 吧。那 setCharacterEncoding() 就能正確的處理中文字元嗎? 答案是 NO! NO! NO! ( 很重要所以說三次 ) 。在 API 文件對這個方法的定義以經說明清楚。

Overrides the name of the character encoding used in the body of this request

表示這方法只能在請求參數使用 POST 傳遞才有效(在 Tomcat 預設和沒有修改設定的情況). 既然 setCharacterEncoding() 不能對 GET 的請求參數進行處理 , 那還有其它方法處理嗎 ? 另外一個編碼的處理方式, 則透過 String 的 getBytes() 指定編碼取得 bytes array 在透過 String 重新建構正確的編碼.

例如瀏覽器使用 UTF-8 處理字元, Web Container 預設使用 ISO-8859-1 編碼, 正確處理編碼的方式是:
String name = request.getParameter("uname");
name = new String(name.getBytes("ISO-8859-1"),"UTF-8");

以下有一範例:

建立表單分別使用 GET 和 POST
<!DOCTYPE HTML>
<html>
<head>
<title>form_get</title>
<meta http-equiv="Content-Type" content="text/html; charset=BIG5">
</head>
<body>
<FORM method="post" action="encoding">
  user : <input type = "text" name = "uname"><br><br>
  <button type="submit">Submit</button>
</FORM>
</body>
</html>

分別處理 GET 和 POST 編碼的 Servlet 如下:
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(/encoding)
public class EncodingDemo extends HttpServlet {
 private static final long serialVersionUID = 1L;
       
    public EncodingDemo() {
        super();
    }

 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  String name = request.getParameter("uname");
     name = new String(name.getBytes("BIG5"),"BIG5");
  System.out.println(name);
  
 }

 
 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  request.setCharacterEncoding("BIG5");
  String name = request.getParameter("uname");
  System.out.println(name);
 }

}

2015年2月3日 星期二

getReader() 的用法

getReader() 


在 HTTPServletRequest 中有定義 getReader() 方法,可以讀取一個 BufferedReader 物件。透過這個物件可以讀取請求本體資料。以下有個範例.

1) 首先建立一個HTML表單用來發出請求 (form.html)。Servlet的名稱為body.view

Figure 1.1

2) 準備好HTML後,現在建立一個 Servlet 命名為 body.view.


package com.learn.servlet;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/body.view")
public class BodyServlet extends HttpServlet {
 private static final long serialVersionUID = 1L;
       
    public BodyServlet() {
        super();
    }

 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  String body = readBody(request);
  PrintWriter out = response.getWriter();
  out.println("");
  out.println("");
  out.println("");
  out.println(body);
  out.println("");
  out.println("");
  
 }
 
 private String readBody(HttpServletRequest request) throws IOException{
  BufferedReader reader = request.getReader();
  String input = null;
  String requestBody = "";
  while((input = reader.readLine()) != null)
   requestBody += input + "
";
  
  return requestBody;
 }

}


執行結果如下圖

 
Figure 1.2

2015年1月29日 星期四

HttpServletResponse

HttpServletResponse是Servlet處理好的內容發送到瀏覽器的一個物件.

發送數據到瀏覽器的方法








設定回應標頭的方法



















設定HTTP Status Code的方法






HTTP Status Code

HTTPServletResponse 定義了很多狀態碼













HttpServletResponse常見的應用

1.使用PrintWriter將資料回傳給瀏覽器
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@WebServlet("/demo")
public class Demo extends HttpServlet {

 private static final long serialVersionUID = -7819316605366593662L;

 public Demo() {
        super(); 
 }

 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
  //設定UTF-8編碼
  response.setCharacterEncoding("UTF-8");
  //設定header告訴瀏覽器要以UTF-8的编码显示資料,如果沒有設定,瀏覽器顯示中文字將會出現亂碼
  response.setHeader("content-type", "text/html;charset=UTF-8");
  PrintWriter writer = response.getWriter();
  writer.println("你好");
 }

 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  
 }
}

運行結果如下:










2.檔案下載

在網頁開發中下載檔案大家經常使用到的功能,使用HttpServletResponse就可以實現這個功能。話不多說直接上code.

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@WebServlet("/demo")
public class Demo extends HttpServlet {

 private static final long serialVersionUID = -7819316605366593662L;

 public Demo() {
    super(); 
 }

 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 
 { 
   //1.讀取檔案
   File downloadFile = new File("D:\\測試.jpg");
   //2.下載文件的名稱
   String fileName = downloadFile.getName();

   //3.設定content-disposition標頭讓瀏覽器以下载的形式打開文件
   response.setHeader("content-disposition", "attachment;filename="+URLEncoder.encode(fileName, "UTF-8"));
  
   int len;
   InputStream in = new FileInputStream(downloadFile);
   //3.設定緩衝區
   byte[] buffer = new byte[1024];
  
   //4. 透過response物件獲取OutputStream
   OutputStream os = response.getOutputStream();
 
   //5.將FileInputStream寫入到buffer緩衝區;
   while ((len = in.read(buffer)) > 0) {
    //6.使用OutputStream将緩衝區的資料输出到瀏覽器
    os.write(buffer, 0, len);
   }
  
   os.close();
   in.close();
}

P.S: 檔案是中文名稱要使用URLEncoder.encode方法進行編碼,否則會出現檔案名亂會是亂碼。

運行結果如下:



























3. 產生驗證碼.
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@WebServlet("/demo")
public class Demo extends HttpServlet {

 private static final long serialVersionUID = -7819316605366593662L;

 public Demo() {
    super(); 
 }

 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
  
  //设置refresh標頭讓瀏覽器每3秒钟刷新一次
  response.setHeader("refresh", "3");
  //在記憶體產生一張寬60高20的圖片
  BufferedImage image = new BufferedImage(60, 20, BufferedImage.TYPE_INT_RGB);
  Graphics2D g = (Graphics2D)image.getGraphics();//得到圖片
  g.setColor(Color.WHITE);//设置图片
  g.fillRect(0, 0, 60, 20);//背景顏色填滿
  //3.將一個亂數寫在圖片上
  g.setColor(Color.RED);//設定图片上字體的顏色
  g.setFont(new Font(null, Font.BOLD, 20));
  g.drawString(randomNum(), 0, 20);
     //4.設定標頭头控制瀏覽器以图片的方式打開
  response.setContentType("image/jpeg");
  //5.設定响应头控制浏览器不缓存图片数据
  response.setDateHeader("expries", -1);
  response.setHeader("Cache-Control", "no-cache");
  //6.將图片送出給瀏覽器
  ImageIO.write(image, "jpg", response.getOutputStream());
 }

 private String randomNum()
 {
  Random random = new Random();
  String num = random.nextInt(99999)+"";
  StringBuffer sb = new StringBuffer();
  for (int i = 0; i < 5-num.length(); i++) 
        sb.append("0");
  
  num = sb.toString()+num;
  return num;
 }

運行結果如下:






2015年1月25日 星期日

HttpServletRequest

HttpServletRequest

關於HttpServletRequest
當 Web 容器收到 HTTP 的請求時,容器會自動收集 web.xml@WebServlet 所定義的資訊,並產生 HttpServletRequest 物件,這個物件可以取得HTTP請求中的資訊。可以在 Servlet 中處理請求,或是將請求轉發給另一個 Servlet 進行處理。各個 Servlet/JSP 在同一個請求週期中需要共用資料的時候,可以透過 HttpServletRequest 物件設定 attribute.

處理請求參數和標頭
HttpServletRequest 定義了一些方法可以取得 HTTP 請求的資訊。例如可以使用以下的方法取得請求參數:

1) getParameter() :

指定請求參數的名稱取得值,例如:
             
String userName = request.getParameter("name");

getParameter() 回傳的是 String 物件, 若請求中沒有指定的參數名稱,則會回傳null。

2) getParameterValues() :

當表單上有可以複選的選項,例如 Checkbox, List 等等,同一個參數將會有多個值,這時候可以使用 getParameterValue() 取得一個 String array, array elements 代表所有選項的值。例如 :

String [] params = request.getParameterValue("checkList");

3) getParameterNames() :

如果想知道每一次請求有多少個參數名稱,則可以使用 getParameterNames(), 這會回傳一個 Enumeration 物件,當中包含全部請求參數名稱。例如:

Enumeration  params = request.getParameterNames();
while(param.hasNextElement())
{
   System.out.println("Request Parameter = "+ params.nextElement);
}

4) getParameterMap()

將請求參數以 Map 的形式回傳, Map 中的 Key 是請求參數名稱 Values 是請求參數值,Values 是 String array 的方式回傳因為請求參數值有肯能有多個值。例如:
// get request parameter map
Map  requestParams = request.getParameterMap();

// retrieve parameter name - values pair from parameter map
StringBuilder sb = new StringBuilder();
for (Map.Entry entry : requestParams.entrySet()) 
{
    String key = entry.getKey();         // parameter name
    String[] value = entry.getValue();   // parameter values as array of String
    .....
}

5) getHeader()

如果想獲取 HTTP 的標頭資訊,則用 getHeader() 這回傳一個 String 物件。例如 :

Sting headers - request.getHeader()

6)  getHeaders()

使用方法和 getParameterNames() 一樣

接下來有個範例顯示如何取得 HTTP 標頭資訊 :


package com.learn.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class Header
 */
@WebServlet("/header.view")
public class HeaderServlet extends HttpServlet {
 private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public HeaderServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

 /**
  * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
  */
 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  // TODO Auto-generated method stub
  PrintWriter out = response.getWriter();
  out.println("");
  out.println("");
  out.println("HeaderDemo");
  out.println("
");
  out.println("");
  out.println("HeaderServlet at "+ request.getContextPath()+"
");  // Application environment path
  Enumeration  e = request.getHeaderNames();     //Get all header names
  
  while(e.hasMoreElements())
  {
      String name = e.nextElement();
      out.println(name +" = "+ request.getHeader(name)+"
");
  }
  
  out.println("");
  out.println("");
  out.close();
  
 }

 /**
  * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
  */
 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  // TODO Auto-generated method stub
 }

}


顯示結果如下:


參考資料
  1. Servlet&JSP 教學手冊

2015年1月17日 星期六

使用@WebServlet

使用@WebServlet

       在開始使用@WebServlet之前有個先決條件,那就是要有一個Servlet程式。如果沒有的朋友請到『第一個Servlet程式』這裡有範例程式提供參考。準備好Servlet,接下來就要告訴Web Container這個Servlet的一些資訊。在這當中有兩種方法:
  1. 使用web.xml定義 
  2. 使用@WebServlet (Servlet 3.0以上才支援)

使用web.xml
要使用web.xml需要在Web應用程式中的WEB-INF目錄建立一個web.xml檔案定義Servlet的資訊,相關內容如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
  http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
 <servlet>
  <servlet-name>HelloServlet</servlet-name>
  <servlet-class>com.learn.servlet.HelloServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>
 <servlet-mapping>
  <servlet-name>HelloServlet</servlet-name>
  <url-pattern>/helloUser.view</url-pattern>
 </servlet-mapping>
</web-app>

這樣的檔案稱為部署描述檔(Deployment Descriptor)。使用web.xml定義會比較麻煩,但web.xml中的設定會覆蓋Servlet中的Annotation設定(也就是第二種方法)。那現在透過這個web.xml是如何定義Servlet的資訊。舉例,若有client發出/helloUser.view的請求,那將會由HelloServlet這個Servlet處理,這分別是由<servlet-mapping>中的<url-pattern><servlet-name>定義。

使用@WebServlet
在 Servlet 3.0 中,已經可以使用 Annotation 告知容器哪些 Servlet 會提供服務。例如在一個 Servlet 中:

@WebServlet("/hello.view")
public class HelloServlet extends HttpServlet {
......
}

只要 Servlet 有設定 @WebServlet 屬性,容器就能自動讀取當中的資訊。以上面的例子為例,@WebServlet 告訴容器,如果URL是 /hello.view , 就由 HelloServlet 的實例提供服務。

參考資料
  1. Servlet&JSP 教學手冊

2015年1月1日 星期四

第一個Servlet程式

第一個 Servlet 程式

所謂工欲善其事,必先利其器。所以開始寫我們的第一支Servlet程式之前必須先準備好開發環境。

開發環境設定
1) 下載 Apache Tomcat (因為是用Servlet 3.0以上,所以Tomcat需要7.x 以上才能支援。)
3) 執行eclipse然後選擇 Windows -> Preferences -> Server ->  Runtime Environment。
4) 在Server Runtime Environments 選擇 Add -> Apache Tomcat  v7.0。然後在Tomcat installation       directory中選擇Tomcat的安裝目錄,最後按下Finish這就完成設定如下圖。


設定完成後就開始我們的第一支Servlet程式吧。

第一個Servlet程式
1) 在選單選擇 File->New->Dynamic Web Project.
2) 在Dynamic Web Project 當中的Target Runtime選擇剛才所新建的Server,Dynamic web 
     module version 選擇 3.0如下圖


3) 展開專案中的 Java Resources -> src 新增一個Servlet

接著可以開始寫第一支Servlet程式的內容了,在新增的Servlet加入一下的內容吧。

package com.learn.servlet;
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class HelloServlet
 */
@WebServlet("/hello.view")
public class HelloServlet extends HttpServlet {  //繼承 HttpServlet
 private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public HelloServlet() {
        super();
    }

 /**
  * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
  */
 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {  
  response.setContentType("text/html;charset=UTF-8");
  PrintWriter out = response.getWriter();
  String name = request.getParameter("name");
  
  out.println("");
  out.println("");
  out.println("Hellow Servlet");
  out.println("");
  out.println("");
  out.println("

Hello! "+ name +" !

"); out.println(""); out.println(""); out.close(); } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }

在以上的範例程式可以看到繼承了 HttpServlet ,然後重新定義 doGet()方法。當瀏覽器發出GET請求時,會呼叫該方法。

在doGet()可以看到兩個參數分別是HttpServletResponse 和 HttpServletRequest。當Web Container 收到HTTP請求後,將會分別建立Request和Response的Java物件,而後在呼叫doGet()時將這兩個物件當成參數傳入。如果需要取得GET或POST請求的參數值,那將透過HttpServletRequestgetParameter()指定參數名稱即可。HttpServletResponse 是response物件,我們可以透過setContentType()設定回傳類型,然後再使用getWriter()取得PrintWriter物件在藉由PrintWriter物件中的println()將HTML回傳給瀏覽器。

執行Servlet
  1. 在『HelloServlet.java』右鍵 Run As -> Run on Server
  2. Tomcat 啟動成功後,你將會看到以下的畫面(恭喜!你的第一個Servlet程式完成了)

                     瀏覽器的網址設定是 http://localhost:8080/FirstServlet/hello.view?name=Eric.

Tomcat預設的port是8080,注意網址的路徑FirstServlet是專案的名稱,為什麼請求的URL是/hello.view呢?請注意看以上的範例程式中有以下一行:

@WebServlet("/hello.view")

這表示,如果請求的URL是/hello.view,就會由HelloServlet處理請求。

參考資料
  1. Servlet&JSP 教學手冊