Servlet中的HttpRequest、HttpResponse、Cookie、Session对象

本文最后更新于:2 小时前

Request对象

常用方法

package cn.edu.neusoft;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 获取请求
 */
@WebServlet("/ServletRequest")
public class Servlet01 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        /*常用方法*/
        String url = request.getRequestURL() + "";
        System.out.println("获取请求时的完整路径: " + url);

        String uri = request.getRequestURI();
        System.out.println("获取请求时的部分路径: " + uri);

        String queryString = request.getQueryString();
        System.out.println("获取请求时的参数字符串: " + queryString);

        String method = request.getMethod();
        System.out.println("获取请求时的请求方式: " + method);

        String protocol = request.getProtocol();
        System.out.println("获取当前协议版本: " + protocol);

        String webapp = request.getContextPath();
        System.out.println("获取请求时的项目站点名(项目对外访问路径): " + webapp);

        /**
         * getParameter(name) 获取指定名称的参数
         * getParameterValues(name) 获取指定名称参数的所有值,用于复选框
         */

        String uname = request.getParameter("uname");
        String upwd = request.getParameter("upwd");
        System.out.println("uname = " + uname);
        System.out.println("upwd = " + upwd);

        String[] languages = request.getParameterValues("languages");
        if ( languages != null && languages.length > 1 ) {
            for ( String language : languages ) {
                System.out.println("language: " + language);
            }
        }

    }
}

在浏览器传入参数后

http://localhost:8080/ServletRequest/ServletRequest?uname=admin&upwd=123&languages=Java&languages=JavaScript&languages=Rust

在IDEA中打印如下

image-20201107175442566

请求乱码问题

​ 乱码原因:在解析过程中默认使用的编码方式为 ISO-8859-1 这种不支持中文的编码方式,所以解析时一定会出现乱码

Tomcat8及以上版本 Tomcat7及以下版本
GET请求 不会乱码 乱码
POST请求 乱码 乱码

乱码情况:POST请求不论什么版本的服务器,中文都会乱码

解决方式:

request.setCharacterEncoding("UTF-8");//这种方式只针对POST请求才有效

针对Tomcat7及以下版本的GET请求乱码解决方式:

用的很少,仅做了解

​ 这种方式针对任意请求的乱码都有效,但设计编码格式转换的问题,如果本身不乱码,使用这种方式转换则会出现另外的乱码问题

String name = new String(request.getParameter("uname").getBytes("ISO-8859-1"), "UTF-8");
package cn.edu.neusoft;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 请求乱码问题
 */
@WebServlet("/Servlet02")
public class Servlet02 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");//这种方式只针对POST请求才有效
        //获取客户端传递的参数
        String uname = request.getParameter("uname");
        String upwd = request.getParameter("upwd");
        System.out.println("uname: " + uname);
        System.out.println("upwd: " + upwd);

        //解决Tomcat7及以下版本的GET请求乱码
        String name = new String(request.getParameter("uname").getBytes("ISO-8859-1"), "utf-8");
        System.out.println("name: " + name);
    }
}

请求转发

​ 可以让请求从服务端跳转到客户端,或者跳转到指定Servlet

request.getRequestDispatcher("url").forward(request, response);

特点:

  • 服务端行为
  • 地址栏不发生改变
  • 从始至终只有一个请求
  • request数据可以共享
package cn.edu.neusoft;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/s03")
public class Servlet03 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //接收客户端参数
        String uname = request.getParameter("uname");
        System.out.println("Servlet03 uname: " + uname);

        //请求转发跳转到Servlet04
//        request.getRequestDispatcher("s04").forward(request, response);
        //请求转发跳转到jsp页面
//        request.getRequestDispatcher("login.jsp").forward(request, response);
        //请求转发跳转到html页面
        request.getRequestDispatcher("login.html").forward(request, response);
    }
}

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h2>Hello</h2>
</body>
</html>

作用域

​ 通过该对象可以在一个请求中传递数据,作用范围:在一次请求中有效,服务器跳转有效(请求转发跳转时有效)

Servlet05

package cn.edu.neusoft;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * request作用域
 */
@WebServlet("/s05")
public class Servlet05 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("Servlet05");

        //设置域对象
        request.setAttribute("name", "admin");
        request.setAttribute("age", 18);

        List<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        request.setAttribute("list", list);

        //请求转发跳转到Servlet06
