Servlet核心接口

首先,Servlet API提供了一个抽象类GenericServlet, 它提供了一种Servlet的通用实现,与具体的网络应用层协议无关。也就是说,不必须是HTTP。而其子类HttpServlet类才是我们研究的重点。正常情况下,我们的Servlet都继承自这个类。

HTTP协议把客户请求分为了GET、POST、PUT、DELETE等方式。HttpServlet为每种请求方式都提供了相应的服务方法,如doGet()、doPost()、doPut()、doDelete()。而服务入口方法service()则基本干一件事:根据请求方式调用对应的doxxx()方法。

一般情况下,Servlet最多支持GET和POST。可以只在一个服务方法里处理请求,然后别的服务方法调用这个服务方法就可以了。

HttpServletRequest和HttpServletResponse

所有的服务方法都接受这两个对象作为参数。很明显,一个代表请求输入,一个代表返回输出。通过操作这两个对象就可以完成Servlet的基本功能。下面通过一个列表来展示这两个类互相对应的常见方法:

HttpServletRequest方法 操作对象 HttpServletResponse方法
getContentType() Body的MIME类型 getContentType() setContentType()
getCharacterEncoding() Body的字符编码 setCharacterEncoding()
getCookies() Cookie addCookie()
getHeader() 请求/响应头 setHeader()

下面是HttpServletRequest的一些特殊方法:

方法 描述
getRequestURI() 返回请求的URL
getMethod() 返回请求的 HTTP 方法的名称
getSession() 返回与该请求关联的当前 session 会话
getParameter() 以字符串形式返回请求参数的值
getAttribute() setAttribute() removeAttribute() 操作已命名属性

下面是HttpServletResponse的一些特殊方法:

方法 描述
setStatus() 设置HTTP响应的状态代码
sendError() 设置特定错误的HTTP响应代码
getOutputStream() 返回一个ServletOutputStream对象,用来输出二进制的正文数据。
getWriter() 返回一个PrintWriter对象,用来输出字符串形式的正文数据。
flushBuffer() 把缓冲区的正文数据发送给客户端。
resetBuffer() 清空缓冲区中的正文数据。
reset() 清空缓冲区以及响应状态代码和响应头数据。

Servlet API高级主题

读写Cookie

Cookie的运行机制是由HTTP协议规定的,由服务器和客户端(浏览器)共同遵守。下面是一个典型的设置Cookie的HTTP返回头:

HTTP/1.1 200 OK
Date: Fri, 04 Feb 2000 21:03:38 GMT
Server: Apache/1.3.9 (UNIX) PHP/4.0b3
Set-Cookie: username=Tom; expires=Friday, 04-Feb-07 22:03:38 GMT; 
                 path=/; domain=w3cschool.cc
...

可以发现,Cookie包含以下几种内容:

  • 一个名称-值对

  • 过期时间

  • 有效domain(域)和path(路径)

如果浏览器被设置了Cookie,它将会保留此信息直到过期时间。当浏览器再次访问该Cookie匹配的路径和域的页面时,它会就会把Cookie的内容也包含到发送到服务器的请求中。

Servlet可以对Cookie做的操作就一目了然了:

  • 可以调request.getCookies()和response.addCookie()

  • 通过Cookie.setMaxAge()设置过期时间。大于0表示存到硬盘直到过期;等于0删除Cookie;小于0表示关闭浏览器时删除Cookie.

  • 可以调setDomain()和setPath()设置Cookie作用范围。默认情况下只对同一个WebApp生效。

管理Session

可以参考菜鸟教程

HTTP是无状态的协议。客户端请求网页到收到响应后,TCP连接就会被关闭,服务器不会保留这个请求的任何记录。但是显然保存客户数据是很有必要的,典型如购物网站的购物车。
在Web开发领域,会话机制是用于跟踪用户状态的普遍解决方案。会话指的是在一段时间内,单个客户端与Web应用的一连串交互过程。在一个会话中,客户可能会多次访问Web应用的同一个网页,也可能访问Web应用的多个网页。

在会话(Session)被引入之前,有以下三种小聪明的方法用来“模拟会话的功能”:

  1. 服务器可以分配一个唯一的Session ID作为每个客户端的cookie,对于客户端的后续请求可以使用接收到的cookie来识别。

  2. 服务器可以发送一个隐藏的HTML表单字段,其值是Session ID. 这样在收到表单提交请求时可以获得Sessio ID.

  3. 在每个URL末尾追加一些额外的数据来标识Session,如w3cschool.cc/file.htm;sessionid=12345

终于Session唱着“不用麻烦了,不用麻烦了” 出场了。

可以调用 HttpSession session = request.getSession(); 获得Session.
调用getAttribute(), setAttribute(), removeAttribute(), getAttributeNames()来利用Session存取对象。
调用invalidate()销毁会话;setMaxInactiveInterval()设置会话不活动时间。

