http請求的參數和屬性

 練習搭建和使用SSH框架的時候,遇到一個http方面很簡單的問題。一個form的表單登錄請求,表單爲最基本的html表單內容,代碼如下

在struts的action中需要獲取輸入的用戶名和密碼,最開始我採用的代碼如下

但是在運行的時候,一直在user.setUserName(request.getAttribute("user_name").toString());這裏報空指針異常,後來改成
user.setUserName(request.getParameter("user_name"));之後就可以正常運行了。
還是上面的SSH項目,爲了瞭解這其中的區別,我將parameters和attributes都打印出來,結果如下
the actual type for request org.apache.struts2.dispatcher.StrutsRequestWrapper
attribute:< __cleanup_recursion_counter,1>
attribute:< struts.actionMapping,org.apache.struts2.dispatcher.mapper.ActionMapping@19a4e8e>
attribute:< struts.valueStack,com.opensymphony.xwork2.ognl.OgnlValueStack@19b7b9d>
parameter:<passwd,sdfg>
parameter:<user_name,lynn>
由此可見,表單傳遞請求的方式主要是通過parameters來完成。而attributes的使用就主要是框架內部使用。

雖然修改之後系統成功運行,但對於java的Http以及表單請求卻產生了許多問題,藉此機會做個瞭解。


以上是來自tomcat源代碼中整理出來的Servlet系統的一部分接口、類的繼承和實現關係。但由於這裏使用的是HttpServletRequest,所以研究的重點要放在HttpServletRequest類上,下面是根據tomat源代碼整理出來的HttpServletRequest系統的結構圖。

從上圖可以看出,本項目中的request.getAttribute和request.getParameter方法是繼承自父接口ServletRequest的。這上面解釋了tomcat所提供的http系統的大致框架結構。要理解這兩個方法之間具體的差別,需要知道這兩個對象的實際類型和對象實例化的點。使用
System. out.println("the actual type for request "+request.getClass().getName());將request對象的類型打印出來,顯示的結果爲“the actual type for request org.apache.struts2.dispatcher.StrutsRequestWrapper”
爲什麼是request的類型是org.apache.struts2.dispatcher.StrutsRequestWrapper呢?
StrutsRequestWrapper類是來自Struts框架,由於在項目中採用了SSH框架,Struts框架對tomcat的http系統進行了擴充。StrutsRequestWrapper是繼承自HttpServletRequestWrapper類,這個類又繼承自ServletRequestWrapper,並且實現了HttpServletRequest接口
public class HttpServletRequestWrapper extends ServletRequestWrapper implements HttpServletRequest{/*....*/},它提供一個帶HttpServletRequest類型參數的構造函數。代碼如下
public HttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
    }
其父類ServletRequestWrappet實現了ServletRequest接口,並且提供了一個帶ServletRequest類型參數的構造函數,相關代碼如下。
 private ServletRequest request;

    /**
     * Creates a ServletRequest adaptor wrapping the given request object.
     *
     * @throws java.lang.IllegalArgumentException
     *             if the request is null
     */
    public ServletRequestWrapper(ServletRequest request) {
        if (request == null) {
            throw new IllegalArgumentException("Request cannot be null");
        }
        this.request = request;
    }
在StrutsRequestWrapper類中重寫了getAttribute方法,沒有重寫getParameter方法。其父類HttpServletRequestWrapper中對這兩個方法都沒有進行重寫。所以,本項目中request對象調用的getParameter方法是來自於ServletRequestWrapper的,代碼如下
  @Override
    public String getParameter(String name) {
        return this .request .getParameter(name);
    }
結合其構造函數來看,具體其作用的還是在生成StrutsRequestWrapper對象時傳遞進來的HttpServletRequest對象。

那就從Struts框架的啓動配置入手,通過在web.xml中配置filter來將Struts框架引入項目。在web.xml中雨filter相關的配置代碼如下。也就是說,tomcat在加載項目的時候,會以StrutsPrepareAndExecuteFilter作爲入口,下面順藤摸瓜。
<filter>
               <filter-name> struts</ filter-name>
               <filter-class> org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter </filter-class>
        </filter>
StrutsPrepareAndExecuteFilter實現了Filter接口,每次進行請求的時候,tomcat會調用其doFilter方法。在doFilter代碼如下
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        try {
            prepare.setEncodingAndLocale(request, response);
            prepare.createActionContext(request, response);
            prepare.assignDispatcherToThread();
                      if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns )) {
                           chain.doFilter(request, response);
                     } else {
                           request = prepare.wrapRequest(request);
                           ActionMapping mapping = prepare.findActionMapping(request, response, true);
                            if (mapping == null) {
                                   boolean handled = execute.executeStaticResourceRequest(request, response);
                                   if (!handled) {
                                         chain.doFilter(request, response);
                                  }
                           } else {
                                   execute.executeAction(request, response, mapping);
                           }
                     }
        } finally {
            prepare.cleanupRequest(request);
        }
    }
其中先調用createActionContext創建了一個ActionContext對象。prePare是PrepareOpearation類型的對象,類的createAction方法中部分代碼如下
ActionContext oldContext = ActionContext.getContext();
        if (oldContext != null) {
            // detected existing context, so we are probably in a forward
            ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));
        } else {
            ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
            stack.getContext().putAll( dispatcher.createContextMap(request, response, null, servletContext ));
            ctx = new ActionContext(stack.getContext());
        }
        request.setAttribute( CLEANUP_RECURSION_COUNTER, counter);
        ActionContext. setContext(ctx);
        return ctx;
也就是說,這裏生成的Map<String,Object> context對象來自舊的ActionContext對象。如果舊的不存在,那就用最初的配置文件構造一個出來,也就是servletContext對象,在PrepareOperations類的構造函數中被初始化,也就是根據tomcat調用StrutsPrepareAndExecuteFilter的init方法時,傳遞進來的配置內容產生。
然後parepare.wrapRequest方法實現對reques對象的包裝,轉成StrutsRequestWrapper對象。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章