构建Web应用时,把请求转发给另一个servlet处理、或在response中包含另一个servlet的输出通常是很有用的。RequestDispatcher接口提供了一种机制来实现这种功能。
当请求启用异步处理时,AsyncContext允许用户将这个请求转发到servlet容器。
实现了RequestDispatcher接口的对象,可以从ServletContext中的下面方法得到:
getRequestDispatcher
getNamedDispatcher
getRequestDispatcher方法需要一个String类型的参数描述在ServletContext作用域内的路径。这个路径必须是相对于ServletContext的根路径,或以’/’开头,或者为空。该方法根据这个路径使用servlet路径匹配规则(见第12章,请求映射到servlet)来查找servlet,把它包装成RequestDispatcher对象并返回。如果基于给定的路径没有找到相应的servlet,那么返回这个路径内容提供的RequestDispatcher。
getNamedDispatcher方法使用一个ServletContext知道的servlet名称作为参数。如果找到一个servlet,则把它包装成RequestDispatcher对象,并返回该对象。如果没有与给定名字相关的servlet,该方法必须返回null。
为了让RequestDispatcher对象使用相对于当前请求路径的相对路径(不是相对于ServletContext根路径)获得一个servlet,在ServletRequest接口中提供了getRequestDispatcher方法。
此方法的行为与ServletContext中同名的方法相似。Servlet容器根据request对象中的信息把给定的相对路径转换成当前servlet的完整路径。例如,在以’/’作为上下文根路径和请求路径/garden/tools.html中,通过ServletRequest.getRequestDispatcher("header.html")获得的请求调度器和通过调用ServletContext.getRequestDispatcher("/garden/header.html")获得的完全一样。
ServletContext和ServletRequest中创建RequestDispatcher对象的方法使用的路径信息中允许附加可选的查询字符串信息。比如,开发人员可以通过下面的代码来获得一个RequestDispatcher:
String path = “/raisins.jsp?orderno=5”;
RequestDispatcher rd = context.getRequestDispatcher(path);
rd.include(request, response);
查询字符串中指定的用来创建RequestDispatcher的参数优先于传递给它包含的servlet中的其他同名参数。与RequestDispatcher相关的参数作用域仅适用于包含(include)或转发(forward)调用期间。
要使用请求调度器,servlet可调用RequestDispatcher接口的include或forward方法。这些方法的参数既可以是javax.servlet.Servlet接口的service方法传来的request和response对象实例,也可以是本规范的2.3版本中介绍的request和response包装器类的子类对象实例。对于后者,包装器实例必须包装容器传递到service方法中的request和response对象。
容器提供者应该保证分发到目标servlet的请求作为原始请求发生在的同一个JVM的同一个线程中。
RequestDispatcher接口的include方法可能随时被调用。Include方法的目标servlet能够访问request对象的各个方法(all aspects),但是使用response对象的方法会受到更多限制。
它只能把信息写到response对象的ServletOutputStream或Writer中,或提交在最后写保留在response缓冲区中的内容,或通过显式地调用ServletResponse接口的flushBuffer方法。它不能设置响应头部信息或调用任何影响响应头部信息的方法,HttpServletRequest.getSession()和HttpServletRequest.getSession(boolean)方法除外。任何试图设置头部信息必须被忽略,任何调用HttpServletRequest.getSession()和HttpServletRequest.getSession(boolean)方法将需要添加一个Cookie响应头部信息,如果响应已经提交,必须抛出一个IllegalStateException异常。
如果默认的servlet是RequestDispatch.include()的目标servlet,而且请求的资源不存在,那么默认的servlet必须抛出FileNotFoundException异常。如果这个异常没有被捕获和处理,以及响应还未提交,则响应状态码必须被设置为500。
除了可以用getNamedDispatcher方法获得servlet外,已经被另一个servlet使用RequestDispatcher的include方法调用过的servlet,有权访问被调用过的servlet的路径。
以下的request属性必须被设置:
javax.servlet.include.request_uri
javax.servlet.include.context_path
javax.servlet.include.servlet_path
javax.servlet.include.path_info
javax.servlet.include.query_string
这些属性可以通过包含的servlet的request对象的getAttribute方法访问,它们的值必须分别与被包含servlet的请求RUI、上下文路径、servlet路径、路径信息、查询字符串相等。如果随后的请求包含这些属性,那么这些属性会被后面包含的属性值替换。
如果包含的servlet通过getNamedDispatcher方法获得,那么这些属性不能被设置。
RequestDispatcher接口的forward方法,只有在没有输出提交到向客户端时,通过正在被调用的servlet调用。如果response缓冲区中存在尚未提交的输出数据,这些数据内容必须在目标servlet的service方法调用前清除。如果response已经提交,必须抛出一个IllegalStateException异常。
request对象暴露给目标servlet的路径元素(path elements)必须反映获得RequestDispatcher使用的路径。
唯一例外的是,如果RequestDispatcher是通过getNamedDispatcher方法获得。这种情况下,request对象的路径元素必须反映这些原始请求。
在RequestDispatcher接口的forward方法无异常返回之前,响应的内容必须被发送和提交,且由Servlet容器关闭,除非请求处于异步模式。如果RequestDispatcher.forward()的目标发生错误,异常信息会传回所有调用它经过的过滤器和servlet,且最终传回给容器。
在转发或包含请求时请求调度机制负责聚集(aggregating)查询字符串参数。
除了可以用getNamedDispatcher方法获得servlet外,已经被另一个servlet使用RequestDispatcher的forward方法调用过的servlet,有权访问被调用过的servlet的路径。
以下的request属性必须设置:
javax.servlet.forward.request_uri
javax.servlet.forward.context_path
javax.servlet.forward.servlet_path
javax.servlet.forward.path_info
javax.servlet.forward.query_string
这些属性的值必须分别与HttpServletRequest的getRequestURI,、getContextPath、 getServletPath、getPathInfo、getQueryString方法的返回值相等,这些方法在从客户端接收到的request对象上调用,值传递给调用链中的第一个servlet对象。
(在request对象上调用从客户端接收请求的调用链中的第一个servlet对象)
这些属性通过转发servlet的request对象的getAttribut方法访问。请注意,即使在多个转发和相继的包含(subsequent includes)被调用的情况下,这些属性必须始终反映原始请求中的信息。
如果转发的servlet使用getNamedDispatcher方法获得,这些属性必须不能被设置。
如果请求分发的目标servlet抛出运行时异常或受检查类型异常ServletException 或 IOException,异常应该传播到调用的servlet。所有其它的异常都应该被包装成ServletExceptions,异常的根本原因设置成原来的异常,因为它不应该被传播。
实现了AsyncContext接口的对象可从ServletRequest的一个startAsync方法中获得,一旦有了AsyncContext对象,你就能够使用它的complete()方法来完成请求处理,或使用下面描述的转发方法。
可以使用AsyncContext中下面的方法来转发请求:
dispatch(path)
这个dispatch方法的String参数描述了一个在ServletContext作用域中的路径。这个路径必须是相对于ServletContext的根路径并以’/’开头。
dispatch(servletContext, path)
这个dispatch方法的String参数描述了一个在ServletContext指定作用域中的路径。这个路径必须是相对于ServletContext的根路径并以’/’开头。
dispatch()
这个方法没有参数,它使用原来的URI路径。如果AsyncContext已经通过startAsync(ServletRequest, ServletResponse)初始化,且传递过来的request是HttpServletRequest的实例,那么这个请求分发到HttpServletRequest.getRequestURI()返回的URI。否则当最后转发时当由容器转发到request的URI。
AsyncContext接口中的dispatch方法可被等待异步事件发生的应用程序调用。如果AsyncContext已经调用了complete()方法,必须抛出IllegalStateException异常。所有不同的dispatch方法会立即返回并且不会提交response。
request对象暴露给目标servlet的路径元素(path elements)必须反映AsyncContext.dispatch中指定的路径。
请求调度机制是在调度请求时负责聚焦(aggregating)查询字符串。
使用AsyncContext的dispatch方法调用过的servlet能够访问原始请求的路径。
下面的request属性必须设置:
javax.servlet.async.request_uri
javax.servlet.async.context_path
javax.servlet.async.servlet_path
javax.servlet.async.path_info
javax.servlet.async.query_string
这些属性的值必须分别与HttpServletRequest的getRequestURI,、getContextPath、 getServletPath、getPathInfo、getQueryString方法的返回值相等,这些方法在从客户端接收到的request对象上调用,值传递给调用链中的第一个servlet对象。
这些属性通过转发servlet的request对象的getAttribut方法访问。请注意,即使在多个转发和相继的包含(subsequent includes)被调用的情况下,这些属性必须始终反映原始请求中的信息。