Servlet方法說明

Serlvet接口只定義了一個服務方法就是service,而HttpServlet類實現了該方法並且要求調用下列的方法之一:
doGet:處理GET請求
doPost:處理POST請求
doPut:處理PUT請求
doDelete:處理DELETE請求
doHead:處理HEAD請求
doOptions:處理OPTIONS請求
doTrace:處理TRACE請求
通常情況下,在開發基於HTTP的servlet時,開發者只需要關心doGet和doPost方法,其它的方法需要開發者非常的熟悉HTTP編程,因此這些方法被認爲是高級方法。
而通常情況下,實現的servlet都是從HttpServlet擴展而來。

doPut和doDelete方法允許開發者支持HTTP/1.1的對應特性;
doHead是一個已經實現的方法,它將執行doGet但是僅僅向客戶端返回doGet應該向客戶端返回的頭部的內容;
doOptions方法自動的返回servlet所直接支持的HTTP方法信息;
doTrace方法返回TRACE請求中的所有頭部信息。

對於那些僅僅支持HTTP/1.0的容器而言,只有doGet, doHead 和 doPost方法被使用,因爲HTTP/1.0協議沒有定義PUT, DELETE, OPTIONS,或者TRACE請求。

另外,HttpServlet定義了getLastModified方法以支持有條件的(conditional)get操作。有條件的get操作是指使用GET方式請求資源並且在頭部指定只有在資源內容在指定時間後被修改的情況下服務器纔有必要回應請求併發送請求的內容。對於那些實現doGet方法並且在不同請求之間內容相同的servlet而言,它應該實現這個方法以提高網絡資源的利用率。

 

Spring的參考實現:

public  void  service(ServletRequest req, ServletResponse res)   
    throws ServletException, IOException   
{   
    HttpServletRequest  request;   
    HttpServletResponse response;   
    
    try {   
        // 既然是 HTTP 協議綁定的 Serlvet, 強制轉換到 HTTP 的領域模型   
        request = (HttpServletRequest) req;   
        response = (HttpServletResponse) res;   
} catch (ClassCastException e) {   
       // 如果傳入的 HTTP 請求和 HTTP 響應不是 HTTP 的領域模型,則拋出 Servlet 異常,這個異常會被 Servlet 容器所處理   
        throw new ServletException( "non-HTTP request or response" );   
    }   
    
    // 如果傳入的請求和響應是預期的 HTTP 請求和 HTTP 響應,則調用 service() 方法。   
    service(request, response);   
}   
    