//        request.getRequestDispatcher("s06").forward(request, response);
        //请求转发跳转到jsp,并通过域对象传输数据
        request.getRequestDispatcher("index.jsp").forward(request, response);

    }
}

Servlet06

package cn.edu.neusoft;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;


@WebServlet("/s06")
public class Servlet06 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("Servlet06");

        //获取域对象内容
        String name = (String) request.getAttribute("name");
        System.out.println("name: " + name);

        Integer age = (Integer) request.getAttribute("age");
        System.out.println("age: " + age);

        List<String> list = (List<String>) request.getAttribute("list");
        for ( String str : list ) {
            System.out.println(str);
        }


    }
}

index.jsp

<%@ page import="java.util.List" %><%--
  Created by IntelliJ IDEA.
  User: QiuQian
  Date: 2020/11/7
  Time: 17:19
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  <h2>index页面</h2>
<%--  如果要在jsp中写Java代码,需要卸载脚本段中--%>
  <%
    //获取对象
    //获取域对象内容
    String name = (String) request.getAttribute("name");
    System.out.println("name: " + name);

    Integer age = (Integer) request.getAttribute("age");
    System.out.println("age: " + age);

    List<String> list = (List<String>) request.getAttribute("list");
    for ( String str : list ) {
      System.out.println(str);
    }
  %>
  </body>
</html>

Response对象

​ HttpServletResponse的主要功能用于服务器对客户端的请求进行响应,将Web服务器处理后的结果返回给客户端。service()方法中的形参接收的是HttpServletResponse接口的实例化对象,这个对象中封装了向客户端发送数据、发送响应头、发送响应状态码的方法。

响应数据

字符流/字节流只能使用其中一种,如果同时使用会报错

package cn.edu.neusoft;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 响应数据
 * 字符流/字节流只能使用其中一种,如果同时使用会报错
 */
@WebServlet("/s01")
public class Servlet01 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取字符输出流
//        PrintWriter printWriter = response.getWriter();
        //输出数据
//        printWriter.write("Hello");

        //得到字节输出流
        ServletOutputStream servletOutputStream = response.getOutputStream();
        //输出数据
        servletOutputStream.write("hi".getBytes());
    }
}

响应乱码问题

​ 在响应中,如果响应的内容含有中文,有可能出现乱码。因为服务器响应的数据也会经过网络传输,服务器端有一种编码方式,客户端也存在一种编码方式,当两端使用的编码方式不同,则会出现乱码。

字符流乱码

​ 对于getWrite()获取到的字符流,响应中文一定会出现乱码,由于服务器端在进行编码时会默认使用 ISO-8859-1 格式的编码,该编码方式不支持中文。

​ 要解决这种乱码,只能在服务器端告知服务器使用一种能够支持中文的编码格式,比如常用的”UTF-8”

response.setCharacterEncoding("UTF-8");

​ 此时还只完成已一半的工作,要保证数据正确显示,还需要指定客户端的解码方式

response.setHeader("content-type", "text/html;charset=UTF-8");

​ 两端指定编码后,乱码就解决了。

​ 总结:保证发送端和接收端的编码保持一致!

Servlet02

package cn.edu.neusoft;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 字符流响应数据
 */
@WebServlet("/s02")
public class Servlet02 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置服务端的编码格式
        response.setCharacterEncoding("UTF-8");
        //设置客户端的解码格式和响应的MIME类型
        response.setHeader("content-type", "text/html;charset=UTF-8");

        //获取字符输出流
        PrintWriter printWriter = response.getWriter();
        //输出数据
        printWriter.write("<h2>秋千</h2>");
    }
}

Servlet03

package cn.edu.neusoft;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 字节流响应数据
 */
@WebServlet("/s03")
public class Servlet03 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //同时设置客户端和服务端的编码格式
        response.setContentType("text/html;charset=UTF-8");

        //得到字节输出流
        ServletOutputStream servletOutputStream = response.getOutputStream();
        //输出数据
        servletOutputStream.write("<h2>你好</h2>".getBytes("UTF-8"));
    }
}

还有一种方式可以同时设置客户端和服务端的编码格式

response.setContentType("text/html;charset=UTF-8");

image-20201108112243427

