http请求的参数和属性

来源:互联网 发布:知道大数据 编辑:程序博客网 时间:2024/06/06 09:54
 练习搭建和使用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对象。