protected void service(HttpServletRequest req, HttpServletResponse resp)   
    throws ServletException, IOException   
{   
    // 從 HTTP 請求中取得這次請求所使用的 HTTT 方法   
    String method = req.getMethod();   
    
    if (method.equals(METHOD_GET)) {   
       // 如果這次請求使用 GET 方法   
    
        // 取得這個 Servlet 的最後修改的時間   
        long lastModified = getLastModified(req);   
        if (lastModified == -1) {   
            //-1 代表這個 Servlet 不支持最後修改操作,直接調用 doGet() 進行處理 HTTP GET 請求   
            doGet(req, resp);   
        } else {   
            // 如果這個 Servlet 支持最後修改操作,取得請求頭中包含的請求的最後修改時間   
            long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);   
    
            if (ifModifiedSince < (lastModified / 1000 * 1000)) {   
            // 如果請求頭中包含的修改時間早於這個 Servlet 的最後修改時間,說明這個 Servlet 自從客戶上一次 HTTP 請求已經被修改了 , 設置最新修改時間到響應頭中   
              maybeSetLastModified(resp, lastModified);   
                
              // 調用 doGet 進行進行處理 HTTP GET 請求   
              doGet(req, resp);   
             } else {   
                // 如果請求頭中包含修改時間晚於這個 Servlet 的最後修改時間,說明這個 Servlet 自從請求的最後修改時間後沒有更改過,這種情況下,僅僅返回一個 HTTP 響應狀態 SC_NOT_MODIFIED   
                resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);   
             }   
        }   
    
    } else if (method.equals(METHOD_HEAD)) {   
        // 如果這次請求使用 POST 方法   
    
        // 如果這個 Servlet 支持最後修改操作,則設置這個 Servlet 的最後修改時間到響應頭中   
        long lastModified = getLastModified(req);   
        maybeSetLastModified(resp, lastModified);   
    
       // 和對 HTTP GET 方法處理不同的是,無論請求頭中的修改時間是不是早於這個 Sevlet 的最後修改時間,都會發 HEAD 響應給客戶,因爲 HTTP HEAD 響應是用來查詢 Servlet 頭信息的操作   
        doHead(req, resp);   
    
    } else if (method.equals(METHOD_POST)) {   
        // 如果這次請求使用 POST 方法   
        doPost(req, resp);   
    
    } else if (method.equals(METHOD_PUT)) {   
        // 如果這次請求使用 PUT 方法   
        doPut(req, resp);     
    
    } else if (method.equals(METHOD_DELETE)) {   
        // 如果這次請求使用 DELETE 方法   
        doDelete(req, resp);   
    
    } else if (method.equals(METHOD_OPTIONS)) {   
        // 如果這次請求使用 OPTIONS 方法   
        doOptions(req,resp);   
    
    } else if (method.equals(METHOD_TRACE)) {   
        // 如果這次請求使用 TRACE 方法   
        doTrace(req,resp);   
    
    } else {   
       // 如果這次請求是其他未知方法,返回錯誤代碼 SC_NOT_IMPLEMENTED 給 HTTP 響應,並且顯示一個錯誤消息,說明這個操作是沒有實現的   
        String errMsg = lStrings.getString( "http.method_not_implemented" );   
        Object[] errArgs = new Object[1];   
        errArgs[0] = method;   
        errMsg = MessageFormat.format(errMsg, errArgs);   
    
        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);   
    }   
}  
protected  void  doGet(HttpServletRequest req, HttpServletResponse resp)   
    throws ServletException, IOException   
{   
    // 取得請求頭包含的 HTTP 協議版本   
    String protocol = req.getProtocol();   
    
    // 直接發送錯誤消息,可見,一個子類需要重寫這些佔位符方法 doGet(), doPost(), doPut(), doDelete() 中的一個或者多個   
    String msg = lStrings.getString( "http.method_get_not_supported" );   
    if (protocol.endsWith( "1.1" )) {   
        // 如果是 HTTP 1.1, 發送 SC_METHOD_NOT_ALLOWED   
        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);   
    } else {   
        // 如果是 HTTP 的更早版本則發送 SC_BAD_REQUEST   
        resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);   
    }   
}   
protected  void  doHead(HttpServletRequest req, HttpServletResponse resp)   
    throws ServletException, IOException   
{   
    // 構造一個特殊的響應類,這個類內部忽略了所有的響應體的輸出   
    NoBodyResponse response = new NoBodyResponse(resp);   
      
    // 重用 doGet() 處理器羅傑   
    doGet(req, response);   
      
    // 設置響應體的字節大小,儘管響應體並沒有輸出,但是客戶端可能關係這個信息   
    response.setContentLength();      
}  
 
