转载: Servlet规范
分派请求
构建Web应用时,把请求转发给另一个servlet处理、或在response中包含另一个servlet的输出通常是很有用的。RequestDispatcher接口提供了一种机制来实现这种功能。
当请求启用异步处理时,AsyncContext允许用户将这个请求分派回servlet容器。
9.1 获得一个RequestDispatcher
实现了RequestDispatcher接口的对象,可以ServletContext的下面方法得到:
getRequestDispatcher
getNamedDispatcher
getRequestDispatcher方法需要一个String类型的参数描述在ServletContext作用域内的路径。这个路径必须是相对于ServletContext的根路径,并且以’/’开头,或者为空。该方法根据这个路径使用servlet路径匹配规则(见第12章,请求映射到servlet)来查找servlet,把它包装成RequestDispatcher对象并返回。如果基于给定的路径没有找到相应的servlet,那么提供一个返回那个路径内容的RequestDispatcher。
getNamedDispatcher方法使用一个ServletContext知道的servlet名称作为参数。如果找到一个servlet,则把它包装成RequestDispatcher对象,并返回该对象。如果没有与给定名字相关的servlet,该方法必须返回null。
为了允许使用相对于当前请求路径的相对路径(不是相对于ServletContext根路径)获得RequestDispatcher对象,在ServletRequest接口中提供了getRequestDispatcher方法。
此方法的行为与ServletContext中同名的方法相似。Servlet容器根据request对象中的信息把给定的相对路径转换成当前servlet的完整路径。例如,在以’/’作为上下文根路径和请求路径/garden/tools.html中,通过ServletRequest.getRequestDispatcher(“header.html”)获得的请求分派器和通过调用ServletContext.getRequestDispatcher(“/garden/header.html”)获得的完全一样。
9.1.1 请求分派器路径中的查询字符串
ServletContext和ServletRequest中使用路径信息创建RequestDispatcher对象的方法允许把可选的查询字符串信息附加到路径中。比如,开发人员可以通过下面的代码来获得一个RequestDispatcher:
String path = “/raisins.jsp?orderno=5”;
RequestDispatcher rd = context.getRequestDispatcher(path);
rd.include(request, response);
查询字符串中指定的用来创建RequestDispatcher的参数优先于传递给它包含的servlet中的其他同名参数。与RequestDispatcher相关的参数作用域仅适用于包含(include)或转发(forward)调用期间。
9.2 请求分派器的使用
要使用请求分派器,servlet可调用RequestDispatcher接口的include或forward方法。这些方法的参数既可以是javax.servlet.Servlet接口的service方法传来的request和response对象实例,也可以是本规范的2.3版本中介绍的request和response包装器类的子类对象实例。对于后者,包装器实例必须包装容器传递到service方法中的request和response对象。
容器提供者应该保证分派到目标servlet的请求作为原始请求发生在同一个JVM的同一个线程中。
9.3 include方法
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。
9.3.1 包含(include)的请求参数
除了可以用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路径、路径信息、查询字符串相等。如果包含后续请求,那么这些属性会被后面包含请求的相应属性值替换。
如果通过getNamedDispatcher方法获得包含的servlet,那么不能设置这些属性。
9.4 forward方法
RequestDispatcher接口的forward方法,只有在没有输出提交到向客户端时,才能通过正在被调用的servlet调用。如果response缓冲区中存在尚未提交的输出数据,这些数据内容必须在目标servlet的service方法调用前清除。如果response已经提交,必须抛出一个IllegalStateException异常。
request对象暴露给目标servlet的路径元素(path elements)必须反映获得RequestDispatcher使用的路径。
唯一例外的是,如果RequestDispatcher是通过getNamedDispatcher方法获得。这种情况下,request对象的路径元素必须反映这些原始请求。
在RequestDispatcher接口的forward方法无异常返回之前,必须发送和提交响应的内容,且由Servlet容器关闭,除非请求处于异步模式。如果RequestDispatcher.forward()的目标发生错误,异常信息会传回所有调用它经过的过滤器和servlet,且最终传回给容器。
9.4.1 查询字符串
在转发或包含请求时请求分派机制负责聚集(aggregating)查询字符串参数。
9.4.2 转发(forward)的请求参数
除了使用getNamedDispatcher方法获得的servlet外,已经被另一个servlet使用RequestDispatcher的forward方法调用过的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方法的返回值相等,调用请求对象的这些方法把值传递给从客户端接收请求的调用链中的第一个servlet对象。
这些属性从转发servlet通过request对象的getAttribut方法访问。请注意,即使在多个转发和相继的包含(subsequentincludes)被调用的情况下,这些属性必须始终反映原始请求中的信息。
如果转发的servlet使用getNamedDispatcher方法获得,必须不能设置这些属性。
9.5 错误处理
如果请求分发的目标servlet抛出运行时异常或受检查类型异常ServletException 或 IOException,异常应该传播到正在调用的servlet。所有其它的异常都应该被包装成ServletExceptions,异常的根本原因设置成原来的异常,因为它不应该被传播。
9.6 获得一个异步上下文对象
实现了AsyncContext接口的对象可从ServletRequest的一个startAsync方法中获得,一旦有了AsyncContext对象,你就能够使用它的complete()方法来完成请求处理,或使用下面描述的转发方法。
9.7 dispatch方法
可以使用下面的方法从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中指定的路径。
9.7.1 查询字符串
请求分派机制是在分派请求时负责聚焦(aggregating)查询字符串参数。
9.7.2 分派的请求参数
使用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方法的返回值相等,调用请求对象的这些方法把值传递给从客户端接收请求的调用链中的第一个servlet对象。
这些属性从转发servlet通过request对象的getAttribut方法访问。请注意,即使在多个转发和相继的包含(subsequent includes)被调用的情况下,这些属性必须始终反映原始请求中的信息。