一、ServletRequest接口
HttpServletRequest接口继承了ServletRequest接口,实现类通常代表一个实际的Http Request。
Servlet容器负责创建一个HttpServletRequest对象(在Web服务器接收到浏览器的一个请求后),然后Servlet容器把这个HttpServletRequest传递给相关Servlet的service方法。(其实同时创建和传递的还有HttpServletResponse对象,这也是我们在编写相关Servlet时,无须关注request是如何被接收,也无须关注response最终如何被返回,这些都是Servlet容器和Web服务器的职责)
ServletRequest接口,定义了一个能代表实际request的对象,它包含了这个request的各项信息。
一个ServletRequest对象提供的数据包括了parameter name and value,attributes,以及input stream。HttpServletRequest对象还能提供额外的http协议相关的信息(他是唯一的子接口)。
当然接口只是定义了行为规范,Java为其提供了两个基础的实现,以供开发人员扩展:
HttpServletRequestWrapper, ServletRequestWrapper。
二、Request中的parameter 和 attributes
2.1 首先来个大体的区分
Request parameter是客户端提交过来的参数,以“name=value”的字符串形式存储在Request对象中,所以对于parameter,只有getParameter方法。
例如,我们登录时提交账户信息,假如是GET方法提交(实际上不会用GET提交),那么在提交的信息会以"?username=root&password=123456"这种形式附加在表单指向的url后面,这种形式称之为Query String。而POST方式则会把信息放在Request Body当中,这种形式称之为POST Body。
一个有意思的点是,Query string data is presented(显示) BEFORE post body data.
例如,如果使用Query String提交“a=hello”和POST Body提交“a=goodbye“和”a=world”,则结果参数集将被排序为a=(hello, goodbye, world)。
更新:发现我以前讲得不够清楚。可以参考下列网址:
How are parameters sent in an HTTP POST request?
HTTP中application/x-www-form-urlencoded字符说明
在将post表单数据填充到参数集(parameter set)之前,必须满足以下条件:
- The request is an HTTP or HTTPS request.
- The HTTP method is POST.
- The content type is application/x-www-form-urlencoded.
- The servlet has made an initial call of any of the 'getParameter' family of methods on the request object.
如果不满足条件,且post表单数据不包含在参数集中,post数据仍然可以通过request object的输入流提供给servlet。
如果满足这些条件,post表单数据将不再可以直接从请求对象的输入流中读取。
Request attributes则与客户端无关,服务器端接收后产生的一个Request对象,可能在各个不同的Servlet间传递而进行必要的处理,这时我们可以在Servlet A中通过setAttribute(String name,Object)方法存储一些信息,然后在Servlet B中通过getAttribute(String name)方法,取出相关的Object对象,进行一些处理。
API中提到了一点,Attributes are reset between requests,我有点疑惑为什么要特意提这样一句,直观上来说,在不同的Request对象上存储东西,二者当然不会共享吧?所以对Attributes的存储机制开始好奇了。
An attribute is a server variable that exists within a specified scope i.e.:
- application, available for the life of the entire application
- session, available for the life of the session
- request, only available for the life of the request
- page (JSP only), available for the current JSP page only
可以确定的一点是,当代码中出现了setAttribute()方法时,那么这个Request对象必定会与一个RequestDispatcher对象一起工作了,因为你在此处添加了一些有用的数据,肯定要在别处取出来用吧?
2.2 RequestDispatcher接口
先总结,RequestDispatcher接口类用于request的转发。
RequestDispatcher也是一个接口,它定义一个这样的对象:接收来自客户端的request,然后把他们送到服务器的其他资源上去(如servlet,html文件,JSP文件)。所以,通常是Servlet容器创建RequestDispatcher对象,用于包装一个有特定路径或者名称的服务器端资源。
这个接口用于包装servlet,但是servlet容器可以创建RequestDispatcher对象来包装任何类型的资源。
在JDK中,这个接口没有任何子接口和实现类。但是在ServletContext接口(Defines a set of methods that a servlet uses to communicate with its servlet container)中有几个get方法来获得RequestDispatcher对象。
RequestDispatcher接口,也仅有两个方法:
void forward(ServletRequest request, ServletResponse response),把一个request从一个Servlet中转发到服务端的另一处资源(servlet, JSP file, or HTML file)。
该方法允许一个servlet对请求进行初步处理,并允许另一个资源生成响应。
对于通过getRequestDispatcher()获得的RequestDispatcher, ServletRequest对象调整了其路径元素和参数,以匹配目标资源的路径。
forward()方法,应该在将response提交给客户机之前(在response body output被刷新之前)调用。如果response已经被提交了,调用forward()会抛出IllegalStateException。 response缓冲区中未提交的输出,在forward()调用前,会自动被清除。(Uncommitted output in the response buffer is automatically cleared before the forward.)
传递给forward的两个对象,要和servlet中service方法中获得的对象是相同的。即针对的是同一次请求过程。
void include(ServletRequest request, ServletResponse response),这个方法用于向response中添加一些资源(servlet, JSP page, HTML file)。
这里就涉及到ServletResponse对象了,它代表着一个要被返回的Response。ServletResponse对象也由Servlet容器创建,(在这样的一个转发过程中)它的路径元素和参数与调用者的保持不变(The ServletResponse object has its path elements and parameters remain unchanged from the caller's. )而且进行include操作的servlet,无法改变response的状态码,也无法设置header信息,这样的操作会被无视掉。(why?)
这里 include方法的ServletRequest request和ServletResponse response必须与传递给调用该include方法的servlet的service方法的两个ServletRequest request和ServletResponse response是同一对(反正一般也不会自己创建了),或者是包装前面这两个对象的ServletRequestWrapper或ServletResponseWrapper类的子类。
三、ServletResponse接口
3.1
HttpServletResponse接口的父接口,定义一个对象来帮助servlet向客户端发送response。
Servlet容器会创建一个 ServletResponse对象,然后与ServletRequest一起传递给servlet的service方法。
如果要在MIME body response中发送二进制数据,请使用getOutputStream()返回的ServletOutputStream。(什么是MIME)
要发送字符数据,使用getWriter()返回的PrintWriter对象。
要混合二进制和文本数据,例如,创建一个multipart响应,请使用ServletOutputStream并手动管理字符部分。
MIME主体响应的字符集可以使用以下任何一种技术显式指定:
per request, per web-app (using ServletContext.setRequestCharacterEncoding(java.lang.String), deployment descriptor(即web.xml)), and per container (for all web applications deployed in that container, using vendor specific configuration(更改web容器的配置)).
如果使用了前面提到的多种技术,那么优先级就是所列的顺序。
对于每个请求,可以使用本接口中的setCharacterEncoding(java.lang.String)和setContentType(java.lang.String)方法显式地指定响应的字符集,或者隐式地使用setLocale(java.util.Locale)方法 。
显式规范优先于隐式规范。如果没有明确指定字符集,将使用ISO-8859-1。
setCharacterEncoding、setContentType或setLocale方法的调用必须在getWriter之前,并在提交要使用的字符编码的响应之前
3.2 方法
ServletResponse接口定义的方法并不复杂,都和设置response的相关的格式有关,其中的一些会设置reponse header。
四、再谈谈ServletContext接口
前面已经提到过了,ServletContext中定义了一系列方法让servlet与servlet容器打交道,例如获取文件的MIME类型、分派request或写入日志文件。
每个Java虚拟机中的每个“web应用程序”,都有一个上下文(context)。
而一个“Web Application”的定义:
A "web application" is a collection of servlets and content installed under a specific subset of the server's URL namespace, such as /catalog and possibly installed via a .war file.)
对于在部署描述符中标记为“distributed”的web应用程序,每个虚拟机都有一个上下文实例。在这种情况下,上下文不能用作共享全局信息的位置(因为信息不是真正的全局的)。使用外部资源,比如数据库。
ServletContext对象包含在ServletConfig对象中,这个对象用于Web服务器在初始化servlet时提供给servlet。API链接
ServletContext提供的方法,与我们在web.xml中常进行的一些配置相似。如添加Filter、Listener、Servlet等。