// 這個方法 doOptions() 設置支持的 HTTP 方法名稱到相應頭中   
protected void doOptions(HttpServletRequest req, HttpServletResponse resp)   
throws ServletException, IOException   
{   
    // 取得當前這個 Servlet 以及父類 Servlet 聲明的所有方法,這些方法不包括本類 HTTP Servlet 所生命的方法   
    Method[] methods = getAllDeclaredMethods( this .getClass());   
      
    // 初始化的時候,假設它不支持任何 HTTP 方法   
    boolean ALLOW_GET = false ;   
    boolean ALLOW_HEAD = false ;   
    boolean ALLOW_POST = false ;   
    boolean ALLOW_PUT = false ;   
    boolean ALLOW_DELETE = false ;   
    boolean ALLOW_TRACE = true ;   
    boolean ALLOW_OPTIONS = true ;   
      
    // 根據子類 Servlet 是否重寫了 HTTP Servlet 的佔位符方法,判斷是否這個 Servlet 實現支持這種 HTTP 方法,例如,如果子類 Servlet 實現了 doGet(), 然後 HTTP GET 方法是支持的   
    for ( int i=0; i<methods.length; i++) {   
        // 遍歷得到的所有生命的方法   
        Method m = methods[i];   
          
        // 如果名字是 doGet(), doPost(),  doPut() 或者 doDelete(), 它支持相應的方法   
        if (m.getName().equals( "doGet" )) {   
            ALLOW_GET = true ;   
            ALLOW_HEAD = true ;   
        }   
        if (m.getName().equals( "doPost" ))   
            ALLOW_POST = true ;   
        if (m.getName().equals( "doPut" ))   
            ALLOW_PUT = true ;   
        if (m.getName().equals( "doDelete" ))   
            ALLOW_DELETE = true ;   
          
    }   
      
     // 把支持的 HTTP 方法名稱拼接成逗號分割的字符串,例如, “GET, POST”, “GET, POST, PUT, DELETE”   
    String allow = null ;   
    if (ALLOW_GET)   
        if (allow== null ) allow=METHOD_GET;   
    if (ALLOW_HEAD)   
        if (allow== null ) allow=METHOD_HEAD;   
        else allow += ", " + METHOD_HEAD;   
    if (ALLOW_POST)   
        if (allow== null ) allow=METHOD_POST;   
        else allow += ", " + METHOD_POST;   
    if (ALLOW_PUT)   
        if (allow== null ) allow=METHOD_PUT;   
        else allow += ", " + METHOD_PUT;   
    if (ALLOW_DELETE)   
        if (allow== null ) allow=METHOD_DELETE;   
        else allow += ", " + METHOD_DELETE;   
    if (ALLOW_TRACE)   
        if (allow== null ) allow=METHOD_TRACE;   
        else allow += ", " + METHOD_TRACE;   
    if (ALLOW_OPTIONS)   
        if (allow== null ) allow=METHOD_OPTIONS;   
         else allow += ", " + METHOD_OPTIONS;   
      
    // 把支持的方法拼接成的字符串設置到 HTTP 協議的相應頭中,這個值的 key 是 "Allow"   
    resp.setHeader( "Allow" , allow);   
}   
    
// 這個方法返回一個字符串到 HTTP 響應體裏面,這個字符串包含請求 URL, 版本信息以及請求的頭信息,主要是用來調試   
protected void doTrace(HttpServletRequest req, HttpServletResponse resp)   
    throws ServletException, IOException   
{   
      
    int responseLength;   
      
    
    // 連接 URI 字符串和協議版本信息字符串   
    String CRLF = "\r\n" ;   
    String responseString = "TRACE " + req.getRequestURI()+   
        " " + req.getProtocol();   
      
    Enumeration reqHeaderEnum = req.getHeaderNames();   
    // 遍歷所有的請求頭信息   
    while ( reqHeaderEnum.hasMoreElements() ) {   
        String headerName = (String)reqHeaderEnum.nextElement();   
          
        // 拼接所有的請求頭到字符串中,並且使用:分割名值對,每對頭信息之間使用回車換行進行分隔   
        responseString += CRLF + headerName + ": " +   
        req.getHeader(headerName);   
    }   
      
    // 附着回車換行符到字符串結尾   
    responseString += CRLF;   
      
    // 取得字符串字節長度信息   
    responseLength = responseString.length();   
      
    // 設置響應類型爲 message/http   
    resp.setContentType( "message/http" );   
      
    // 設置響應體的長度   
    resp.setContentLength(responseLength);   
    
    // 輸出字符串消息到響應中   
    ServletOutputStream out = resp.getOutputStream();   
    out.print(responseString);   
      
    // 關閉相應流,結束操作   
    out.close();   
    return ;   
} 


發佈了73 篇原創文章 · 獲贊 60 · 訪問量 32萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章