跳轉到內容

Jakarta EE 程式設計/Servlet

50% developed
來自 Wikibooks,開放世界中的開放書籍

Servlet 是在伺服器端執行的 Java 類,它接收 HTTP 資料,並在遵循 HTTP 協議的約束條件下進行處理或執行其他操作。

Servlet 示例

[編輯 | 編輯原始碼]
package 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 Hello
 */
@WebServlet("/Hello")
public class Hello extends HttpServlet {

    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public Hello() {
        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
       
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try {
            out.println("Hello World");
        } finally {
            out.close();
        }
    }

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

        doGet(request,response);
    }
}

我們注意到這個類中有兩個方法:doGet()doPost()。第一個方法透過 HTTP 響應對 GET 請求的接收,第二個方法響應對 POST 請求的接收。由於我們希望在兩種情況下 Servlet 都能處理請求,因此 doPost() 會轉發到 doGet()

在 Tomcat 上部署

[編輯 | 編輯原始碼]
  • 將編譯後的類及其包放入工作 Web 目錄的 WEB-INF 中的 classes 目錄中(這裡:webTest
  • 透過新增以下內容來編輯 WEB-INF 中的 web.xml
<web-app 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"
  version="3.0"
  metadata-complete="true">  

  <!-- Beginning here -->

    <servlet>
      <servlet-name>Hello</servlet-name><!-- Class name -->
      <servlet-class>servlet.Hello</servlet-class><!-- Class tree: with the package -->
    </servlet>
    <servlet-mapping>
        <servlet-name>Hello</servlet-name><!-- Class name -->
        <url-pattern>/Hello</url-pattern><!-- Class pattern in the URL-->
    </servlet-mapping>

   <!-- End here -->

<web-app>

因此 WEB-INF 包含

   WEB-INF
     web.xml
     classes
       servlet
         Hello.class

呼叫 Servlet

[編輯 | 編輯原始碼]

https://:999/webTest/Hello

瀏覽器中的結果

Hello World

URL 的分解

[編輯 | 編輯原始碼]

[協議://][DNS]:[埠]/[根目錄]/[Servlet 名稱]

關於 URL 的資訊方法

[編輯 | 編輯原始碼]
// Returns the server name
request.getServerName();

// Returns the server port
request.getServerPort();

// Returns the name of the application hosting the servlet
request.getContextPath();

// Returns the servlet path
request.getServletPath();

// Returns the type of the HTTP request used
request.getMethod();

// Returns the parameters sent in the URL
request.getQueryString();

// Returns the URL used to contact the servlet
request.getRequestURL();

// Returns the local address
request.getLocalAddr();

// Returns the local name
request.getLocalName();

// Returns the local port
request.getLocalPort();

// Returns the remote address
request.getRemoteAddr();

// Returns the remote host
request.getRemoteHost();

Servlet 中的示例

package 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 Hello
 */
@WebServlet("/Hello")
public class Hello extends HttpServlet {

  private static final long serialVersionUID = 1L;
   
  /**
   * @see HttpServlet#HttpServlet()
   */
  public Hello() {
    super();
    // TODO Auto-generated constructor stub
  }

  protected void processRequest(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {
    response.setContentType("text/html;charset=UTF-8");
    PrintWriter out = response.getWriter();
    try {
      out.println(request.getServerName());
      out.println("<br/>");
      out.println(request.getServerPort());
      out.println("<br/>");
      out.println(request.getContextPath());
      out.println("<br/>");
      out.println(request.getServletPath());
      out.println("<br/>");
      out.println(request.getMethod());
      out.println("<br/>");
      out.println(request.getQueryString());
      out.println("<br/>");
      out.println(request.getRequestURL());
      out.println("<br/>");
      out.println(request.getLocalAddr());
      out.println("<br/>");
      out.println(request.getLocalName());
      out.println("<br/>");
      out.println(request.getLocalPort());
      out.println("<br/>");
      out.println(request.getRemoteAddr());
      out.println("<br/>");
      out.println(request.getRemoteHost());
    } finally {
      out.close();
    }
  }
   
  /**
   * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
   */
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // TODO Auto-generated method stub
   
    processRequest(request, response);
  }

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

    processRequest(request, response);
  }
}

相關呼叫:https://:999/webTest/Hello

localhost
999
/webTest
/Hello
GET
null
https://:999/webTest/Hello
127.0.0.1
localhost
999
127.0.0.1
127.0.0.1

引數讀取方法

[編輯 | 編輯原始碼]
// Parameter retrieving by the "parameterName"
request.getParameter(parameterName);

// Returns an enumeration of all the parameters of a request
request.getParameterNames();

// Returns all the parameter values of "parameterName"
request.getParameterValues(parameterName);

// Returns an iteration of the parameter names
request.getParameterMap();

頭資訊讀取方法

[編輯 | 編輯原始碼]
// Returns the header names
request.getHeaderNames();

// Returns the headers
request.getHeaders();

// Returns the number of bytes of the request
request.getContentLength();

// Returns the content type of the request
request.getContentType();

// Returns the default language
request.getLocale();

// Returns the list of languages
request.getLocales();

// Returns the header date
request.getDateHeader(String name);

// Returns the header specified by the "name"
request.getHeader(String name);

// Returns the headers specified by the "name"
request.getHeaders(String name);

用於新增資訊的請求方法

[編輯 | 編輯原始碼]
// Store an object in the HttpServletRequest object
request.setAttribute(String name, Object o);

// Returns the stored object "name"
request.getAttribute(String name);

// Returns an enumeration of the attribute names
request.getAttributeNames();

// Remove the attribute "name" the attributes of the request
request.removeAttribute(String name);

用於新增頭資訊的方法

[編輯 | 編輯原始碼]

HTTP 頭資訊為響應新增補充資訊。

// Indicates the nature of the information put into the response
response.setContentType(String type);

// Indicates the local language
response.setLocale(Locale loc);

// Adds the header "name" with the value "value"
response.addHeader(String name, String value);

響應主體構建

[編輯 | 編輯原始碼]
  • (PrintWriter)getWriter() 允許將文字傳輸到 HTTP 響應主體。
  • (ServletOutputStream)getOutputStream() 允許將二進位制資料傳輸到 HTTP 響應主體。

切勿同時使用這兩種方法,否則會導致 IllegalStateException 異常。

以下是一個二進位制傳送的示例

package servlet;

import java.io.IOException;

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 ReponseHttp2
 */
@WebServlet("/HttpResponse2")
public class HttpResponse2 extends HttpServlet {
  private static final long serialVersionUID = 1L;
       
  /**
   * @see HttpServlet#HttpServlet()
   */
  public HttpResponse2() {
    super();
    // TODO Auto-generated constructor stub
  }
   
  protected void processRequest(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {
    response.sendRedirect("images/toto.jpg");
  }
   
  /**
   * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
   */
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // TODO Auto-generated method stub
    processRequest(request, response);
  }

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

}

在這個示例中,我們使用 processRequest(request, response) 來始終在 GET 或 POST 中獲得相同的處理過程。

構建 HTTP 響應

[編輯 | 編輯原始碼]

狀態碼分為 5 類

1XX:資訊
2XX:成功
3XX:重定向
4XX:客戶端錯誤
5XX:伺服器錯誤

有關這些程式碼的詳細資訊,請訪問 http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

  • setStatus(int rc)
  • sendError(int sc, String message)

... 允許設定頁面的錯誤狀態。例如,我們將生成一個伺服器錯誤。

package 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 ReponseHTTP
 */
@WebServlet("/ReponseHTTP")
public class ReponseHTTP extends HttpServlet {

  private static final long serialVersionUID = 1L;
       
  /**
   * @see HttpServlet#HttpServlet()
   */
  public ReponseHTTP() {
    super();
    // TODO Auto-generated constructor stub
  }

  protected void processRequest(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {
    response.setContentType("text/html;charset=UTF-8");
    PrintWriter out = response.getWriter();
    try {
      response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "the server is overloaded. Please try later.");
    } finally {
      out.close();
    }
  }
   
  /**
   * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
   */
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // TODO Auto-generated method stub
    processRequest(request, response);
  }

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

其他資源

[編輯 | 編輯原始碼]

RequestDispatcher

[編輯 | 編輯原始碼]

包含外部資源。

RequestDispatcher rd = null;
rd = getServletContext().getRequestDispatcher("/header.html");
rd.include(request, response);

Servlet 結果傳輸到另一個資源。

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

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.HttpServlet;
import javax.servlet.HttpServletRequest;
import javax.servlet.HttpServletResponse;

public class Result extends HttpServlet {
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
       
      response.setContentType("text/html;charset=UTF-8");
      PrintWriter out = response.getWriter();
      try {
        int result = 10;
        RequestDispatcher rd = null;
        rd = getServletContext().getRequestDispatcher("/result.jsp");
        request.setAttribute("theResult", result);
        rd.forward(request, response);
      } finally {
        out.close();
      }
   }
}

