前言
我们在前面几章中学到,包含JavaScript(或者嵌入了其他技术,如Java小应用程序)的HTML文档可以改变文档的浏览器表示的内容和结构,来响应多种不同的事件、弹出式对话框窗口。HTML与JavaScript和DOM的结合有时被称为动态HTML(DynamicHTML,DHTML),包含脚本的HTML文档被称为动态(dynamic)文档。另一方面,不包含脚本的简单HTML文档则被称为静态(static)文档。
在本章中,我们将继续学习有关基于 Web的系统的编程知识。从涉及Web浏览器的客户端编程转向服务器端编程。而其中这章主要涉及的是Java servlet编程,它可以定制服务器来给Web用户提供某些服务。
servlet简介
正如前言中写到的,servlet是一个可用于在接收到HTTP请求时动态生成响应的许多技术之一(动态生成HTML的技术)。实质上讲,servlet 只是一个Java类,在启动服务器时由Web服务器对其进行实例化。当服务器接收到某些HTTP请求时,将在这个实例上调用特定的方法。
它的工作原理很简单,分为4步:
- 客户端发送HTTP请求到服务端;
- 服务器将请求或响应的对象发送给servlet程序(它是一个Java程序);
- servlet将修改后的响应对象发送回服务器;
- 服务器从动态信息中创建一个HTTP响应发送回客户端。
下面是一个非常简单的servlet例子,用于输出“Hello World",这里仅仅讲解代码部分,在Tomcat服务器实践部分请见:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
/**
* Hello World! servlet
*/
public class ServletHello extends HttpServlet
{
/**
* Respond to any HTTP GET request with an
* HTML Hello World! page.
* @param request
* @param response
* @throws javax.servlet.ServletException
* @throws java.io.IOException
*/
@Override
public void doGet (HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
// Set the HTTP content type in response header
response.setContentType("text/html; charset=\"UTF-8\"");
// Create the body of the response
try ( // Obtain a PrintWriter object for creating the body
// of the response
PrintWriter servletOut = response.getWriter()) {
// Create the body of the response
servletOut.println(
"<!DOCTYPE html \n" +
" PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \n" +
" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\"> \n" +
"<html xmlns='http://www.w3.org/1999/xhtml'> \n" +
" <head> \n" +
" <title> \n" +
" ServletHello.java \n" +
" </title> \n" +
" </head> \n" +
" <body> \n" +
" <p> \n" +
" Hello World! \n" +
" </p> \n" +
" </body> \n" +
"</html> ");
servletOut.close();
}
}
}
servlet程序在使用doGet()
方法的时候,必须遵循以下步骤:
public void doGet (HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException{}
为了响应GET请求,servlet类必须重写HttpServlet的doGet()
方法。servlet方法执行,通常调用服务器传递给它的HttpServletRequest(接受请求)和HttpServletResponse(接受响应)对象上的方法,并且可能会抛出ServletException和IOException异常。例如从请求中获取参数或者向响应中写入数据。response.setContentType("text/html; charset=\"UTF-8\"");
在这个例子中,响应的内容类型被设置为"text/html",并且字符集被设置为"UTF-8"。这意味着响应的内容将是HTML格式的,并且使用UTF-8字符集进行编码。这个方法应该在处理HTTP响应之前被调用,以确保响应的内容类型被正确设置。try (PrintWriter servletOut = response.getWriter())
使用try-with-resources语句创建了一个PrintWriter对象,它被初始化为response.getWriter()的返回值。PrintWriter对象可以用于向HTTP响应中写入文本数据。这个操作必须在setContentType()
设置好Content-type之后才能调用。servletOut.println()
把一个有效的HTML文档输出到PrintWriter对象servletOut
中。servletOut.close();
最后关闭PrintWriter对象。
servlet没有一个main方法,因为Tomcat服务器提供了main。作为替代,doGet()
或doPost()
充当servlet的一种main方法。servlet的主要输出通常是HTML,对于其他Java程序则不一定。
参数数据
导航到与一个servlet关联的URL可以被视作类似于在Java中调用一个方法。也就是说,当我们导航到该URL时,服务器就会为我们调用servlet的doGet0方法,并且实质上会返回一个(较长的HTML)字符串,表示该方法的“返回值”。对Java方法的实际调用可以包含URL参数值。例如:http://www.example.com/servlet/PrintThis?arg=aString
在URL内,问号(?)标志着URL的路径部分的结束和URL的查询部分的开始,并且URL的查询部分包含一个查询字符串(query string)。在这种情况下查询字符串包含一个名为arg的参数(parameter)它被赋予字符串值aString。
对于多个参数,可以通过用“与”符号(&)分隔相邻的参数名=值
对。例如,如下URL的查询字符串包含两个参数:http://www,example.com/servlet/PrintThis?arg=aString&color=red
其中参数的顺序无关紧要。
如果我们想给参数赋值一个空字符,我们可以直接在参数名后跟一个"="即可。例如:
http://www.example.com/servlet/PrintThis?arg=&color=red
http://www.example.com/servlet/PrintThis?color=red&arg=
如果参数值是一个非字母数字字符,那么需要将其转义后写为值。例如我们arg参数的参数值为" 'a String' ":http://www.examplecom/servlet/PrintThis?arg=%27a+String%27
转义的过程称为URL编码,转换回正常的字符串的形式称为URL解码。
在servlet中,有一些可以访问参数数据的HttpServletRequest方法如下:
会话(Sessions)
为了便于使用,许多Web站点被设计成通过一系列页面(而不是一个大页面)从站点访问者那里获得信息。例如,用户可能在一个页面上输入产品订购信息,在另一个页面上输入送货地址,并在第三个页面上输入信用卡信息。但是,如果web站点正在处理上百个用户的请求,服务器怎样知道哪些HTTP请求来自于哪些用户呢?
我们知道HTTP(超文本传输协议)是一种无状态协议,这意味着它不会在客户端和服务器之间维护任何有关先前请求或会话的信息。每个请求都被视为一个新请求,没有任何先前请求的知识。
在Web应用程序中,Session是一种服务器端技术(由服务器存储),用于跟踪用户在不同页面和请求之间的状态。Session允许服务器在用户访问网站时创建一个唯一的标识符(session ID),并将该标识符与用户的会话信息相关联。这个会话信息可以包括任何需要在不同页面之间共享的数据,例如用户的登录状态、购物车中的商品、用户的偏好设置等。
对于新用户如果请求不包含一个session ID,那么就假定请求来自一个新用户,并且Web服务器会生成一个新的唯一session ID与这个用户相关联。当Web服务器创建HTTP响应消息时,将作为响应的一部分包含会话ID一起返回。
客户端(浏览器)接收到session ID后,会存储到客户端中,在下次再次访问服务端的时候会作为HTTP请求的一部分将session ID发送给服务端。服务端将识别来自单个用户的所有HTTP请求,并和其他用户独立。如下图是服务端与两个客户端的session ID发送以及响应的过程:
每个客户第一次发送请求的时候,服务端都会返回一个session ID,在下次客户端请求时,客户端会同时发送session ID以便服务端识别不同客户响应不同的内容。
创建会话
服务器通过把一个HttpSession对象与服务器维护的每个会话相关联,来支持session。每个对象都会存储其会话的会话ID,还会存储其他与会话相关的信息。
当servlet在其HttpServletRequest 参数上调用getSession()
方法,它会有两种情况:
- 关联的HTTP请求不包含一个有效的session ID时服务器就会创建一个HttpSession对象,并分配一个session ID;
getSession()
方法会返回最近创建的对象的session ID。 - 如果HTTP请求包含一个有效的会话ID,那么调用
getSession()
将会返回以前创建的HttpSession对象,其中包含这个会话ID。
servlet可以通过在HttpSession对象上调用布尔方法isNew()
,来确定HttpSession返回的HttpSession对象是否是最新创建的。
我们可以通过HttpSession对象的getId()
方法获取session ID。
下面是一个统计访问者数量的servlet程序代码,统计访问者和统计访问数不一样,统计访问者是统计用户数,同一个用户的session ID相同,只有在不同的session ID访问时计数器才会加一。
public class VisitorCounter extends HttpServlet
{
// Number of visitors to the servlet since
// the program (web server) started
private int visits=0;
/**
* Respond to any HTTP GET request with an
* HTML Hello World! page with visitor counter.
* @param request
* @param response
* @throws javax.servlet.ServletException
* @throws java.io.IOException
*/
@Override
public void doGet (HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
// Set the HTTP content type in response header
response.setContentType("text/html; charset=\"UTF-8\"");
// Determine whether or not this is the first visit
// by this user, and update visit count if this is
// the first visit
try ( // Obtain a Pr/*i*/ntWriter object for creating the body
// of the response
PrintWriter servletOut = response.getWriter()) {
// Determine whether or not this is the first visit
// by this user, and update visit count if this is
// the first visit
HttpSession session = request.getSession();
if (session.isNew()) {
visits++;
}
// Create the body of the response, including visit count
servletOut.println(
"<!DOCTYPE html \n" +
" PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \n" +
" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\"> \n" +
"<html xmlns='http://www.w3.org/1999/xhtml'> \n" +
" <head> \n" +
" <title> \n" +
" VisitorCounter.java \n" +
" </title> \n" +
" </head> \n" +
" <body> \n" +
" <p> \n" +
" Hello World! \n" +
" </p> \n" +
" <p> \n" +
" This page has been viewed by \n" +
visits +
" visitors since the most recent server restart.\n And your session ID is " +
session.getId() +
" </p> \n" +
" </body> \n" +
"</html> ");
servletOut.close();
}
}
}
HttpSession session = request.getSession();
:使用HttpServletRequest
的getSession()
方法作为初始值,并创建了一个HttpSession对象session用来存储session。session.isNew()
用于检测是否这个session是新创建的,如果是新创建的话,则返回TRUE,这个时候统计一个用户。session.getId()
用于取得session ID。
当我们正确运行改servlet程序时,就可以看到如下页面:
此时无论我们怎么刷新,计数器依然是1,因为在同一个客户端中,你的session ID相同,只有新的session ID才会增加。
我们可以打开无痕浏览(隐私模式下,之前的session ID会清除,会视为一个新的客户端)或者是更换一个浏览器再次访问,这时就可以看到计数器加一了,且session ID更变。
Cookie
Cookie是 HTTP 响应的 Set-Cookie 标头字段中的名称/值对,它是由Web服务器通过用户使用的Web浏览器存储在用户计算机上的小文本文件。当用户访问同一网站时,该网站可以从用户的计算机读取该Cookie,以便在用户不断地浏览网站时跟踪用户的活动并执行其他功能。
Cookie和Session都是Web应用程序中用于跟踪用户状态的技术,但它们有一些区别和各自的用途。
- Cookie是在客户端(即用户的浏览器)上存储数据的一种机制。当Web服务器向客户端发送响应时,可以包含一个或多个Cookie,以便客户端在将来的请求中将这些Cookie发送回服务器。Cookie通常用于存储用户的个人偏好设置、登录状态、以及其他与用户相关的数据。由于Cookie是存储在客户端上的,因此它们对于跨多个页面的状态跟踪非常有用。
- Session则是在服务器端存储数据的一种机制。当用户在Web应用程序中进行交互时,Web服务器会创建一个会话(session),并为该会话分配一个唯一的标识符(session ID)。服务器可以将该session ID存储在Cookie中,以便在将来的请求中识别客户端。通过session ID,服务器可以在多个页面之间跟踪用户的状态,并存储与用户相关的数据。与Cookie不同,Session数据存储在服务器上,因此它们对于存储敏感信息(例如用户的身份验证信息)非常有用。
Web应用程序会同时使用Cookie和Session来跟踪用户状态。例如,当用户登录时,Web服务器可能会创建一个Session,将用户的身份验证信息存储在Session中,并将Session ID存储在Cookie中(cookie还包含其他信息)。在将来的请求中,客户端将Cookie发送回服务器,服务器可以使用Session ID来查找该用户的Session,并恢复该用户的身份验证信息。
在servlet中,我们可以通过request.getCookies()
方法取得客户端请求里包含的cookie。在响应中可以使用response.addCookie(Cookie)
方法向响应中添加cookie。
除此之外还有如下类方法:
当然具体使用起来和session差不多。
Comments NOTHING