字节流乱码

​ 对于getOutputStream()方式获取到的字节流,响应中文时,由于本身就是传输的字节,所以此时可能会出现乱码,也可能显示正确。当服务端给的字节恰好和客户端使用的编码格式一致时,文本显示正确,否则出现乱码。无论如果,我们都应该准确掌握服务器和客户端使用的是哪种编码格式,以确保数据显示正确

​ 同时设置客户端和服务端的编码格式

​ 不管服务器和客户端使用的是什么编码格式,全都自己指定!我命由我不由服务器和客户端!

response.setContentType("text/html;charset=UTF-8");

image-20201108113535299

重定向

​ 重定向是一种服务器指导客户端的行为。客户端发出第一个请求,被服务器接受处理后,服务器会进行响应,在响应的同时,服务器会给客户端一个新的地址(下次请求的地址 response.sendRedirect(url); ),当客户端接收到响应后,会立刻自动根据服务器给的新地址发起第二个请求,服务器接收请求并作出响应,重定向完成。

​ 从描述中可以看出重定向中有两个请求存在,并且属于客户端行为

///重定向跳转到index.jsp
response.sendRedirect("index.jsp");

​ 通过观察浏览器我们发现第一次请求获得的响应码为302,并且含有一个location头信息。并且地址栏最终看到的地址是和第一次请求地址不同的,地址栏已经发生了变化。

image-20201108115256258

Servlet04

package cn.edu.neusoft;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 重定向
 */
@WebServlet("/s04")
public class Servlet04 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("servlet04...");

        //重定向跳转到Servlet05
        response.sendRedirect("s05");

        //接收参数
        String uname = request.getParameter("uname");
        System.out.println("S04_uname: " + uname);
        
    }
}

Servlet05

package cn.edu.neusoft;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 重定向
 */
@WebServlet("/s05")
public class Servlet05 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("servlet05...");

        //接收参数
        String uname = request.getParameter("uname");
        System.out.println("S05_uname: " + uname);
    }
}

在浏览器传入参数

http://localhost:8080/Test2/s04?uname=admin

控制台打印结果

这个因为重定向存在两次请求!在Servlet04中,request对象是一个新的;Servlet05中也是一个新的

总结:重定向也是一种简单的跳转方式,只是这种跳转方式相对于请求转发而言,地址栏会发生变化,而且会有两次请求,数据无法共享

请求转发与重定向的区别

  • 请求转发的地址栏不会发生改变,重定向的地址栏会发生改变
  • 请求转发只有一次请求,重定向有两次
  • 请求转发时request对象可共享,重定向是request对象不共享
  • 请求转发是服务端行为,重定向是客户端行为
  • 请求转发时的地址只能是当前站点下的资源,重定向可以是任何地址

Cookie对象

​ Cookie是浏览器提供的一种技术,通过服务器的程序能将一些只须保存在客户端,或者在客户端进行处理的数据,放在本地的计算机上,不需要通过网络传输,因而提高网页处理的效率,并且能够减少服务器的负载,但是由于Cookie是服务器端保存在客户端的信息,所以其安全性也是很差的。例如常见的记住密码则可以通过Cookie来实现。

​ 有一个专门操作Cookie的类javax.servlet.http.Cookie。随着服务器端的响应发送给客户端,保存在浏览器。当下次再访问服务器时把Cookie再带回服务器。Cookie的格式:键值对用“=”链接,多个键值对间通过” ; “隔开。

Cookie的创建和发送

package com.xxx.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * Cookie的创建和发送
 */
@WebServlet("/coo01")
public class Cookie01 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse resposne) throws ServletException, IOException {
        //Cookie的创建
        Cookie cookie = new Cookie("name", "admin");
        //响应Cookie
        resposne.addCookie(cookie);

    }
}

image-20201115112740846

Cookie获取

package com.xxx.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * Cookie的获取
 *  返回的是Cookie数组
 */
@WebServlet("/coo02")
public class Cookie02 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse resposne) throws ServletException, IOException {
        //获取Cookie数组
        Cookie[] cookies = request.getCookies();
        //判断Cookie是否为空
        if ( cookies != null && cookies.length > 0 ) {
            for ( Cookie cookie : cookies ) {
                String name = cookie.getName();
                String value = cookie.getValue();
                System.out.println("名称: " + name + ", 值: " + value);
            }
        }

    }
}