目標資源:result.jsp

<%@page contentType="text/html" pageEncoding="UTF-8">
<%@page import="java.text.*" %>
<html>
  <body>
    <%
    theResult = (double) request.getAttribute("theResult");
    %>

    Result: <%= theResult %>
  </body>
</html>

重定向

[編輯 | 編輯原始碼]

伺服器通知瀏覽器向給定的 URL 傳送 HTTP 請求。

response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); // For a 301 HTTP response
response.setHeader("location", "http://www.google.be"); // For a 302 HTTP response
response.sendRedirect("http://www.google.be");

過濾器

[編輯 | 編輯原始碼]

過濾器在 HTTP 請求被 Servlet 接收之前或在 HTTP 響應被返回之前新增一個處理過程。

其作用包括:

  • 訪問控制
  • 訪問日誌
  • 資料解壓縮
  • 解密
  • 資料轉換

可以依次執行多個響應。過濾器類必須實現 javax.servlet.Filter 介面。

Clipboard

待辦事項
新增示例。


事件通知應用程式操作,例如物件的例項化和銷燬。

  • 應用程式相關事件:由 Servlet 上下文觸發的事件。
  • 會話相關事件:這些類必須實現 HttpSessionListenerHttpSessionAttributeListener 介面。

Servlet 同步

[編輯 | 編輯原始碼]

這種同步避免了伺服器同時使用兩個執行緒,例如當同一個類的兩個例項使用兩個不同的客戶端呼叫同一個方法時。synchronized(this){} 必須應用於關鍵程序,例如計數器。

public class ... {
  public void method() {
    out = response.getWriter();
    synchronized(this) {
      counter++;
    }
    out.println("The counter value is " + counter);
  }
}
Clipboard

待辦事項
新增練習。


華夏公益教科書