其实,Session实际上默认也是通过Cookie记Session ID来实现的。如果客户端禁用了Cookie,则要通过重写URL来解决。使用response.encodeURL().

最后,可以用Servlet API定义的HttpSessionLisener接口来统计在线用户人数。该接口定义了sessionCreated(), sessionDestroyed() 两个方法,顾名思义。

文件上传

可以参考 菜鸟教程

使用如下的form表单,点击上传时客户端发送的HTTP请求正文将是“multipart/form-data”类型,它表示复杂的包括多个子部分的复合表单。

<form method="post" action="/TomcatTest/UploadServlet" enctype="multipart/form-data">
    选择一个文件:
    <input type="file" name="uploadFile" />
    <br/><br/>
    <input type="submit" value="上传" />
</form>

Apache提供了commons-fileupload包来帮助处理文件上传。需要引入这个包及其依赖包commons-io包。
核心代码如下:

    protected void doPost(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException {

        // 配置上传参数
        DiskFileItemFactory factory = new DiskFileItemFactory();
        // 设置内存临界值 - 超过后将产生临时文件并存储于临时目录中
        factory.setSizeThreshold(MEMORY_THRESHOLD);
        // 设置临时存储目录
        factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
 
        ServletFileUpload upload = new ServletFileUpload(factory);         
        // 设置最大文件上传值
        upload.setFileSizeMax(MAX_FILE_SIZE);        
        // 设置最大请求值 (包含文件和表单数据)
        upload.setSizeMax(MAX_REQUEST_SIZE);
 
        // 构造临时路径来存储上传的文件
        // 这个路径相对当前应用的目录
        String uploadPath = getServletContext().getRealPath("./") + File.separator + UPLOAD_DIRECTORY;
                
        // 如果目录不存在则创建
        File uploadDir = new File(uploadPath);
        if (!uploadDir.exists()) {
            uploadDir.mkdir();
        }
 
        try {
            // 解析请求的内容提取文件数据
            @SuppressWarnings("unchecked")
            List<FileItem> formItems = upload.parseRequest(request);
 
            if (formItems != null && formItems.size() > 0) {
                // 迭代表单数据
                for (FileItem item : formItems) {
                    // 处理不在表单中的字段
                    if (!item.isFormField()) {
                        String fileName = new File(item.getName()).getName();
                        String filePath = uploadPath + File.separator + fileName;
                        File storeFile = new File(filePath);
                        // 在控制台输出文件的上传路径
                        System.out.println(filePath);
                        // 保存文件到硬盘
                        item.write(storeFile);
                        request.setAttribute("message",
                            "文件上传成功!");
                    }
                }
            }
        } catch (Exception ex) {
            request.setAttribute("message",
                    "错误信息: " + ex.getMessage());
        }
        ...
    }

转发、包含和重定向

Web应用在响应客户端的一个请求时,有可能响应过程很复杂,需要多个Web组件共同协作,才能生成响应结果。

Servlet规范为servlet之间的协作提供了两种途径:

  • 请求转发:Servlet源组件先对客户请求做一些预处理操作,然后把请求转发给其他Web组件。调用getRequestDispatcher().forward()方法。

  • 包含: Servlet源组件把其他Web组件生成的响应结果包含到自身的响应结果中。调用getRequestDispatcher().include()方法。

其中:源组件和目标组件共享同一个HttpServletRequest对象和HttpServletResponse对象;目标组件可以是Serlet、JSP或者HTML文档。

调用

PrintWriter out = response.getWriter();
out.println("这些内容不会出现在响应中。");
RequestDispatcher dispatcher = context.getRequestDispatcher("/output");
dispatcher.forward(request, response);

时, 会清空response中的正文数据缓冲区,然后调用目标组件的service()方法,把响应的结果返回客户端。因此源组件的响应结果不会被发送到客户端。

而如果调用下面代码:

PrintWriter out = response.getWriter();
out.println("<html><head><title>MainServlet</title></head>");
out.println("<body>");

RequestDispatcher headerDispatcher = context.getRequestDispatcher("/header.html");
RequestDispatcher greetDispatcher = context.getRequestDispatcher("/greet");
RequestDispatcher footerDispatcher = context.getRequestDispatcher("/footer.html");

// 包含header.html
headerDispatcher.forward(request, response);
// 包含GreetServlet输出的HTML片段
greetDispatcher.forward(request, response);
// 包含footer.html
footerDispatcher.forward(request, response);

out.println("</body></html>");
out.close();

程序就会把三种目标组件的输出全部包含到最终输出中来。结果如下:

result

重定向就是告诉客户端老娘不伺候了,去找那个小碧池吧!调用response.sendRedirect()即可。这时候客户端浏览器就会乖乖地去找另一个网页。


Toconscience
153 声望39 粉丝

引用和评论

0 条评论