image-20201115113453079

Cookie设置到期时间

​ 除了Cookie 的名称和内容外,我们还需要关心一个信息,到期时间,到期时间用来指定该cookie何时失效。默认为当前浏览器关闭即失效。我们可以手动设定cookie的有效时间(通过到期时间计算),通过setMaxAge(int time);方法设定cookie的最大有效时间,以秒为单位。

到期时间的取值

  • 负整数
    若为负数,表示不存储该cookie。
    cookie的 maxAge属性的默认值就是-1,表示只在浏览器内存中存活,一旦关闭浏览器窗口,那么cookie就会消失
  • 正整数
    若大于0的整数,表示存储的秒数。
    表示cookie对象可存活指定的秒数。当生命大于0时,浏览器会把Cookie保存到硬盘上,就算关闭浏览器,就算重启客户端电脑,cookie 也会存活相应的时间。

  • 若为0,表示删除该cookie。
    cookie生命等于0是一个特殊的值,它表示cookie 被作废!也就是说,如果原来浏览器已经保存了这个Cookie,那么可以通过Cookie的setMaxAge(0)来删除这个Cookie。无论是在浏览器内存中,还是在客户端硬盘上都会删除这个Cookie。
package com.xxx.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * Cookie的到期时间
 */
@WebServlet("/coo03")
public class Cookie03 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse resposne) throws ServletException, IOException {

        //创建cookie
        Cookie cookie = new Cookie("uname", "zhangsan");
        //设置存活时间
        cookie.setMaxAge(-1);//关闭浏览器失效
        //响应cookie
        resposne.addCookie(cookie);

        //创建cookie
        Cookie cookie2 = new Cookie("uname2", "lisi");
        //设置存活时间
        cookie2.setMaxAge(30);//存活30秒
        //响应cookie
        resposne.addCookie(cookie2);

        //创建cookie
        Cookie cookie3 = new Cookie("uname3", "wangwu");
        //设置存活时间
        cookie3.setMaxAge(0);//删除cookie
        //响应cookie
        resposne.addCookie(cookie3);
    }
}

image-20201115115506871

Cookie的注意事项

  • Cookie保存在当前浏览器中。
    在一般的站点中常常有记住用户名这样一个操作,该操作只是将信息保存在本机上,换电脑以后这些信息就无效了。而且cookie还不能跨浏览器。

  • Cookie存中文问题
    Cookie 中不能出现中文,如果有中文则通过URLEncoder.encode()来进行编码,获取时通过URLDecoder.decode()来进行解码。

    String name = "姓名";
    String value = "张三";
    //通过URLEncoder.encode()来进行编码
    name = URLEncoder.encode(name);
    value = URLEncoder.encode(value);
    //创建cookie对象
    Cookie cookie = new Cookie(name, value);
    //响应cookie对象
    response.addCookie(cookie);
    //获取时通过URLDecoder.decode()来进行解码
    URLDecoder.decode(cookie.getName());
    URLDecoder.decode(cookie.getValue());
  • 同名Cookie问题
    如果服务器端发送重复的Cookie那么会覆盖原有的Cookie。

  • 浏览器存放Cookie的数量
    不同的浏览器对Cookie也有限定,Cookie的存储有是上限的。Cookie是存储在客户端(浏览器)的,而且般是由服务器端创建和设定。后期结合Session来实现回话跟踪。cookie对大小的限制在4kb左右

Cookie的路径

​ Cookie的setPath设置cookie的路径,这个路径直接决定服务器的请求是否会从浏览器中加载某些cookie。

package com.xxx.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * Cookie的路径
 */
@WebServlet("/coo05")
public class Cookie05 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse resposne) throws ServletException, IOException {

        /* 当前服务器下任何项目的任意资源都可获取Cookie对象 */
        Cookie cookie1 = new Cookie("cookie01", "cookie01");
        cookie1.setPath("/");
        resposne.addCookie(cookie1);

        /* 当前项目下的资源可获取Cookie对象(默认不设置Cookie的path或设置为当前站点名) */
        Cookie cookie2 = new Cookie("cookie02", "cookie02");
        cookie2.setPath("/Cookie");//当前站点名
        resposne.addCookie(cookie2);

