1. servlet 的生命周期。
a) init方法:首次创建servlet时,它的init方法会得到调用,因此,init是放置一次性设置代码的地方。
i. 可以使用此方法进行常规初始化,用来创建或载入在Servlet生命期内用到的一些数据,或者执行某些一次性的计算。
ii. 由初始化参数控制的初始化。
要理解init参数的动机,您需要了解什么样的人可能希望对Servlet的行为方式进行定制。其中包括:开发人员、最终用户、部署人员。
开发人员通过改变代码改变Servlet的行为。
最终用户通过向HTML表单提供数据改变Servlet的行为。
部署人员为了能够在不修改Servlet的源代码的情况下,就可以将Servlet在机器间移动,以及改变特定的参数(例如,数据库的地址,连接共享大小,或者数据文件的位置)。init参数的目的就是为了提供这项能力。
b) service方法:在init之后,针对每个用户请求,都会创建一个纯种,该线程调用前面创建的实例的service方法。service方法检查HTTP请求的类型(GET,POST,PUT,DELETE等)并相应地调用doGet,doPost,doPut,doDelete等方法。
c) doGet,doPost和doXxx方法
GET请求起因于正常的URL请求,或没有指定METHOD的HTML表单。POST请求起因于特别将POST列为METHOD的HTML表单。你可以覆盖doGet和/或doPost方法来处理。
DELETE 请求由doDelete处理,PUT 由doPut方法处理,OPTIONS 由doOptions处理,TRACE 由doTrace方法处理。OPTIONS和TRACE的请求在Servlet中是自动支持的。
doHead以处理HEAD请求(HEAD请求规定,服务器应该只返回正常的HTTP头,不含与之相关联的文档)。为了能够更快地生成对HEAD请求的响应(例如来自定制客户的请求,只需要HTTP报头,不需构建实际的文档输出),会实现doHead方法。
2. SingleThreadModel接口
a) 通过让Servlet实现这个接口,系统会保证不会有多个请求线程同时访问该servlet的单个实例。大多数和情况下,系统将所有的请求排队,一次只将一个请求转给单个servlet实例。
b) 此接口已经过时,如果要同步最好使用synchronized关键字。
1. 表单数据的读取。
a) 单个值的读取:getParameter(表单项名称),表单项名称大小写敏感。如果表单项存在但没有相应的值,返回空的String;如果没有这样的表单项,则返回null。
b) 多个值的读取:getParameterValues(表单项名称),如果同一表单项名称在表单数据中多次出现,则应该调用getParameterValues(返回字符串的数组)。对于不存在的表单项名称,getParameterValues的返回值是null,如果参数只有单一的值,则返回只有一个元素的数组。
如果是HTML表单的设计者,最好保证每个文本字段、复选框或其他用户界面元素都有一个唯一的名称。
2. 参数名的查找:getParameterNames和getParameterMap
getParameterNames以Enumeration的形式返回表单项名称列表,其中的的每一项都可以转换成String,并可以用在getParameter或getParameterValues调用中。如果当前请求中没有表单名称返回空的Enumeration(不是null)。
Enumeration只是一个接口,它保证实际的类实现了hasMoreElements和nextElement方法:它并不保证具体的实现会采用某种特定的底层数据结构。
getParameterNames的替代方案是getParameterMap。这个方法返回一个Map:表单名称(字符串)是表的键,表单项的值是表的值。
3. 读取上载的文件和原始数据:getReader或getInputStream
当数据不是HTML表单提交,而是来自于定制的客户程序时,可能需要自己读取和分析这些数据。最常见的客户程序是applet。
当数据来自于上载的文件时,可能需要自己读取数据。servlet的API没有定义任何机制来读取<input type=”file”>元素的文件。
4. 多字符集输入的读取:setCharacterEncoding(字符集名称)
request.getParameter使用服务器的当前字符集解释输入。要改变这种默认行为,需要使用ServletRequest的setCharacterEncoding方法。setCharacterEncoding必须在访问任何请求参数之前调用。
我们可以按照某个字符集读取参数,然后将它转换到另外的字符集;或者用某些字符集提供的自动检测特性。
第一种方案:使用getByte提取出原始的字节数据,之后将这些字节连同期望字符集的名称一同传递给String的构造函数。
例:
String firstNameWrongEncoding = request.getParameter(“firstName”);
String firstName = new String(firstNameWrongEncoding.getBytes(), “shift_JIS”);
第二种方案:需要使用一种支持从默认字符集进行检测和转换的字符集。
例:如果允许输入既可以是英语,也可以是日语,则要使用下面的语句:
request.setCharacterEncoding(“JISAutoDetect”);
String firstName = request.getParameter(“firstName”);
5. 参数缺失或异常时默认值的应用
如果用户没有提供必需的信息,那么该servlet应该怎么处理这种情况
a) 使用默认值
b) 重新显示这个表单(提示用户缺失的值)。
c) 请求中的参数需要检查下面三种情况:
i. 参数的值为null
用户使用了错误的表单或使用了包含GET数据的URL书签,但在制作URL书签之后,参数名发生变化,都会发生返回值为null的情况。
ii. 参数的值为空字符串(“”)
用户没有输入指定的值。为了安全起见,最好调用trim,移除用户可能输入的任何空格。
iii. 参数的值为非空字符串,但格式错误
在长度和类型(只要求数值的字段)上输入错误。在设计servlet时,要使之能够优雅地处理参数缺失(null或空字符串)或格式不正确等情况。
6. 过滤字符串中的HTML特殊字符
a) 如果servlet希望生成含有诸如<或>等字符的HTML,只需简单地使用标准的HTML字符实体——<或>。
b) 如果您需要读取请求参数,并将它们的值显示在生成的页面中,则必须过滤出那些特殊的HTML字符。不这样做可能会导致输出中缺失部分,或者某些部分格式错误。
c) Java字符串虽不可改变的(即不能修改),因此,重复的字符串拼接操作需要复制许多字符串片段,并在使用后废弃。建议在循环中执行重复性的拼接操作时,应该使用可以改变的数据结构:StringBuffer是通常的选择。
7. 根据请求参数自动填充Java对象:表单bean
a) 普通的Java对象,如果它所属的类使用私有字段,且拥有遵循get/set命令约定的方法,则可以看作是bean。方法名(除去单词“get”或”set”,并且首字母小写)称为属性(property)。
8. 当参数缺失或异常时重新显示输入表单。
a) 由同一servlet提供表单、处理数据并提供最后的结果。
表单省略Action属性,从而,表单提交时会自动发送到表单自身的URL。
b) 由一个servlet提供表单:由第二个servlet处理数据并提供结果。
从一个servlet转到另外的servlet可以使用response.sendredirect或RequestDispatcher的forward方法。将数据从负责处理的servlet传递回显示表单的servlet时,最简单的方式是将它存储在HttpSession对象中。
c) 由一个JSP页面“手动地”提供表单:由一个servlet或JSP页面处理数据并提供结果。
d) 由一个JSP页面提供表单,用从数据对象获取的值自动填写表单中相应的字段:由一个servlet或JSP页面处理这些数据并提供最终结果。
1. 简介:请求报头在JSP中的读取及应用与在servlet中相同。请求报头由浏览器间接地设定,并紧跟在初始的GET和POST请求行之后发送。
2. 请求报头的读取。
只需用报头的名称为参数,调用HttpServletRequest的getHeader方法。如果当前的请求中提供了指定的报头,则这个调用返回一个String,否则返回null。在HTTP1.0中,请求的所有报头都是可选的:在HTTP1.1中,只有Host是必需的。因而,在使用请求报头之前一定要检查是否为null。报头名称对大小写不敏感。
a) 访问HTTP报头的方法汇总。
getHeader是读取输入报头的通用方式。访问其他常用报头的方法有:
i. getCookies方法返回Cookie报头的内容。
ii. getAuthType和getRemoteUser
返回Authorization报头进行拆分,分解成它的各个构成部分。
iii. getContentLength
返回Content-Length 报送的值(作为一个int值返回)
iv. getContentType
返回Content-Type报头的值(作为一个String返回)
v. getDateHeader 和 getIntHeader
读取指定的报头,然后分别将它们转换成Date和int值。
vi. getHeaderNames
可以使用此方法得到一个Enumeration,枚举当前特定请求中所有的报头名称。
vii. getHeaders
大多数情况下,每个报头名称在请求中只出现一次。然而,报头偶尔也有可能出现多次,每次出现列出各自的值。可以使用此方法得到一个Enumeration,枚举报头每次出现所对应的值。
b) 获取主请求行自身的信息,同样是使用HttpServletRequest提供的方法:
i. getMethod
返回主请求方法(一般是GET或POST,也有可能是HEAD,PUT和DELETE方法)
ii. getRequestURI
返回URL中主机和端口之后,但在表单数据之前的部分。例:http://randomhost.com/servlet/search.BookSearch?subject=jsp,getRequestURI返回“/servlet/search.BookSearch”。
iii. getQueryString
返回表单数据。http://randomhost.com/servlet/search.BookSearch?subject=jsp,getQueryString返回“subject=jsp”。
iv. getProtocol
返回请求行的第三部分,一般为HTTP/1.0或HTTP/1.1。
c) 了解HTTP1.1请求报头
i. Accept:这个报头指明浏览器或其他客户程序能够处理的MIME类型。
ii. Accept-Charset:标明浏览器可以使用的字符集(如ISO-8859-1)。
iii. Accept-Encoding:详细列出客户端能够处理的编码类型。gzip或compress是二种最常见的值。
iv. Accept-Language:列出客户程序首选的语言。
v. Authorization:在访问密码保护的Web页面时,客户用这个报头来标识自己的身份。
vi. Connection:标明客户是否能够处理持续性HTTP连接。持续性连接允许客户或其他浏览器在单个socket 中读取多个文件(例如HTML文件及相关的几幅图像),从而节省协商几个独立连接所需的开销。
服务器只在读完HTTP请求之后,才会调用servlet。servlet的工作只是使服务器能够使用持续性连接;servlet通过设置Content-Length响应报头来做到这一点。
vii. Content-Length:这个报头只适用于POST请求,用来给定POST数据的大小,以字节为单位。只需简单地使用request.getContentlength()就可以得到这个报头的值。
viii. Cookie:这个报头向服务器返回cookie,这些cookie是之前由服务器发送给浏览器的。不要直接读取这个报头,而要使用requet.getCookies。
ix. Host:它标明原始URL中给出的主机名和端口号。
x. if-Modified-Since:仅当页面在指定的日期之后发生更改的情况下,客户程序才希望获取该页面。
xi. If-Unmodified-Since:这个报头和If-Modified-Since正好相反;它规定仅当文档比指定的日期要旧时,操作才需要继续。一般来说,If-Modified-Since用在GET请求中(“仅当文档比我缓存的版本要新时,才传送该文档”),而If-Unmodified-Since用在PUT请求中(“仅当我生成这个文档之后,没有其他人对它做过更改时,才更新这个文档”)。
xii. Referer:标明引用Web页面的URL。例如,如果您在Web页面1 单击指向Web页面2的链接,那么,在浏览器请求Web页面2 时,就会将Web页面1 的URL引入Referer报头。
xiii. User-Agent:标识生成请求的浏览器或其他客户程序,根据这个报头,可以针对不同类型的浏览器返回不同的内容。
简介:Web服务器对请求的响应,一般由一个状态行、一些响应报头、一个空行和相应的文档构成。状态行由HTTP版本、一个状态代码(整数)、以及一段对应状态代码的简短消息(如OK)组成。除MIME类型的Content-Type报头之外,其他的报头都是可选的。
1. HTTP1.1 中可用的特定状态代码。共分为5类:
a) 100-199
都是信息性的,标示客户应该采取的其他动作。
b) 200-299
表示请求成功。
c) 300-399
用于那些已经移走的文件,常常包括Location报头,指出新的地址。
d) 400-499
表明客户引发的错误。
e) 500-599
表示由服务器引发的错误。
2. 状态代码的指定:
a) 设置任意状态代码:setStatus(一个整数)建议使用HttpServletResponse中定义的常量。因为HTTP请求由状态行、一个或多个报头、一个空行、以及实际的文档按照此处开出的次序组成。所以要在用PrintWriter实际返回任何内容之前调用setStatus。
b) 设置302和404状态代码:sendRedirect和sendError
这两个方法都会抛出IOException异常,而setStatus不会。
状态代码302命令浏览器连接到新位置。sendRedirect方法生成302响应以及Location报头,给出新文档的URL。
状态代码404用于服务器没有找到文档的情况。sendError方法发送状态代码(一向为404)以及一小段简短的消息,这段消息被自动安排到HTML文档中发送给客户。
响应报头可以用来:指定cookie;提供页面的修改日期(用于客户端缓存),指示浏览器在指定的时间间隔后重新载入页面;给出文件的大小使持续性HTTP连接的应用成为可能;指定生成文档的类型以及执行许多其他任务。
1. 设置响应报头。
a) setHeader(String headerName, String headerValue)
这个方法将指定名称的响应报头设为给定的值。
b) setDateHeader(String header, long milliseconds)
这个方法省去将Java日期转换成GMD时间字符串的麻烦。
c) setIntHeader(String header, int headerValue)
这个方法可以省去在将整数插入到报头之前将int转换成String的不便。
HTTP允许相同的报头名多次出现,有时,我们希望加入新的报头,而非替换已有的同名报头。方法setHeader、setDateHeader、setIntHeader会替换任何同名的已有报头,而addHeader,addDateHeader和addIntHeader等方法可以添加一个报头。
d) setContentType(String mimeType)
这个方法设置Content-Type报头,大多数servlet都要用到这个方法。
e) setContentLength(int length)
这个方法设置Content-Length报头,如果浏览器支持持续性(继续使用)HTTP连接,这个报送十分有用。
f) addCookie(Cookie c)
这个方法向Set-Cookie报头插入一个cookie。由于响应中一般都会拥有多个Set-Cookie行,故而没有对应的setCookie方法。
g) sendRedirect(String address)
将状态代码设为302,同时设置