美文网首页
Servlet与JSP学习笔记(三) Servlet核心(下)

Servlet与JSP学习笔记(三) Servlet核心(下)

作者: Toconscience | 来源:发表于2017-04-11 23:38 被阅读28次

    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();
    

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

    resultresult

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

    相关文章

      网友评论

          本文标题:Servlet与JSP学习笔记(三) Servlet核心(下)

          本文链接:https://www.haomeiwen.com/subject/mvdpattx.html