        /* 指定项目下的资源可获取Cookie对象 */
        Cookie cookie3 = new Cookie("cookie03", "cookie03");
        cookie3.setPath("/LoginServlet");//设置指定项目的站点名
        resposne.addCookie(cookie3);

        /* 指定目录下的资源可获取Cookie对象 */
        Cookie cookie4 = new Cookie("cookie04", "cookie04");
        cookie4.setPath("/Cookie/coo02");//设置指定项目的站点名
        resposne.addCookie(cookie4);
    }
}

image-20201115122454129

在LoginServlet中访问的Cookie

image-20201115122833796

​ 如果我们设置path,如果当前访问的路径包含了cookie的路径(当前访问路径在cookie路径基础上要比cookie的范围小) cookie就会加载到request对象之中。
​ cookie的路径指的是可以访问该cookie的顶层目录,该路径的子路径也可以访问该cookie。

总结:当访问的路径包含了cookie的路径时,则该请求将带上该cookie;如果访问路径不包含cookie路径,则该请求不会携带该cookie

Session对象

​ HttpSession对象是javax.servlet.http.HttpSession的实例,该接口并不像HttpServletRequest或HttpServletResponse还存在一个父接口,该接口只是一个纯粹的接口。这因为session本身就属于HTTP协议的范畴。
​ 对于服务器而言,每一个连接到它的客户端都是一个session,servlet容器使用此接口创建HTTP客户端和HTTP服务器之间的会话。会话将保留指定的时间段,跨多个连接或来自用户的页面请求。一个会话通常对应于一个用户,该用户可能多次访问一个站点。可以通过此接口查看和操作有关某个会话的信息,比如会话标识符、创建时间和最后一次访问时间。在整个session 中,最重要的就是属性的操作。
​ session无论客户端还是服务器端都可以感知到,若重新打开一个新的浏览器,则无法取得之前设置的session,因为每一个session只保存在当前的浏览器当中,并在相关的页面取得。
​ Session的作用就是为了标识一次会话,或者说确认一个用户;并且在一次会话(一个用户的多次请求)期间共享数据。我们可以通过request.getSession()方法,来获取当前会话的session对象。

标识符JSESSIONID

​ Session既然是为了标识一次会话,那么此次会话就应该有一个唯一的标志,这个标志就是sessionld。
​ 每当一次请求到达服务器,如果开启了会话(访问了session),服务器第一步会查看是否从客户端回传一个名为JSESSIONID的cookie,如果没有则认为这是一次新的会话,会创建一个新的session对象,并用唯一的sessionld为此次会话做一个标志。如果有JESSIONID这个cookie回传,服务器则会根据JSESSIONID这个值去查看是否含有id为JSESSION值的session对象,如果没有则认为是一个新的会话,重新创建一个新的session对象,并标志此次会话;如果找到了相应的session对象,则认为是之前标志过的一次会话,返回该session对象,数据达到共享。
​ 这里提到一个叫做JSESSIONID的cookie,这是一个比较特殊的cookie,当用户请求服务器时,如果访问了session,则服务器会创建一个名为JSESSIONID,值为获取到的session(无论是获取到的还是新创建的)的sessionld的cookie对象,并添加到response对象中,响应给客户端,有效时间为关闭浏览器。
​ 所以Session的底层依赖Cookie来实现。

package com.xxx.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;


/**
 * Session对象
 *  session对象的获取
 *      request.getSession();
 *      当获取Session对象时,会先判断是否存在
 *          存在,获取
 *          不存在,创建Session对象
 *  常用方法
 *      获取session的会话标识符 getId();
 *      获取session创建时间 getCreationTime();
 *      获取session最后一次访问时间 getLastAccessedTime();
 *      判断是否是新的session isNew();
 */
@WebServlet("/ser01")
public class Session01 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取session对象
        HttpSession session = request.getSession();

        //获取session的会话标识符
        String id = session.getId();
        System.out.println(id);

        //获取session创建时间
        System.out.println(session.getCreationTime());
        //获取session最后一次访问时间
        System.out.println(session.getLastAccessedTime());
        //判断是否是新的session
        System.out.println(session.isNew());
    }
}

session域对象

