3.3 隐式对象
3.3 隐式对象
概述
Servlet容器会传递几个对象给它运行的Servlet。例如,可以通过Servlet的service方法拿到HttpServletRequest和HttpServletResponse对象,以及可以通过init方法访问到ServletConfig对象。此外,可以通过调用HttpServletRequest对象的getSession方法拿到HttpSession对象。
JSP隐式对象
在JSP中,可以通过使用隐式对象来访问上述对象。JSP隐式对象如下表所示。
| 对象 | 类型 |
|---|---|
request |
javax.servlet.http.HttpServletRequest |
response |
javax.servlet.http.HttpServletResponse |
out |
javax.servlet.jsp.JspWriter |
session |
javax.servlet.http.HttpSession |
application |
javax.servlet.ServletContext |
config |
javax.servlet.ServletConfig |
pageContext |
javax.servlet.jsp.PageContext |
page |
javax.servlet.jsp.HttpJspPage |
exception |
java.lang.Throwable |
request隐式对象
该隐式对象代表Servlet/JSP容器传递给Servlet服务方法的HttpServletRequest对象。可以将request理解为一个指向HttpServletRequest对象的引用变量。下面的代码示例,从HttpServletRequest对象中返回userName参数值:
1 | <% |
pageContext隐式对象
获取Servlet相关对象
pageContext提供了有用的上下文信息,并且通过其方法可以访问各种Servlet相关对象,如getRequest、getResponse、getServletContext、getServletConfig和getSession。当然,这些方法在脚本中不是非常有用的,因为可以更直接地通过隐式对象request、response、session和application等来直接访问。
获取和设置属性
PageContext中提供了另一组有趣的方法:用于获取和设置属性的方法,也就是getAttribute方法和setAttribute方法。属性值可被存储在4个范围之一:页面、请求、会话和应用程序。
- 页面范围是最小范围,这里存储的属性只在同一个
JSP页面之中可用。 - 请求范围是指属性存储在当前的
ServletRequest里面。 - 会话范围指属性存储在当前的
HttpSession里面。 - 应用程序范围指属性存储在应用的
ServletContext里面。
setAttribute方法
PageContext的setAttribute方法签名如下:
1 | public abstract void setAttribute(java.lang.String name, |
其中,scope的取值范围为PageContext对象的中存储静态int值:PAGE_SCOPE、REQUEST_ SCOPE、SESSION_SCOPE和APPLICATION_SCOPE。
把属性存储到页面范围
若要保存一个属性到页面范围,可以直接使用setAttribute重载方法:
1 | public abstract void setAttribute(java.lang.String name, |
实例
如下脚本将一个属性保存到ServletRequest中:
1 | <% |
同样效果的Java代码如下:
1 | <% |
out隐式对象
隐式对象out引用了一个javax.servlet.jsp.JspWriter对象,这类似于你在调用HttpServletResponse的getWriter方法时得到的java.io.PrintWriter对象。可以通过调用它的print方法将消息发送到浏览器。例如:
1 | out.println("Welcome"); |
Demo
implicitObjects.jsp页面:
1 | <%@page import="java.util.Enumeration"%> |
浏览器显示效果如下:
在浏览器中具体看到的内容,取决于所使用的浏览器及其环境。
注意,在默认情况下,JSP编译器会将JSP页面的内容类型设为text/html。如果要使用不同的类型,则需要
通过调用response.setContentType()或者使用页面指令来设置内容类型。例如,下面就是将内容类型设置为text/json:
1 | response.setContentType("text/json"); |
3.2 注释
3.2 注释
JSP页面中的两种注释
在开发中为JSP页面添加注释是一个良好的习惯。JSP支持两种不同的注释格式:
(1)JSP注释。该注释记录了在JSP页面中做了什么。JSP注释不会发送到客户端.
(2)HTML/XHTML注释。这些注释将会发送到浏览器上.
JSP注释
JSP注释以“<%--”开始,以“--%>”结束。下面是一个例子:
1 | <%-- JSP注释 --%> |
注意 JSP注释不会被发送到浏览器端,也不可以嵌套。
HTML注释
HTML/XHTML注释语法如下:
1 | <!-- HTML注释 --> |
一个HTML/XHTML注释不会被容器处理,会原样发送给浏览器。HTML/XHTML注释的一个用途是用来确定JSP页面本身。
1 | <!-- 这个是 /jsp/store/displayProducts.jsp 的输出 --> |
尤其是在运行有多个JSP片段的应用时,会特别有用。开发人员可以根据HTML源代码中的HTML注释,找出是哪一个JSP页面或片段产生了这些相应的HTML片段。
3.1 JSP概述
3.1 JSP概述
JSP相较Servlet的优点
**JSP页面本质上是一个Servlet**。然而,用JSP页面开发比使用Servlet更容易,主要有两个原因。
- 首先,不必编译
JSP页面; - 其次,
JSP页面是一个以.jsp为扩展名的文本文件,可以使用任何文本编辑器来编写它们。
JSP在哪里运行
JSP页面在JSP容器中运行,一个Servlet容器通常也是JSP容器。例如,Tomcat就是一个Servlet/JSP容器。
在JSP第一次被请求时 容器要做什么
当一个JSP页面第一次被请求时,Servlet/JSP容器主要做以下两件事情:
(1)把JSP页面转换成JSP页面实现类,该实现类是一个实现javax.servlet.jsp.JspPage接口或其子接口javax.servlet.jsp.HttpJspPage的Java类。而javax.servlet.jsp.JspPage接口又是javax.servlet.Servlet的子接口,这使得**每一个JSP页面都是一个Servlet**。该实现类的类名由Servlet/JSP容器生成。如果出现转换错误,则相关错误信息将被发送到客户端。
(2)如果转换成功,Servlet/JSP容器随后编译该Servlet类,并装载和实例化该类,并像其他正常的Servlet一样执行生命周期操作。
在JSP页面的后续请求时 容器要做什么
对于同一个JSP页面的后续请求,Servlet/JSP容器会先检查JSP页面是否被修改过。
- 如果该
JSP页面被修改过,则该JSP页面会被重新转换为Servlet、然后编译并执行。 - 如果该
JSP页面没有被修改过,则执行已经在内存中的JSP Servlet。
这样一来,一个JSP页面的第一次调用的实际花费总比后续调用的花费多,因为第一次调用涉及转换和编译。
如何减少第一次调用JSP页面时的花费
为了解决这个问题,可以执行下列动作之一:
- 配置应用程序,使所有的
JSP页面在应用程序启动时被调用,也就是在引用程序启动时就转换和编译,而不是在第一次请求时被调用。 - 预编译
JSP页面,并将其部署为Servlet。
JSP自带的API包
JSP自带的API包含4个包,如下所示:
javax.servlet.jsp。这个包包含了用于Servlet/JSP容器将JSP页面翻译为Servlet的核心类和接口。其中的两个重要成员是JspPage和HttpJspPage接口。所有的JSP页面实现类必须实现JspPage或HttpJspPage接口。在HTTP环境下,实现HttpJspPage接口是显而易见的选择。javax.servlet.jsp.tagext。这个包中包含了用于开发自定义标签的类型。javax.el。这个包中提供了统一表达式语言的API。javax.servlet.jsp.el。这个包提供了一组必须由Servlet/JSP容器支持,以便在JSP页面中使用表达式语言的类。
除了javax.servlet.jsp.tagext,我们很少直接使用JSP API。事实上,编写JSP页面时,我们更关心Servlet API,而非JSP API。当然,我们还需要掌握JSP语法,本章后续会进一步说明。开发JSP容器或JSP编译器时,JSP API已被广泛使用。
可以在以下网址查看JSP API:
https://docs.oracle.com/javaee/7/api/index.html?javax/servlet/jsp/package-summary.html
模板数据和语法元素
JSP页面可以包含模板数据和语法元素。这里,语法元素是一些具有特殊意义的JSP转换符。例如,“<%”是一个元素,因为它表示在JSP页面中的Java代码块的开始。“%>”也是一个元素,因为它是Java代码块的结束符。除去语法元素外的一切是模板数据。模板数据会原样发送给浏览器。例如,**JSP页面中的HTML标记和文字都是模板数据**。
Demo
第一个JSP页面
下面是一个名为welcome.jsp的JSP页面。这个页面仅仅向用户发送一个Welcome。
1 | <html> |
在Tomcat中,welcome.jsp页面在第一次请求时被翻译成名为welcome_jsp的Servlet。你可以在Tomcat工作目录下的子目录中找到生成的Servlet,该Servlet继承
自org.apache.jasper. runtime.HttpJspBase抽象类,并实现了javax.servlet.jsp.HttpJspPage接口,org.apache.jasper. runtime.HttpJspBase继承于javax.servlet.http.HttpServlet类。
下面是为welcome.jsp生成的Servlet。
JSP页面对应的Servlet
JSP页面会被编译成Servlet,并发布到Tomcat安装目录下的\work\Catalina\localhost\项目名\包名\类名_jsp.java,我这里是D:\dev\apache-tomcat-8.5.35\work\Catalina\localhost\app03a\org\apache\jsp\welcome_jsp.java,打开这个文件,内容如下:
1 | /* |
JSP实现类的主体方法
正如我们在上面的代码中看到的,JSP页面的主体是_jspService方法,该方法中响应输出如下:
1 | public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response) |
这个_jspService方法被定义在HttpJspPage,并被HttpJspBase的service方法调用。HttpJspBase类中的service方法如下所示:
1 | public final void service(HttpServletRequest request, |
一个JSP页面不同于一个Servlet的另一个方面是,JSP页面不需要添加注解或在部署描述符中配置来映射URL。在应用程序目录中的每一个JSP页面可以直接在浏览器中输入路径页面访问。
app03a应用程序的结构非常简单,由一个空的WEB-INF目录和welcome.jsp页面构成,如下图所示:
可以通过如下URL访问welcome.jsp页面:
http://localhost:8080/app03a/welcome.jsp
说明
添加新的JSP界面后,无须重启Tomcat。
显示今日日期
todaysDate.jsp:
1 | <%@ page language="java" contentType="text/html; charset=UTF-8" |
上面的JSP页面最终编译成:E:\apache-tomcat-8.5.35\work\Catalina\localhost\app03a\org\apache\jsp\todaysDate_jsp.java
这个Servlet,查看这个Servlet,可以看到_jspService方法中的响应输出部分如下:
1 | public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response) |
现在可以通过如下URL访问todaysDate.jsp页面:
http://localhost:8080/app03a/todaysDate.jsp
显示效果如下:
todaysDate.jsp页面发送了几个HTML标签和字符串"当前日期: "以及今天的日期到浏览器。
请注意两件事情。首先,Java代码可以出现在JSP页面中的任何位置,并通过<%和%>包括起来,这个<%...%>块被称为scriplet。其次,可以使用page指令的import属性导入在JSP页面中使用的Java类型,如果没有导入的类型,必须在代码中写Java类的全路径名称。
第3章 Java Server Pages(JSP)概述
第3章 Java Server Pages(JSP)概述
Servlet的缺点
Servlet有两个缺点是无法克服的:
- 写在
Servlet中的所有HTML标签必须写成Java字符串的形式,这使得处理HTTP响应报文的工作变得十分烦琐; - 所有的文本和
HTML标记是硬编码,导致即使是表现层的微小变化,如改变背景颜色,也需要重新编译Servlet。
JSP
Java Server Pages(JSP)解决了上述两个问题。不过,JSP不会取代Servlet,相反,它们具有互补性。现代的Java Web应用会同时使用Servlet和JSP页面。
本章概述
本章介绍了JSP技术,并讨论了在JSP页面中,隐式对象的使用意见,3个语法元素(指令、脚本元素和动作),还讨论了错误处理。可以用标准语法或XML语法编写JSP。用XML语法编写的JSP页面被称为JSP文档。由于很少用XML语法编写JSP,故本章不做介绍。在本章中,我们将主要学习JSP标准语法。
2.4 Java Servlet 会话管理 通过HttpSession对象
2.4 Java Servlet 会话管理 通过HttpSession对象
在所有的会话跟踪技术中,HttpSession 对象是最强大和最通用的。一个用户可以有且最多有一个HttpSession,并且不会被其他用户访问到。
谁创建HttpSession对象
HttpSession对象在用户第一次访问网站的时候自动被创建。
如何获取HttpSession对象
你可以通过调用HttpServletRequest的getSession方法获取该对象。getSession有两个重载方法:
1 | HttpSession getSession() |
没有参数的getSession方法会返回当前的HttpSession,若当前没有,则创建一个并返回。getSession(false)返回当前HttpSession,如当前不存在,则返回null。getSession(true)返回当前HttpSession,若当前没有,则创建一个并返回,getSession(true)同getSession()的效果一致.
如何往HttpSession中存入数据
可以通过HttpSession的setAttribute方法将值放入HttpSession,该方法的方法签名如下:
1 | void setAttribute(java.lang.String name, java.lang.Object value) |
请注意,不同于URL重新、隐藏域或cookie,放入到HttpSession 的值,是存储在内存中的,因此,不要往HttpSession放入太多对象或放入大对象。
尽管现代的Servlet容器在内存不够用的时候会将保存在HttpSessions的对象转储到二级存储上,但这样有性能问题,因此小心存储。
HttpSession中的数据要满足的条件
此外,放到HttpSession的值不限于String类型,可以是任意实现java.io.Serializable的java对象,因为在内存不够用的时候,Servlet容器会将这些对象放入文件或数据库中,当然你也可以将不支持序列化的对象放入HttpSession,只是这样,当Servlet容器进行序列化的时候会失败并报错。
调用setAttribute方法时,若传入的name参数此前已经使用过,则会用新值覆盖旧值。
从HttpSession对象中取出数据
通过调用HttpSession的getAttribute方法可以取回之前放入HttpSession中的对象,该方法的签名如下:
1 | java.lang.Object getAttribute(java.lang.String name) |
从HttpSession对象中批量取出数据
HttpSession还有一个非常有用的方法,名为getAttributeNames,该方法会返回一个Enumeration对象来迭代访问保存在HttpSession中的所有值:
1 | java.util.Enumeration<java.lang.String> getAttributeNames() |
注意,所有保存在HttpSession的数据不会被发送到客户端,不同于其他会话管理技术,**Servlet容器为每个HttpSession生成唯一的标识,并将该标识发送给浏览器**,或创建一个名为JSESSIONID的cookie,或者在URL后附加一个名为jsessionid 的参数。在后续的请求中,浏览器会将标识提交给服务端,这样服务器就可以识别该请求是由哪个用户发起的。Servlet容器会自动选择一种方式传递会话标识,无须开发人员介入。
提示:
- 隐藏域技术会往浏览器发送带隐藏域的表单.
Cookie技术会往浏览器发送Cookie.
获取Http对象的标识
可以通过调用 HttpSession的getId方法来读取该标识:
1 | java.lang.String getId() |
此外,HttpSession.还定义了一个名为invalidate 的方法。该方法强制会话过期,并清空其保存的对象。默认情况下,HttpSession 会在用户不活动一段时间后自动过期,该时间可以通过部署描述符的 session-timeout元素配置,若设置为30,则会话对象会在用户最后一次访问30分钟后过期,如果部署描述符没有配置,则该值取决于Servlet容器的设定。
大部分情况下,你应该主动销毁无用的HttpSession,以便释放相应的内存。
查看设置超时时间
可以通过调用HttpSession 的getMaxInactiveInterval方法来查看会话多久会过期。该方法返回一个数字类型,单位为秒。调用setMaxInactiveInterval 方法来单独对某个HttpSession 设定其超时时间:
1 | void setMaxInactiveInterval(int seconds) |
若设置为0,则该HttpSession 永不过期。通常这不是一个好的设计,因为该 HttpSession 所占用的堆内存将永不释放,直到应用重加载或Servlet容器关闭。
Demo
一个简单的购物Demo,具有显示商品列表,商品详情页面,购物车页面.并且在商品详情页面提供添加到购物车的功能.
Product
商品类用来存储商品的信息,如下所示:
1 | package session.management.httpsession; |
购物车中光有商品而已是不行的,还要知道用户买了几个商品,商品和对应的数量构成了购物车中记录的数据结构。
ShoppingItem
1 | package session.management.httpsession; |
有了商品,有了记录,下面就来写功能代码了.
ShoppingCartServlet
1 | package session.management.httpsession; |
代码详解
创建并初始化商品列表
1 | // 商品列表 |
从商品列表中查找出一个商品
1 | // 根据商品ID,从商品列表中查找一个商品. |
匹配URL
1 | @WebServlet( |
doGet方法
1 | @Override |
显示产品列表
1 | private void sendProductList(HttpServletResponse response) |
显示商品详情
1 | // 显示商品详情 |
显示商品详情页,表单属性如下:
1 | <form method='post' action='addToCart'> |
当用户点击加入购物车时,会使用POST方法把表单发送给.\addToCart这个URL.
doPost方法
1 | @Override |
显示购物车
1 | // 显示购物车 |
2.3 Cookies
2.3 Cookies
URL重写和隐藏域仅适合保存无须跨越太多页面的信息。如果需要在多个页面间传递信息,则以上两种技术实现成本高昂,因为你不得不在每个页面都进行相应处理。幸运的是,Cookies技术可以帮助我们。Cookies是一个很少的信息片段,可自动地在浏览器和Web服务器间交互,因此cookies可存储在多个页面间传递的信息。Cookie作为HTTP header的一部分,其传输由HTTP协议控制。此外,你可以控制cookies的有效时间。浏览器通常支持每个网站高达20个cookies。Cookies的问题在于用户可以通过改变其浏览器设置来拒绝接受cookies。
要使用cookies,需要熟悉javax.servlet.http.Cookie 类以及HttpServletRequest和HttpServletResponse两个接口。
创建Cookies
可以通过传递name和value两个参数给Cookie 类的构造函数来创建一个cookies:
1 | Cookie cookie = new Cookie(name, value); |
如下是一个创建语言选择的cookie示例:
1 | Cookie languageSelectionCookie = new Cookie("language", "Italian"); |
Cookie属性
创建完一个Cookie对象后,你可以设置domain、 path和maxAge属性。其中,maxAge 属性决定cookie何时过期。
服务器如何发送Cookie
要将cookie发送到浏览器,需要调用 HttpServletResponse的add方法:
1 | httpServletResponse.addCookie(cookie); |
浏览器如何发送Cookie
浏览器在访问同一Web服务器时,会将之前收到的 cookie一并发送。 此外,Cookies也可以通过客户端的javascript脚本创建和删除。
服务器端如何读取浏览器提交的Cookie
服务端若要读取浏览器提交的cookie,可以通过 HttpServletRequest接口的getCookies方法,该方法返回一个Cookie数组,若没有Cookies则返回null.
查找Cookie
你需要遍历整个数组来查询某个特定名称的cookie。如下为查询名为maxRecords的cookie的示例:
1 | //获取浏览器提交的所有Cookie数组 |
目前,还没有类似于getCookieByName这样的方法来帮助简化工作。
删除Cookie
没有一个直接的方法来删除一个cookie,你只能创建一个同名的Cookie,并将maxAge属性设置为0,并添加到HttpServletResponse接口中,覆盖掉原来的Cookie即可。如下为删除一个名为userName的cookie代码:
1 | Cookie cookie = new Cookie("userName", ""); |
demo
工具类
1 | package session.management.cookies; |
设置Cookie的Servlet
1 | package session.management.cookies; |
这个Servlet会把表单输入转换为Cookie发送到浏览器,对应的URL为http://localhost:8080/SessionManagement/setcookie,
当浏览器再次访问http://localhost:8080/SessionManagement/这个Java项目下的资源时,浏览器会把之前服务器发给他的Cookie,再原封不动的提交给服务器。这样服务器就可以根据Cookie来进行响应的操作。
在上面代码中,服务器分别发送fruitNum和cookieFontSize这两个cookie给浏览器,下面将再创建两个Servlet来使用者连个Cookie。
填写表单,如下所示:
然后点击Set按钮,提交表单,得到提示如下:
根据Cookie显示数组中的元素
ShowFruitsByCookie这个Servlet会根据fruitNum这个Cookie的值,来显示打印数组中的元素,如下所示:
1 | package session.management.cookies; |
点击工具条上的Show Fruits 超链接,效果如下:
根据Cookie设置CSS
SetCSSByCookie这个Servlet根据cookieFontSize这个Cookie中的值来来设置元素的CSS样式,如下所示:
1 | package session.management.cookies; |
点击工具条上的Show CSS超链接,显示效果如下:
2.1 URL重写
URL重写
URL重写是一种会话跟踪技术,它将一个或多个键值对 添加到URL的查询字符串中,每个键值对 通常为 key=value形式,格式如下所示:
1 | url?key_1=value_1&key_2=value_2...&key_n=value_n |
注意,URL和键值对之间用问号?来分割,键值对之间用单与符号&分割。URL重写适合于在少数URL之间传递信息的情况下,它有如下限制:
URL在某些浏览器上最大长度为2000字符,所以URL重写可能无法传递大量数据。- 如果要传递
信息到下一个资源,需要将信息插入到URL的查询字符串中,这种情况适用于动态页面之间传值,而不适用于静态页面之间传值; URL重写需要在服务端上完成,所有的链接都必须带值,因此当一个页面存在很多链接时,其处理过程会是一个不小的挑战;- 对应信息中的某些字符,例如
空格、单与符号&和问号?等必须用base64编码,而编码和解码会增加开销; URL的内容会显示在浏览器的地址栏上,所以**URL重写不适合用来传递敏感信息**。
所以,**URL重写仅适合于在少量页面间 传递不敏感信息的情况。**
下面来写个Demo,使用URL重写在同一个页面中传递信息。
Demo
项目目录
创件一个名为SessionManagement的动态Java Web项目,然后创建项目结果如下所示:
完整代码
1 | package session.management; |
代码详解
URL映射
Servlet中的URL映射的注解如下所示:
1 | //配置Servlet映射到URL |
name表示当前session.management.RewriteURL这个Servlet的部署名。urlPatterns表示该Servlet的URL地址,这个是个数字形式,同一个Servlet可以设置不同的URL地址.
映射好URL后,后面就可以通过http://localhost:8080/项目名称/test这样的格式来访问这个Servlet,这里是:
http://localhost:8080/SessionManagement/test
初始化数据
1 | private ArrayList<String> fruits = null; |
在这个Servlet中有两个成员变量fruits和vegetables用于存放要展示的数据。
当用户第一次通过URL访问到该Servelet时,Web容器(Tomcat)会调用init()方法来初始化数据。在这里,init()方法用来初始化成员变量的数据。
响应get请求 覆盖doGet方法
当直接通过http://localhost:8080/SessionManagement/test这个URL访问的时候,Tomcat会调用init()方法来初始化,初始化只进行一次.后面不再调用init()方法.然后调用doGet()方法来响应请求。doGet方法如下:
1 | @Override |
doGet方法会获取请求中名称为choice的参数值。然后根据参数值的不同来调用不同的方法进行响应。
当我们请求http://localhost:8080/SessionManagement/test对应的Servlet时,URL中没有带查询字符串,所以不存在choice这个参数。request.getParameter("choice");返回null.这样就会调用showIndex(response);这个方法来响应。
返回初始界面
showIndex()方法如下,所谓的响应,就是往响应对象里写一些字符串。Web容器会把响应对象中的这些字符串处理一下然后发送给浏览器。
1 | private void showIndex(HttpServletResponse response) |
此时浏览器显示如下:
这个页面有两个超链接,如下所示:
1 | <a href="?choice=fruits">显示水果</a> |
响应重写的URL
在刚才的页面中,显示水果的URL为?choice=fruits这是个查询字符串,查询字符串中有一个名为choice的参数,参数值为fruits。
如果点击这超链接,浏览器会再次发送一个HTTP请求给服务器。
这里URL中地址缺省,浏览器会默认发给当前的地址。也就是在http://localhost:8080/SessionManagement/test这个地址后面加上查询字符串,得到的URL为:http://localhost:8080/SessionManagement/test?choice=fruits,这相当于重写了URL.
虽然,这个时候请求的Servlet还是http://localhost:8080/SessionManagement/test这个URL对应的Servlet.但是这回跟第一次请求时的情况是不一样的,第一次没有带请求参数,这次带了请求参数。Tomcat还是会调用doGet来生成响应。
不过这次因为带了参数choice=fruits,doGet方法会调用showFruits方法来生成响应。
1 | private void showFruits(HttpServletResponse response) |
这个方法会把水果列表中的数据输出。如下所示:
同理如果此时点击显示蔬菜链接我们就可以显示蔬菜。
第2章 会话管理
2.2 隐藏域
2.2 隐藏域
使用隐藏域来保持状态类似于URL重写技术,但不是将值附加到URL上,而是放到HTML表单的隐藏域中。当表单提交时,隐藏域的值也同时提交到服务器端。隐藏域技术仅当网页有表单时有效。该技术相对于 URL重写的优势在于:没有字符数限制,同时无须额外的编码。但该技术同URL重写一样,不适合跨越多个界面。
清单2.3展示了如何通过隐藏域来更新客户信息。清单2.2的Customer类为客户对象模型。
清单2.2 Customer类
1 | package app02a.hiddenfields; |
清单2.3 CustomerServlet类
1 | package app02a.hiddenfields; |
CustomerServlet类继承自HttpServlet,其URL映射分别为/customer、/editCustomer和 /updateCustomer。前两个URL会调用Servlet的doGet方法, 而/updateCustomer 会调用doPost方法。/customer是本例的入口URL。该URL会列举出在 init 方法中所初始化的类级别的列表对象customers(在 真实应用中,通常是从数据库中获取用户信息),如图下图所示。
如图上图所示,每个客户信息后都有一个edit链接, 每个edit链接的href属性为 /editCustomer?id=customerId。当通过/editCustomer访问servlet时, servlet会返回一个编辑表单,如下图所示。
此时,servlet返回的表单如下:
1 | <form method="post" action="updateCustomer"> |
可以看到表达中的隐藏域为<input type="hidden" name="id" value="1">.
该隐藏域保存了所编辑的客户id,因为隐藏域包含在表单中,所以会随着表单一起提交给服务器,这样服务端就知道应更新哪个客户信息。
需要强调的是,表单是通过post方式提交的,因此调用的是servlet的doPost方法。