​ Session用来表示一次会话,在一次会话中数据是可以共享的,这时session作为域对象存在,可以通过setAttribute(name,value)方法向域对象中添加数据,通过getAttribute(name)从域对象中获取数据,通过removeAttribute(name)从域对象中移除数据。

//获取session对象
HttpSession session = request.getSession();
//设置session域对象
session.setAttribute("uname", "admin");
//获取指定名称的session域对象
String uname = (String)request.getAttribute("uname");
//移除指定名称的session对象
session.removeAttribute("uname");

​ 数据存储在session域对象中,当session对象不存在了,或者是两个不同的session对象时,数据也就不能共享了。这就不得不谈到session的生命周期。

package com.xxx.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;


/**
 * Session域对象
 *  请求转发
 *      一次请求
 *      request作用域有效
 *      session作用域有效
 *  重定向
 *      两次请求
 *      request作用域无效
 *      session作用域有效
 */
@WebServlet("/ser02")
public class Session02 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        /* session域对象 */
        //获取session对象
        HttpSession session = request.getSession();

        //设置域对象
        session.setAttribute("uname", "admin");
        session.setAttribute("upwd", "123456");

        //移除域对象
        session.removeAttribute("upwd");

        /* request域对象 */
        request.setAttribute("name", "zhangsan");

        //请求转发跳转到index.jsp页面
//        request.getRequestDispatcher("index.jsp").forward(request, response);
        //重定向跳转到index.jsp页面
        response.sendRedirect("index.jsp");
    }
}

session对象的销毁

package com.xxx.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;


/**
 * Session对象的销毁
 */
@WebServlet("/ser03")
public class Session03 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        /* session域对象 */
        //获取session对象
        HttpSession session = request.getSession();

        //设置域对象
        session.setAttribute("uname", "admin");

//        //获取session最大不活动时间
//        System.out.println("session最大不活动时间: " + session.getMaxInactiveInterval());
//        //修改session最大不活动时间
//        session.setMaxInactiveInterval(15);//15秒不失效
//        System.out.println("session最大不活动时间: " + session.getMaxInactiveInterval());

        //立即销毁
        session.invalidate();
    }
}

默认到期时间

​ 当客户端第一次请求servlet并且操作session时,session对象生成,Tomcat中 session默认的存活时间为30min,即不操作界面的时间,一旦有操作,session会重新计时。
​ 那么session的默认时间可以改么?答案是肯定的。
​ 可以在Tomcat中的conf目录下的web.xml文件中进行修改。

自己设置到期时间

​ 除了以上的修改方式外,我们也可以在程序中自己设定session的生命周期,通过session.setMaxInactiveInterval(int)来设定session的最大不活动时间,单位为秒。

//获取session对象
Httpsession session = request.getSession();
//设置session最大不活动时间
session.setMaxInactiveInterval(15);//15秒

​ 也可以通过getMaxInactiveInterval()方法来查看当前Session对象的最大不活动时间

//获取session的最大不活动时间
int time = session.getMaxInactiveInterval();

立即销毁

​ 可以通过session.invalidate()方法让session立刻失效

//销毁session对象
session.invalidate();

关闭浏览器销毁

​ Session底层依赖Cookie对象, Cookie对象默认只在浏览器中存活,关闭浏览器即失效。
​ 从前面的JESSION可知道,session的底层依赖cookie 实现,并且该cookie的有效时间为关闭浏览器,从而session在浏览器关闭时也相当于失效了(因为没有JSESSION再与之对应)。

//不操作

关闭服务器销毁

​ 当关闭服务器时,session销毁。
​ Session失效则意味着此次会话结束,数据共享结束。

//不操作

Context对象

​ 每一个web应用都有且仅有一个ServletContext对象,又称Application对象,从名称中可知,该对象是与应用程序相关的。在WEB容器启动的时候,会为每一个WEB应用程序创建一个对应的ServletContext对象。
​ 该对象有两大作用,第一、作为域对象用来共享数据,此时数据在整个应用程序中共享;第二、该对象中保存了当前应用程序相关信息。例如可以通过getServerInfo()方法获取当前服务器信息,getRealPath(String path)获取资源的真实路径等。

对象的获取和常用方法

package com.xxx.servlet;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


/**
 * 获取ServletContext对象
 *
 */
@WebServlet("/s01")
public class Servlet01 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //通过request对象获取
        ServletContext servletContext = request.getServletContext();

        //通过session对象获取
        ServletContext servletContext1 = request.getSession().getServletContext();

        //通过ServletConfig对象获取
        ServletContext servletContext2 = getServletConfig().getServletContext();

        //直接获取
        ServletContext servletContext3 = getServletContext();


        //常用方法
        //1.获取当前服务器的版本信息
        String serverInfo = request.getServletContext().getServerInfo();
        System.out.println("serverinfo: " + serverInfo);

        //2.获取当前项目真实路径
        String realPath = request.getServletContext().getRealPath("/");
        System.out.println("realPath: " + realPath);
    }
}

image-20201121182343510

ServletContext域对象

​ ServletContext 也可当做域对象来使用,通过向ServletContext 中存取数据,可以使得整个应用程序共享某些数据。当然不建议存放过多数据,因为ServletContext 中的数据一旦存储进去没有手动移除将会一直保存

//获取servletcontext对象
ServletContext servletContext = request.getServletContext();
//设置域对象
ServletContext.setAttribute("name", "zhangsan");
//获取域对象
String name = (String) servletContext.getAttribute("name");
//移除域对象
servletContext.removeAttribute("name");

Servlet的三大域对象

  • request域对象

    在一次请求中有效。请求转发有效,重定向失效

  • session域对象

    在一次会话中有效。请求转发和重定向都有效,session销毁后失效

  • servletSession域对象

    在整个应用程序中有效,服务器关闭后失效

域对象的选择:域范围越大,所占内存越多。在能够满足需求的情况下,尽可能选择域范围最小的。

文件上传

​ 文件上传涉及到前台页面的编写和后台服务器端代码的编写,前台发送文件,后台接收并保存文件,这才是一个完整的文件上传。

​ 在做文件上传的时候,会有一个上传文件的界面,首先我们需要一个表单,并且表单的请求方式为POST;其次我们的form表单的enctype必须设为”multipart/form-data”,即enctype=”multipart/form-data”,意思是设置表单的类型为文件上传表单。默认情况下这个表单类型是”application/x-www-form-urlencoded”,不能用于文件上传。只有使用了multipart/form-data才能完整地传递文件数据。

前端代码

文件上传:

1. 准备表单
 2. **设置表单的提交方式为POST请求 method="post"**
 3. **设置表单类型为文件上传表单 enctype="multipart/form-data"**
 4. **设置文件提交的地址 action="uploadServlet"**
 5. 准备表单元素
      1. 普通的表单项 type="text"
      2. 文件项 type="file"
6. 设置元素的name属性值**表单提交一定要设置表单元素的name属性值,否则后台无法接受数据!**
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文件上传</title>
</head>
<body>
    <form method="post" enctype="multipart/form-data" action="uploadServlet">
        姓名: <input type="text" name="uname"> <br>
        文件: <input type="file" name="myfile" > <br>
        <button>提交</button>
    </form>
</body>
</html>

后端代码

package com.xxxx.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;

@WebServlet("/uploadServlet")
@MultipartConfig    // 如果是文件上传,必须要加这个注解
public class UploadServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("文件上传");
        //设置请求的编码格式
        request.setCharacterEncoding("UTF-8");
        //获取普通表单项(获取参数)
        String uname = request.getParameter("uname");   // 表单中表单元素的name属性值
        System.out.println("uname: " + uname);

        //获取Part对象(Servlet将multipart/form-data的POST请求封装成Part对象)
        Part part = request.getPart("myfile");    // 表单中file文件域的name属性值

        //通过Part对象得到上传的文件名
        String cd = part.getHeader("Content-Disposition");
        String fileName = cd.substring(cd.lastIndexOf("=")+2, cd.length()-1);
        System.out.println("fileName: " + fileName);

        //得到文件存放的路径
        String filePath = request.getServletContext().getRealPath("/");
        System.out.println("filePath: " + filePath);

        //上传文件到指定目录
        part.write(filePath + "/" + fileName);
    }
}

image-20201122150429753

需要注意的是,在tomcat7及以上的环境下就没有part.getSubmittedFileName()这一方法,无法直接获取文件名,所以我们需要使用如下方法获取文件名

//通过Part对象得到上传的文件名
String cd = part.getHeader("Content-Disposition");
String fileName = cd.substring(cd.lastIndexOf("=")+2, cd.length()-1);

文件下载

超链接下载

​ 当我们在HTML或JSP页面中使用a标签时,原意是希望能够进行跳转,但当超链接遇到浏览器不识别的资源时会自动下载;当遇见浏览器能够直接显示的资源,浏览器就会默认显示出来,比如txt、png、jpg等。当然我们也可以通过download 属性规定浏览器进行下载。但有些浏览器并不支持。

默认下载

<!-- 当超链接遇到浏览器不识别的资源时,会自动下载 -->
<a href="test.zip">超链接下载</a>

指定download属性下载

<!-- 当超链接遇到浏览器识别的资源时,默认不会下载。通过download属性可进行下载 -->
<a href="test.zip" download>超链接下载</a>

​ download属性可以不写任何信息,会自动使用默认文件名。如果设置了download属性的值,则使用设置的值作为文件名。当用户打开浏览器点击链接的时候,就会直接下载文件。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文件下载</title>
</head>
<body>
    <!--
        超链接下载:
            当使用超链接(a标签)时,如果遇到浏览器能识别的资源,则会显示内容。否则会进行下载
        download属性
            通过download属性规定浏览器进行下载
    -->

    <!-- 浏览器能够识别的资源 -->
    <a href="download/SQL.txt">文本文件</a>
    <a href="download/test.jpg">图片文件</a>
    <!-- 浏览器不能识别的资源 -->
    <a href="download/test.zip">压缩文件</a>
    <hr>
    <a href="download/SQL.txt" download>文本文件</a>
    <a href="download/test.jpg" download="kcb.jpg">图片文件</a>

</body>
</html>

下载文件需要在IDEA中的服务器配置中添加资源路径,才能够访问到资源

image-20201122155556229

后台实现下载

代码有点小问题还未修复

实现步骤:

  1. 需要通过response.setContentType方法设置Content-type头字段的值,为浏览器无法使用某种方式或激活某个程序来处理的MIME类型,例如”application/octet-stream”或”application/x-msdownload”
  2. 需要通过response.setHeader方法设置Content-Disposition头的值为”attachment;filename=文件名
  3. 读取下载文件,调用response.getOutputStream方法向客户端写入附件内容。
package com.xxxx.servlet;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;

/**
 * 1. 需要通过response.setContentType方法设置Content-type头字段的值,为浏览器无法使用某种方式或激活某个程序来处理的MIME类型,例如"application/octet-stream"或"application/x-msdownload"
 * 2. 需要通过response.setHeader方法设置Content-Disposition头的值为"attachment;filename=文件名"
 * 3. 读取下载文件,调用response.getOutputStream方法向客户端写入附件内容。
 */

@WebServlet("/downloadServlet")
public class DownloadServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("文件下载...");

        //设置请求编码格式
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");

        //获取参数(下载文件名)
        String fileName = request.getParameter("fileName");
        System.out.println("fileName: " + fileName);
        //参数的非空判断trim():去除字符串前后空格
        if ( fileName == null || "".equals(fileName.trim()) ) {
            response.getWriter().write("请输入要下载的文件名");
            response.getWriter().close();
            return ;
        }
        //得到图片存放的路径
        String path = request.getServletContext().getRealPath("/download/");
        System.out.println("path: " + path);
        //通过路径得到file对象
        File file = new File(path + fileName);
        System.out.println("file: " + file);
        //判断文件对象是否存在并且是一个标准文件
        System.out.println(file.exists());
        System.out.println(file.isFile());
        if ( file.exists() && file.isFile() ) {
            //设置相应类型(浏览器无法使用某种方式或激活某个程序来处理的MIME类型)
            response.setContentType("application/x-msdownload");
            //设置响应头
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
            //得到file文件输入流
            InputStream in = new FileInputStream(file);
            //得到字节输出流
            ServletOutputStream out = response.getOutputStream();
            //定义一个byte数组
            byte[] bytes = new byte[1024];
            //定义一个长度
            int len = 0;
            //循环输出
            while ( (len = in.read(bytes)) != -1 ) {
                //输出
                out.write(bytes, 0, len);
            }
            //关闭资源
            out.close();
            in.close();
        }
        else {
            response.getWriter().write("文件不存在,请重试");
            response.getWriter().close();
        }
    }
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!