JSP&Servlet学习笔记(上)

学习笔记 专栏收录该内容
19 篇文章 7 订阅

目录

 

1. Web相关概念

1.1 软件架构

1.2 资源分类

1.3 网络通信三要素

2. Tomcat

2.1 web服务器软件概念

2.2 常见的java相关的web服务器软件

2.3 Tomcat

2.3.1 Tomcat的下载、安装与卸载

2.3.2 Tomcat的启动

2.3.3 Tomcat的关闭

2.3.4 Tomcat的配置

2.3.5 将Tomcat集成到IDEA中

3.Servlet入门

3.1 Servlet概念

3.2 快速入门

3.3 Servlet执行原理

3.4 Servlet的生命周期

3.5 Servlet3.0

3.6 Servlet的体系结构

3.7 Servlet相关配置

3.8 IDEA与Tomcat的相关配置

4. HTTP

4.1 概念

4.2 请求消息数据格式

4.2.1 请求行

4.2.2 请求头

4.2.3 请求空行

4.2.4 请求体(正文)

4.3 响应消息数据格式

4.3.1 响应行

4.3.2 响应头

4.3.3 响应空行

4.3.4 响应体

5. Servlet的Request对象

5.1 request对象和request对象的原理

5.2 Request对象的继承体系结构

5.3 Request对象的功能

5.3.1 获取请求消息数据

5.4 Request对象的其他功能

5.4.1 获取请求参数的通用方式

5.4.2 请求转发

5.4.3 共享数据

5.4.4 获取ServletContext

5.5 案例:用户登录

5.5.1 用户登录案例需求

5.5.2 代码开发实现

5.5.3 资源路径写法

6. Servlet的Response对象

6.1 Response对象的功能

6.1.1 设置响应行

6.1.2 设置响应头

6.1.3 设置响应体

6.2 Response对象的功能的应用

6.2.1 重定向

6.2.2 服务器输出字符串数据到浏览器

6.2.3 服务器输出字节数据到浏览器

6.2.4 实现验证码

6.3 ServletContext对象

6.3.1 概念

6.3.2 获取该对象的方法

6.3.3 功能

6.4 案例之文件下载

6.4.1 文件下载需求

6.4.2 分析

6.4.3 代码实现

7. 会话技术

7.1 概述

7.2 Cookie

7.2.1 概念

7.2.2 快速入门

7.2.3 实现原理

7.2.4 cookie细节

7.2.5 Cookie的特点和作用

7.2.6 案例-记住上一次访问时间

7.3 Session

7.3.1 概念

7.3.2 快速入门

7.3.3 原理

7.3.4 细节

7.3.5 session特点


注:JSP&Servlet学习笔记(下)

1. Web相关概念

1.1 软件架构

常见软件架构分为以下两类:

  • C/S:客户端/服务器端
  • B/S:浏览器/服务器端

1.2 资源分类

web资源分为静态资源和动态资源两种。

静态资源:所有用户访问后,得到的结果都是一样的,称为静态资源。静态资源可以直接被浏览器解析。如: html,css,JavaScript.......

动态资源:每个用户访问相同资源后,得到的结果可能不一样。称为动态资源。动态资源被访问后,需要先转换为静态资源,在返回给浏览器。如:servlet/jsp,php,asp....

1.3 网络通信三要素

  • IP:电子设备(计算机)在网络中的唯一标识。
  • 端口:应用程序在计算机中的唯一标识。 0~65536
  • 传输协议:规定了数据传输的规则。常用基础协议:
    • tcp:安全协议,三次握手。 速度稍慢。
    • udp:不安全协议。 速度快。

 


 

2. Tomcat

2.1 web服务器软件概念

服务器:安装了服务器软件的计算机。

服务器软件:接收用户的请求,处理请求,做出响应的软件。

web服务器软件:接收用户的请求,处理请求,做出响应。在web服务器软件中,可以部署web项目,让用户通过浏览器来访问这些项目。

2.2 常见的java相关的web服务器软件

  • webLogic:oracle公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
  • webSphere:IBM公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
  • JBOSS:JBOSS公司的,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
  • Tomcat:Apache基金组织,中小型的JavaEE服务器,仅仅支持少量的JavaEE规范servlet/jsp。开源的,免费的。

而我们主要学习的就是tomcat。

JavaEE:是Java语言在企业级开发中使用的技术规范的总和,一共规定了13项大的规范。

2.3 Tomcat

2.3.1 Tomcat的下载、安装与卸载

tomcat的地址地址:http://tomcat.apache.org/

下载下来后将压缩包解压即可,就已经安装完成了。

(注意:安装目录(即存放Tomcat解压缩后的目录文件夹)不要有中文和空格。)

如果要卸载tomcat的话,直接删除安装路径就可以了。

2.3.2 Tomcat的启动

找到bin目录下的startup.bat,双击运行该文件即可。

会出现一个黑框,打印着日志

接着在浏览器输入http://localhost:8080即可访问了

在使用过程中可以遇到问题:

  • (1)黑窗口一闪而过
    • 原因:没有正确配置JAVA_HOME环境变量
    • 解决方案:正确配置JAVA_HOME环境变量,百度解决。
  • (2)启动报错端口被占用
    • 原因:80880端口被占用
    • 解决:关闭掉被占用的端口。有以下两种解决方案:
      • ①找到占用的端口号,并且找到对应的进程,杀死该进程。通过netstat -ano在DOS窗口找到8080被占用的端口。
      • ②修改自身的端口号,通过conf/server.xml配置文件

注:一般会将tomcat的默认端口号修改为80。80端口号是http协议的默认端口号。

2.3.3 Tomcat的关闭

关闭tomcat分为正常关闭和强制关闭。

  • 正常关闭:
    • 找到bin/shutdown.bat,双击运行即可。
    • 使用快捷键Ctrl+C
  • 强制关闭:
    • 点击启动窗口的×

2.3.4 Tomcat的配置

Tomcat的目录结构如下:

部署配置项目的方式有如下三种:

1. 直接将项目放到webapps目录下即可。

然后通过/Demo3_war这样的方式访问,其中/Demo3_war是项目的访问路径,也是该项目的根目录文件夹名称。

除了上面的,还可以简化部署,将项目打成一个war包,再将war包放置到webapps目录下,war包会自动解压。

2. 配置conf/server.xml文件(这种配置很不安全,可能弄坏整个tomcat的配置)

在<Host>标签中作如下配置:

其中D盘的hello目录下有如下文件作为测试:

在浏览器进行访问:

注:不推荐使用,不便于管理项目。

3. 在conf\Catalina\localhost创建任意名称的xml文件

重启tomcat,浏览器进行访问

下面介绍下Java动态项目的目录结构:

  • 项目的根目录
    • WEB-INF目录:
      • web.xml:web项目的核心配置文件
      • classes目录:放置字节码文件的目录
      • lib目录:放置依赖的jar包

2.3.5 将Tomcat集成到IDEA中

使用IDEA创建一个JavaEE项目,并部署项目。

点击“New”集成tomcat

集成成功

然后点击“Next”创建web项目

如下创建项目完成

点击绿色箭头运行,会自动打开浏览器

项目集成成功。

下面说下一些基本使用:

  • 热部署静态资源

即在web下创建一些如html文件,在html更新内容等,都不需要再重启tomcat,直接刷新浏览器就能访问了。

  • 配置虚拟路径

配置后,浏览器输入:http://localhost:8080/hello/才能访问

如果不配置,浏览器通过http://localhost:8080/就能访问

 


 

3.Servlet入门

3.1 Servlet概念

Servlet是运行在服务器端的小程序,servlet全称是server applet。

实际上Servlet就是一个接口,定义了Java类被浏览器访问到(tomcat识别)的规则。

我们(程序员)使用的时候,就是自定义一个类来实现Servlet接口,复写方法。

3.2 快速入门

步骤:

  • (1)使用IDEA创建JavaEE项目。
  • (2)自定义一个类,实现Servlet接口。
  • (3)实现接口中的抽象方法,主要是在service()方法中输出。
  • (4)在web.xml中配置Servlet

第一步:创建一个JavaEE项目

第二步:自定义一个类,实现Servlet接口

public class HelloServlet implements Servlet {

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {

    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

第三步:实现接口中的抽象方法,主要是在service()方法中输出。

第四步:在web.xml中配置Servlet

    <!--配置Servlet -->
    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.demo.servlet.HelloServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

第五步:测试

运行tomcat,浏览器输入:http://localhost:8080/hello控制台查看打印结果

发现service()方法被执行。

3.3 Servlet执行原理

执行原理如下图:

  • 1. 当服务器接受到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径
  • 2. 查找web.xml文件,是否有对应的<url-pattern>标签体内容。
  • 3. 如果有,则在找到对应的<servlet-class>全类名
  • 4. tomcat会将全类名所代表的字节码文件加载进内存,并且创建其对象
  • 5. 调用其方法,这里调用了service()方法。

3.4 Servlet的生命周期

如下图:

(1)被创建:执行init()方法,只执行一次。

①概述

Servlet什么时候被创建?即第一次被访问时,Servlet才会被创建。

即只有访问如上http://localhost:8080/hello才会执行init()方法和service()方法

但是,可以手动配置执行Servlet的创建时机。

②手动配置创建Servlet的时机

配置方法如下(在web.xml的<servlet>标签下配置):

  • 第一次访问时,创建Servlet,那么设置<load-on-startup>值为负数,默认值为-1.

下面的配置就会在第一次访问时创建Servlet,同不配置产生一样的效果。

  • 在服务器启动时,创建Servlet,那么设置<load-on-startup>值为0正整数

使用如下配置时,在启动服务器后就会执行init()方法,创建Servlet,控制台也会输出"init..."

③可能存在的线程安全问题

Servlet的init()方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的。

多个用户同时访问时,如果Servlet中定义了成员变量,则可能存在线程安全问题。

解决:尽量不要在Servlet中定义成员变量。即使定义了成员变量,也不要对修改值。

(2)提供服务:执行service()方法,执行多次。

每次访问Servlet时,service()方法都会被调用一次。

即访问如上http://localhost:8080/hello都会执行service()方法,就可以看到控制台不断打印"service..."

(3)被销毁:执行destroy()方法,只执行一次。

  • 该destroy()方法是Servlet被销毁时才执行。
  • 什么时候Servlet被销毁呢?当服务器关闭时,Servlet被消耗。
  • 但注意,只有服务器正常关闭时,才会执行destroy方法。
  • destroy方法在Servlet被销毁之前执行,一般用于释放资源

除了上面三个关于servlet生命周期的方法之外,实现Servlet接口还有两个方法getServletConfig()和getServletInfo()。

  • getServletConfig()方法获取ServletConfig对象,即Servlet的配置对象
  • getServletInfo()方法获取servlet的一些信息,关于版本、作者的信息

3.5 Servlet3.0

(1)概述

Servlet3.0比较显著的好处是支持注解配置,即不需要配置web.xml文件了。

其他特性请参考:Servlet 3.0 新特性详解

(2)使用步骤

使用步骤如下:

  • 创建JavaEE项目,选择Servlet的版本3.0以上,可以不创建web.xml
  • 定义一个类,实现Servlet接口
  • 复写方法
  • 在类上使用@WebServlet注解,进行配置

第一步:创建JavaEE项目,选择Servlet的版本3.0以上,可以不创建web.xml

第二步:定义一个类,实现Servlet接口

public class HelloServlet implements Servlet {

}

第三步:复写方法

public class HelloServlet implements Servlet {

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("init...");
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) {
        System.out.println("service...");
    }

    @Override
    public void destroy() {
        System.out.println("destroy...");
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public String getServletInfo() {
        return null;
    }
}

第四步:在类上使用@WebServlet注解,进行配置

其中该注解

@WebServlet(urlPatterns = "/hello")

// 可以简写为如下形式:
// @WebServlet("资源路径")
@WebServlet("/hello")

等价于

    <!--配置Servlet -->
    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.demo.servlet.HelloServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

查看@WebServlet注解的源代码,如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet {
    String name() default "";//相当于<Servlet-name>

    String[] value() default {};//代表urlPatterns()属性配置

    String[] urlPatterns() default {};//相当于<url-pattern>

    int loadOnStartup() default -1;//相当于<load-on-startup>

    WebInitParam[] initParams() default {};

    boolean asyncSupported() default false;

    String smallIcon() default "";

    String largeIcon() default "";

    String description() default "";

    String displayName() default "";
}

3.6 Servlet的体系结构

为什么会说到Servlet体系结构呢?实现一个Servlet接口,但实际使用的只有service()方法,其他方法基本上用不到,还不能删除。

(1)Servlet

该类要实现五个方法,但常用的只有一个service()方法

@WebServlet(urlPatterns = "/hello")
public class HelloServlet implements Servlet {

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {

    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

(2)GenericServlet

该类将Servlet接口中其他的方法做了默认空实现,只将service()方法作为抽象。

因此将来定义Servlet类时,可以继承GenericServlet,实现service()方法即可。

@WebServlet("/hello2")
public class HelloServlet2 extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {

    }
}

(3)HttpServlet

这才是最常用的实现类,屏蔽了对请求方式的判断处理。

对http协议的一种封装,简化操作。

如图:

实现上查看HttpServlet的源代码的service()方法,正是对请求方法getMethod()的封装。

一般自定义类使用该类的步骤如下:

  • 定义类继承HttpServlet
  • 复写doGet/doPost方法
public class HelloServlet3 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        
    }
}

3.7 Servlet相关配置

(1)urlPartten:Servlet访问路径

由下图可以看出<url-partten>对应的是Servlet访问路径

使用注解后对应的就是urlPartten参数,该参数指向Servlet访问路径。

注意:

  • 一个Servlet可以定义多个访问路径 : @WebServlet({"/d4","/dd4","/ddd4"})
  • 路径定义规则:
    • /xxx:路径匹配。例如"/login"等
    • /xxx/xxx:多层路径,目录结构。/xxx/*表示匹配xxx目录下的任意Servlet。例如"/user/login"等。
    • *.do:扩展名匹配,不要加"/",否则会报错

3.8 IDEA与Tomcat的相关配置

1、IDEA会为每一个tomcat部署的项目单独建立一份配置文件

运行一个Servlet项目后,可以在控制台看到如下的输出日志:

注意Using CATALINA_BASE,复制它对应的路径,在本地资源管理器打开:

打开conf目录,里面就是配置文件

如server.xml配置文件中可以修改端口、虚拟目录等。

但我们同IDEA集成使用后,不需要再在这个配置文件中修改,只需要在IDE界面中修改就会同步到配置文件的修改中:

发现配置文件也同步修改了

如果要查看项目的所在目录,可以在/conf/Catalina/localhost下查看这个xml配置文件

打开查看,就是项目实际所在的目录

再将这个目录在资源管理器中打开,就是本项目

工作空间项目  和  tomcat部署的web项目

  • tomcat真正访问的是“tomcat部署的web项目”,"tomcat部署的web项目"对应着"工作空间项目" 的web目录下的所有资源
  • WEB-INF目录下的资源不能被浏览器直接访问。

 


 

4. HTTP

4.1 概念

HTTP全称是Hyper Text Transfer Protocol,即是超文本传输协议。

什么是传输协议?即定义了客户端和服务器端通信时,发送数据的格式。

特点:

  • 基于TCP/IP的高级协议
  • 默认端口号:80
  • 基于请求/响应模型的:一次请求对应一次响应
  • 无状态的:每次请求之间相互独立,不能交互数据

历史版本:

  • 1.0:每一次请求响应都会建立新的连接
  • 1.1:复用连接

4.2 请求消息数据格式

通过浏览器向Servlet发送一个请求,然后浏览器F12查看,可以看到请求头和响应头。

将GET请求头原始信息复制下来,如下:

GET /hello HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: keep-alive
Cookie: Webstorm-bbf7ba1a=9f630819-8a4f-40ea-8164-7a71918ee9aa; Idea-d1852ee0=946448e7-234a-4caa-83bb-38b847d8fb58; JSESSIONID=ADE0A02EA8BCCC03020517D711FFF2A9
Upgrade-Insecure-Requests: 1

将POST请求头原始信息复制下来,如下:

POST /hello HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 13
Origin: http://localhost:8080
Connection: keep-alive
Referer: http://localhost:8080/postSubmit.jsp
Cookie: Webstorm-bbf7ba1a=9f630819-8a4f-40ea-8164-7a71918ee9aa; Idea-d1852ee0=946448e7-234a-4caa-83bb-38b847d8fb58; JSESSIONID=86AE0FB0BDD6142DDD4B6A3AD2438374
Upgrade-Insecure-Requests: 1

请求消息是客户端发送给服务器端的数据,请求消息的数据格式分为:

  • 请求行
  • 请求头
  • 请求空行
  • 请求体(正文)

4.2.1 请求行

请求行定义了请求方式、请求URL与请求协议/版本号相关信息。

GET /hello HTTP/1.1

关于请求方式,HTTP协议有7种请求方式,常用的有如下两种:

  • GET
  • POST
    • 1. 请求参数在请求体中
    • 2. 请求的url长度没有限制的
    • 3. 相对安全

4.2.2 请求头

请求头定义了客户端浏览器要告诉服务器的一些信息。

Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: keep-alive
Cookie: Webstorm-bbf7ba1a=9f630819-8a4f-40ea-8164-7a71918ee9aa; Idea-d1852ee0=946448e7-234a-4caa-83bb-38b847d8fb58; JSESSIONID=ADE0A02EA8BCCC03020517D711FFF2A9
Upgrade-Insecure-Requests: 1

常见的请求头:

  • User-Agent:浏览器告诉服务器,我访问你使用的浏览器版本信息,是浏览器版本信息
    • 作用:可以在服务器端获取该头的信息,解决浏览器的兼容性问题
  • Referer:告诉服务器,我(当前请求)从哪里来?例如: http://localhost/login.html
    • 作用:防盗链;统计工作。

4.2.3 请求空行

空行,就是用于分割POST请求的请求头,和请求体的。

4.2.4 请求体(正文)

GET方式是没有请求体的。

封装POST请求消息的请求参数的。

4.3 响应消息数据格式

将响应头数据信息复制下来,如下:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Length: 0
Date: Thu, 10 Dec 2020 02:20:53 GMT

有响应数据的情况下会有数据

响应消息是服务器端发送给客户端的数据,数据格式如下:

  • 响应行
  • 响应头
  • 响应空行
  • 响应体:传输的数据

4.3.1 响应行

HTTP/1.1 200 OK

响应行的组成:协议/版本 响应状态码 状态码描述

响应状态码:服务器告诉客户端浏览器本次请求和响应的一个状态。

  • 状态码都是3位数字
  • 状态码的分类:
    • 1xx:服务器就收客户端消息,但没有接受完成,等待一段时间后,发送1xx多状态码
    • 2xx:成功。代表:200
    • 3xx:重定向。代表:302(重定向),304(访问缓存)
    • 4xx:客户端错误。代表如下:
      • 404(请求路径没有对应的资源)
      • 405:请求方式没有对应的doXxx方法
    • 5xx:服务器端错误。代表:500(服务器内部出现异常)

4.3.2 响应头

Server: Apache-Coyote/1.1
Content-Length: 0
Date: Thu, 10 Dec 2020 02:20:53 GMT

常见的响应头:

  • Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式。
  • Content-disposition:服务器告诉客户端以什么格式打开响应体数据。可选择的值有如下两种:
    • in-line:默认值,在当前页面内打开
    • attachment;filename=xxx:以附件形式打开响应体。文件下载

4.3.3 响应空行

4.3.4 响应体

响应传输的数据,有字符串也有二进制数据,都会由浏览器解析。

(建议:使用抓包工具查看请求信息与响应信息,学习更为直观)

 


 

5. Servlet的Request对象

5.1 request对象和request对象的原理

request和response对象是由服务器创建的。我们来使用它们。

request对象是来获取请求消息,response对象是来设置响应消息。

5.2 Request对象的继承体系结构

	ServletRequest		--	接口
		|	继承
	HttpServletRequest	-- 接口
		|	实现
	org.apache.catalina.connector.RequestFacade 类(tomcat编写的,可以在tomcat的源码中看到)

5.3 Request对象的功能

5.3.1 获取请求消息数据

5.3.1.1 获取请求行数据

即要获取如下数据:

GET /demo/hello?username=lisi HTTP/1.1

要获取以上请求行数据,Request对象提供了以下方法:

  • 1. 获取请求方式:GET
    • String getMethod()
  • 2. 获取虚拟目录:/demo
    • String getContextPath()
  • 3. 获取Servlet路径: /hello
    • String getServletPath()
  • 4. 获取get方式请求参数:username=lisi
    • String getQueryString()
  • 5. 获取请求URI:/demo/hello
  • 6. 获取协议及版本:HTTP/1.1
    • String getProtocol()
  • 7. 获取客户机的IP地址:127.0.0.1
    • String getRemoteAddr()

 代码实例如下:

@WebServlet("/hello")
public class HelloServlet1 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 获取请求方式:GET
        String method = request.getMethod();
        System.out.println(method);
        System.out.println("-----------------------------------------");

        // 2. 获取虚拟目录:/demo
        String contextPath = request.getContextPath();
        System.out.println(contextPath);
        System.out.println("-----------------------------------------");

        // 3. 获取Servlet路径: /hello
        String servletPath = request.getServletPath();
        System.out.println(servletPath);
        System.out.println("-----------------------------------------");

        // 4. 获取get方式请求参数:username=lisi
        String queryString = request.getQueryString();
        System.out.println(queryString);
        System.out.println("-----------------------------------------");

        // 5. 获取请求URI:/demo/hello
        String requestURI = request.getRequestURI();
        System.out.println(requestURI);
        StringBuffer requestURL = request.getRequestURL();
        System.out.println(requestURL);
        System.out.println("-----------------------------------------");

        // 6. 获取协议及版本:HTTP/1.1
        String protocol = request.getProtocol();
        System.out.println(protocol);
        System.out.println("-----------------------------------------");

        // 7. 获取客户机的IP地址:127.0.0.1
        String remoteAddr = request.getRemoteAddr();
        System.out.println(remoteAddr);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}
/**
 * 打印结果:
 * 
 * GET
 * -----------------------------------------
 * /demo
 * -----------------------------------------
 * /hello
 * -----------------------------------------
 * username=lisi
 * -----------------------------------------
 * /demo/hello
 * http://localhost:8080/demo/hello
 * -----------------------------------------
 * HTTP/1.1
 * -----------------------------------------
 * 127.0.0.1
 */

在浏览器输入http://localhost:8080/demo/hello?username=lisi查看控制台打印结果

5.3.1.2 获取请求头数据

如果要获取这样的请求头数据:

Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0

可以有如下方法:

  • String getHeader(String name):通过请求头的名称获取请求头的值,name参数不区分大小写
  • Enumeration<String> getHeaderNames():获取所有的请求头名称

代码实例:可以通过判断user-agent来判断使用的浏览器从而解决浏览器的兼容问题。

@WebServlet("/hello2")
public class HelloServlet2 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // String getHeader(String name):通过请求头的名称获取请求头的值,name参数不区分大小写
        String host = request.getHeader("Host");
        System.out.println(host);
        String userAgent = request.getHeader("user-agent");
        System.out.println(userAgent);

        System.out.println("-------------------------");

        // Enumeration<String> getHeaderNames():获取所有的请求头名称
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            System.out.println(headerNames.nextElement());
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}
/**
 * 打印结果:
 * localhost:8080
 * Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36
 * -------------------------
 * host
 * connection
 * upgrade-insecure-requests
 * user-agent
 * sec-fetch-dest
 * accept
 * sec-fetch-site
 * sec-fetch-mode
 * sec-fetch-user
 * accept-encoding
 * accept-language
 * cookie
 */

浏览器输入http://localhost:8080/demo/hello2然后在控制台查看打印结果

5.3.1.3 获取请求体数据

请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数。

因此得到请求体数据的步骤如下:

  • 1. 获取流对象
    • BufferedReader getReader():获取字符输入流,只能操作字符数据
    • ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据
  • 2. 再从流对象中拿数据

因此在web下创建一个post.jsp来测试.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form action="/hello3" method="post">
        <input type="text" name="username">
        <input type="submit" value="提交">
    </form>
</body>
</html>

然后在Servlet中使用getReader()方法读取

@WebServlet("/hello3")
public class HelloServlet3 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取字符流
        BufferedReader br = request.getReader();
        //2.读取数据
        String line = null;
        while((line = br.readLine()) != null){
            System.out.println(line);
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

getInputStream()后面再举例说明。

5.4 Request对象的其他功能

5.4.1 获取请求参数的通用方式

上面的方式获取某个参数很麻烦,所以有以下通用获取请求参数的方式。不论get还是post请求方式都可以使用下列方法来获取请求参数。

  • 1. String getParameter(String name):根据参数名称获取参数值
    • 例如: username=zs&password=123
  • 2. String[ ] getParameterValues(String name):根据参数名称获取参数值的数组
    • 例如:hobby=xx&hobby=game
  • 3. Enumeration<String> getParameterNames():获取所有请求的参数名称
  • 4. Map<String,String[]> getParameterMap():获取所有参数的map集合

在POST提交中可能遇到中文问题:

  • get方式:tomcat 8 已经将get方式乱码问题解决了
  • post方式:会乱码
    • 解决:在获取参数前,设置request的编码request.setCharacterEncoding("utf-8");。注意,这里设置成utf-8其实是跟前端页面设置的编码是utf8有关,必须一致。

实例代码:

demo4.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="/demo/hello4" method="post">
    <input type="text" placeholder="请输入用户名" name="username"><br>
    <input type="text" placeholder="请输入密码" name="password"><br>

    <input type="checkbox" name="hobby" value="game">游戏
    <input type="checkbox" name="hobby" value="study">学习
    <br>

    <input type="submit" value="注册">

</form>
</body>
</html>

HelloServlet4.java

@WebServlet("/HelloServlet4")
public class HelloServlet4 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //post 获取请求参数

        //根据参数名称获取参数值
        String username = request.getParameter("username");
       /* System.out.println("post");
        System.out.println(username);*/

        //根据参数名称获取参数值的数组
        String[] hobbies = request.getParameterValues("hobby");
        /*for (String hobby : hobbies) {
            System.out.println(hobby);
        }*/

        //获取所有请求的参数名称
        Enumeration<String> parameterNames = request.getParameterNames();
        /*while(parameterNames.hasMoreElements()){
            String name = parameterNames.nextElement();
            System.out.println(name);
            String value = request.getParameter(name);
            System.out.println(value);
            System.out.println("----------------");
        }*/

        // 获取所有参数的map集合
        Map<String, String[]> parameterMap = request.getParameterMap();
        //遍历
        Set<String> keyset = parameterMap.keySet();
        for (String name : keyset) {
            //获取键获取值
            String[] values = parameterMap.get(name);
            System.out.println(name);
            for (String value : values) {
                System.out.println(value);
            }
            System.out.println("-----------------");
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //get 获取请求参数
/*
        //根据参数名称获取参数值
        String username = request.getParameter("username");
        System.out.println("get");
        System.out.println(username);*/

        this.doPost(request, response);
    }
}

因为doPost()和doGet()方法中使用的是通用请求参数方式,都可以使用,因此只需要在一个方法体内写逻辑处理,另一个方法直接调用写了逻辑处理的方法即可。

5.4.2 请求转发

注意:同请求重定向比较学习

请求转发是一种在服务器内部的资源跳转方式。

是通过Request来进行的,从一个Servlet跳转到另一个Servlet。

请求转发的步骤:

  • 1. 通过request对象获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path)
  • 2. 使用RequestDispatcher对象来进行转发:forward(ServletRequest request, ServletResponse response)
// 请求转发的第一种写法
RequestDispatcher requestDispatcher = request.getRequestDispatcher("/hello5_2");
requestDispatcher.forward(request, response);

// 请求转发的第二种写法
request.getRequestDispatcher("/hello5_2").forward(request, response);

请求转发的特点:

  • 1. 浏览器地址栏路径不发生变化
  • 2. 只能转发到当前服务器内部资源中。即无法转发到百度等外部网站中
  • 3. 转发是一次请求

实例代码:

HelloServlet5_1.java

@WebServlet("/hello5_1")
public class HelloServlet5_1 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("HelloServlet5_1...");
//        // 请求转发的第一种写法
//        RequestDispatcher requestDispatcher = request.getRequestDispatcher("/hello5_2");
//        requestDispatcher.forward(request, response);

        //存储数据到request域中
        request.setAttribute("msg", "hello");

        // 请求转发的第二种写法
        request.getRequestDispatcher("/hello5_2").forward(request, response);

        // 无法转发到当前服务器之外的资源
//        request.getRequestDispatcher("http://www.baidu.com").forward(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

HelloServlet5_2.java

@WebServlet("/hello5_2")
public class HelloServlet5_2 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取数据
        Object msg = request.getAttribute("msg");
        System.out.println(msg);

        System.out.println("HelloServlet5_2...");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

5.4.3 共享数据

可以在request作用域内共享数据,即从一个Servlet得到请求转发的多个Servlet内都可以共享request域内的数据。

  • 域对象:一个有作用范围的对象,可以在范围内共享数据
  • request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据,即从一个Servlet得到请求转发的多个Servlet。
  • 方法:
    • 1. void setAttribute(String name,Object obj):存储数据
    • 2. Object getAttitude(String name):通过键获取值
    • 3. void removeAttribute(String name):通过键移除键值对

实例代码参考上一节

5.4.4 获取ServletContext

  • 方法:ServletContext getServletContext()

该方法获取到的ServletContext对象后面会详细说明。

5.5 案例:用户登录

5.5.1 用户登录案例需求

  • 1.编写login.html登录页面。username & password 两个输入框
  • 2.使用Druid数据库连接池技术,操作mysql,day14数据库中user表
  • 3.使用JdbcTemplate技术封装JDBC
  • 4.登录成功跳转到SuccessServlet展示:登录成功!用户名,欢迎您
  • 5.登录失败跳转到FailServlet展示:登录失败,用户名或密码错误

5.5.2 代码开发实现

第一步:创建项目,导入html页面,配置文件,jar包

  • 使用IDEA创建JavaWeb项目

  • 导入jar包

在WEB-INF目录下创建lib文件夹,然后将jar包放入

  • 将index.jsp改造成一个登录页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>案例:用户登录</title>
</head>
<body>
<form action="/demo/loginServlet" method="post">
    用户名:<input type="text" name="username"> <br>
    密码:<input type="password" name="password"><br>

    <input type="submit" value="登录">

</form>
</body>
</html>
  • 在src目录创建jdbc.properties文件配置数据库连接信息
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=root

# 由于使用了连接池,所以需要注意上面的键名,不能随便取

第二步:创建数据库环境

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(30) NOT NULL,
  `password` varchar(30) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

第三步:创建包com.demo.domain,创建实体类User.java

public class User {
    private int id;
    private String username;
    private String password;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

第四步:创建com.demo.util包,编写工具类JDBCUtils.java

public class JDBCUtils {
    private static DataSource ds ;

    static {
        try {
            //1.加载配置文件
            Properties pro = new Properties();
            //使用ClassLoader加载配置文件,获取字节输入流
            InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
            pro.load(is);

            //2.初始化连接池对象
            ds = DruidDataSourceFactory.createDataSource(pro);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取连接池对象
     */
    public static DataSource getDataSource(){
        return ds;
    }

    /**
     * 获取连接Connection对象
     */
    public static Connection getConnection() throws SQLException, SQLException {
        return ds.getConnection();
    }
}

第五步:创建包com.demo.dao,创建类UserDao.java,提供login()方法

public class UserDao {
    //声明JDBCTemplate对象共用
    private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());

    /**
     * 登录方法
     *
     * @param loginUser 只有用户名和密码
     * @return user包含用户全部数据, 没有查询到,返回null
     */
    public User login(User loginUser) {
        //1.编写sql
        String sql = "select * from user where username = ? and password = ?";
        //2.调用query方法
        User user = null;
        try {
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return user;
    }
}

第六步:编写com.demo.web.servlet.LoginServlet类

@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.设置编码
        req.setCharacterEncoding("utf-8");
        //2.获取请求参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        //3.封装user对象
        User loginUser = new User();
        loginUser.setUsername(username);
        loginUser.setPassword(password);

        //4.调用UserDao的login方法
        UserDao dao = new UserDao();
        User user = dao.login(loginUser);

        //5.判断user
        if (user == null) {
            //登录失败
            req.getRequestDispatcher("/failServlet").forward(req, resp);
        } else {
            //登录成功
            //存储数据
            req.setAttribute("user", user);
            //转发
            req.getRequestDispatcher("/successServlet").forward(req, resp);
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

第七步:编写FailServlet.java和SuccessServlet.java类

SuccessServlet.java

@WebServlet("/successServlet")
public class SuccessServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //获取request域中共享的user对象
        User user = (User) request.getAttribute("user");

        if (user != null) {
            //设置编码
            response.setContentType("text/html;charset=utf-8");
            //输出
            response.getWriter().write("登录成功!" + user.getUsername() + ",欢迎您");
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        this.doPost(request, response);
    }
}

FailServlet.java

@WebServlet("/failServlet")
public class FailServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置编码
        response.setContentType("text/html;charset=utf-8");
        //输出
        response.getWriter().write("登录失败,用户名或密码错误");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

然后运行tomcat测试效果。

5.5.3 资源路径写法

  • login.html中form表单的action路径的写法:虚拟目录+Servlet的资源路径。

 


 

6. Servlet的Response对象

6.1 Response对象的功能

功能:设置响应消息(响应行、响应头、响应体)。

6.1.1 设置响应行

响应行格式:

HTTP/1.1 200 ok
  • 设置状态码的方法:setStatus(int sc)

6.1.2 设置响应头

  • 设置响应头:setHeader(String name, String value)

6.1.3 设置响应体

即将数据输出到客户端通过响应体。使用步骤:

  • 1. 获取输出流
    • 字符输出流:PrintWriter getWriter()
    • 字节输出流:ServletOutputStream getOutputStream()
  • 2. 使用输出流,将数据输出到客户端浏览器

 

6.2 Response对象的功能的应用

6.2.1 重定向

(1)重定向:即资源跳转的方式。

(2)重定向的代码实现。如下:

// 1. 设置状态码为302
response.setStatus(302);
// 2.设置响应头location
response.setHeader("location","/demo/demoServlet");

或者用下面更简单的方式

//简单的重定向方法
response.sendRedirect("/demo/demoServlet");

代码实例参考:DemoServlet1_1.java与DemoServlet1_2.java

(3)重定向的特点与转发的特点

  • 重定向的特点(redirect)
    • 1. 地址栏发生变化
    • 2. 重定向可以访问其他站点(服务器)的资源。既可以重定向到如百度这样的网址去。
    • 3. 重定向是两次请求。不能使用request对象来共享数据
  • 转发的特点(forward)
    • 1. 转发地址栏路径不变
    • 2. 转发只能访问当前服务器下的资源
    • 3. 转发是一次请求,可以使用request对象来共享数据

(4)重定向的路径写法

路径分为相对路径和绝对路径。下面说下两种路径的写法:

  • 1. 相对路径:通过相对路径不可以确定唯一资源
    • 例如:./index.html
    • 特点:不以/开头,以.开头路径
    • 规则:找到当前资源和目标资源之间的相对位置关系
      • ./:表示当前目录(./可以省略不写)
      • ../:表示后退一级目录
  • 2. 绝对路径:通过绝对路径可以确定唯一资源
    • 例如:http://localhost:8080/demo/demoServlet 或 /demo/demoServlet
    • 特点:以/开头的路径
    • 规则:判断定义的路径是给谁用的?判断请求将来从哪儿发出
      • 给客户端浏览器使用:需要加虚拟目录(项目的访问路径)
        • 建议虚拟目录动态获取:request.getContextPath(),避免虚拟目录变化。例如<a> , <form>,重定向...
      • 给服务器使用:不需要加虚拟目录
        • 转发路径。如request.getRequestDispather("/demoServlet").forward(request,response)

代码实例参考:location.jsp、location2.jsp

6.2.2 服务器输出字符串数据到浏览器

(1)实现步骤:

  • 1. 获取字符输出流
  • 2. 输出数据

(2)需要注意中文乱码问题

不同的浏览器解码不一样,所以可能出现乱码问题。

从IE浏览器查看编码,使用的GBK解码,浏览器默认按照什么编码解码跟电脑系统环境有关信息。

故解决步骤如下:

  • 1. PrintWriter pw = response.getWriter();获取的流的默认编码是ISO-8859-1
  • 2. 设置该流的默认编码
  • 3. 告诉浏览器响应体使用的编码
//获取流对象之前,设置流的默认编码:ISO-8859-1 设置为:GBK
response.setCharacterEncoding("utf-8");
//告诉浏览器,服务器发送的消息体数据的编码。建议浏览器使用该编码解码
response.setHeader("content-type","text/html;charset=utf-8");

更简单的形式:

//简单的形式,设置编码,是在获取流之前设置
response.setContentType("text/html;charset=utf-8");

代码实例如下:

@WebServlet("/demo2")
public class DemoServlet2 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//        //获取流对象之前,设置流的默认编码:ISO-8859-1 设置为:GBK
//        response.setCharacterEncoding("utf-8");
//        //告诉浏览器,服务器发送的消息体数据的编码。建议浏览器使用该编码解码
//        response.setHeader("content-type","text/html;charset=utf-8");

        //简单的形式,设置编码,是在获取流之前设置
        response.setContentType("text/html;charset=utf-8");

        //1.获取字符输出流
        PrintWriter pw = response.getWriter();
        //2.输出数据
        pw.write("<h1>hello response</h1>");
        pw.write("您好!response");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

6.2.3 服务器输出字节数据到浏览器

实现步骤:

  • 1. 获取字节输出流
  • 2. 输出数据

代码实例如下:

@WebServlet("/demo3")
public class DemoServlet3 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");

        //1.获取字节输出流
        ServletOutputStream sos = response.getOutputStream();
        //2.输出数据
        sos.write("你好".getBytes("utf-8"));
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

6.2.4 实现验证码

验证码的本值就是一张图片,而输入的是图片中的信息。之所以使用验证码是为了防止有人恶意注册,用机器不断自动填表单注册。

code.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<img id="checkCode" src="<%=request.getContextPath()%>/codeServlet"/>

<a id="change" href="">看不清换一张?</a>
</body>
<script>
    /*
        分析:
            点击超链接或者图片,需要换一张
            1.给超链接和图片绑定单击事件
            2.重新设置图片的src属性值
     */
    window.onload = function () {
        //1.获取图片对象
        var img = document.getElementById("checkCode");
        //2.绑定单击事件
        img.onclick = function () {
            //加时间戳
            var date = new Date().getTime();
            img.src = "<%=request.getContextPath()%>/codeServlet?" + date;
        }

    }
</script>
</html>

CodeServlet.java

@WebServlet("/codeServlet")
public class CodeServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int width = 100;
        int height = 50;

        //1.创建一对象,在内存中图片(验证码图片对象)
        BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);


        //2.美化图片
        //2.1 填充背景色
        Graphics g = image.getGraphics();//画笔对象
        g.setColor(Color.PINK);//设置画笔颜色
        g.fillRect(0,0,width,height);

        //2.2画边框
        g.setColor(Color.BLUE);
        g.drawRect(0,0,width - 1,height - 1);

        String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz0123456789";
        //生成随机角标
        Random ran = new Random();

        for (int i = 1; i <= 4; i++) {
            int index = ran.nextInt(str.length());
            //获取字符
            char ch = str.charAt(index);//随机字符
            //2.3写验证码
            g.drawString(ch+"",width/5*i,height/2);
        }


        //2.4画干扰线
        g.setColor(Color.GREEN);

        //随机生成坐标点

        for (int i = 0; i < 10; i++) {
            int x1 = ran.nextInt(width);
            int x2 = ran.nextInt(width);

            int y1 = ran.nextInt(height);
            int y2 = ran.nextInt(height);
            g.drawLine(x1,y1,x2,y2);
        }


        //3.将图片输出到页面展示
        ImageIO.write(image,"jpg",response.getOutputStream());
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

说下在请求路径后面加时间参数的问题,如果不加任何参数,把src值设为"/demo/codeServlet",那么图片资源只能被更新一次,因为就会被浏览器缓存下来,再刷新也不会显示新的验证码,之所以时间作为参数,是因为时间是不断向前的,不会出现被一直缓存的情况,即使是随机数也可能出现某两次相同的情况,而不能刷新得到新的验证码。

6.3 ServletContext对象

6.3.1 概念

代表整个web应用,可以和程序的容器(服务器)来通信。

6.3.2 获取该对象的方法

  • 1. 通过request对象获取:request.getServletContext();
  • 2. 通过HttpServlet获取:this.getServletContext();

代码实例:

@WebServlet("/demo4")
public class DemoServlet4 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取ServletContext对象的方式
        ServletContext servletContext1 = request.getServletContext();
        ServletContext servletContext2 = this.getServletContext();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

6.3.3 功能

  • 1. 获取MIME类型:
    • MIME类型:在互联网通信过程中定义的一种文件数据类型
      • 格式: 大类型/小类型   如:text/html、image/jpeg
    • 获取:String getMimeType(String file)  。在tomcat下的conf下的web.xml中有这些对应的映射。
  • 2. 域对象:共享数据(ServletContext对象范围:所有用户所有请求的数据)
    • 1. setAttribute(String name,Object value)
    • 2. getAttribute(String name)
    • 3. removeAttribute(String name)
  • 3. 获取文件的真实(服务器)路径
    • 访问web目录下的资源
      • String b = context.getRealPath("/b.txt");
    • 访问WEB-INF目录下的资源
      • String c = context.getRealPath("/WEB-INF/c.txt");
    • 访问src目录下的资源
      • String a = context.getRealPath("/WEB-INF/classes/a.txt");

获取MIME类型的代码实例:

DemoServelt5.java

@WebServlet("/demo5")
public class DemoServlet5 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext servletContext = request.getServletContext();
        String mimeType = servletContext.getMimeType("a.html");
        System.out.println(mimeType);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

可以在自己配置的tomcat下的conf下的web.xml查看

域对象共享数据的代码实例:

DemoServlet6_1.java

@WebServlet("/demo6_1")
public class DemoServlet6_1 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext servletContext = request.getServletContext();
        servletContext.setAttribute("msg","Hello ServletContext");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

DemoServlet6_2.java

@WebServlet("/demo6_2")
public class DemoServlet6_2 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取ServletContext对象
        ServletContext servletContext = request.getServletContext();
        // 通过getAttribute(name)方法获取存储的值
        Object msg = servletContext.getAttribute("msg");
        System.out.println(msg);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

获取文件真实路径的代码实例:

a.txt、b.txt、c.txt文件所处位置:

@WebServlet("/demo7")
public class DemoServlet7 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // ServletContext对象
        ServletContext servletContext = request.getServletContext();

        // 1.访问web目录下的资源
        String realPath1 = servletContext.getRealPath("/b.txt");
        System.out.println(realPath1);

        // 2.访问WEB-INF目录下的资源
        String realPath2 = servletContext.getRealPath("/WEB-INF/c.txt");
        System.out.println(realPath2);

        // 3.访问src目录下的资源
        String realPath3 = servletContext.getRealPath("/WEB-INF/classes/a.txt");
        System.out.println(realPath3);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

6.4 案例之文件下载

6.4.1 文件下载需求

  • 1. 页面显示超链接
  • 2. 点击超链接后弹出下载提示框
  • 3. 完成图片文件下载

6.4.2 分析

    1. 超链接指向的资源如果能够被浏览器解析,则在浏览器中展示,如果不能解析,则弹出下载提示框。不满足需求
    2. 任何资源都必须弹出下载提示框
    3. 使用响应头设置资源的打开方式:
        * content-disposition:attachment;filename=xxx

6.4.3 代码实现

步骤:
    1. 定义页面,编辑超链接href属性,指向Servlet,传递资源名称filename
    2. 定义Servlet
        1. 获取文件名称
        2. 使用字节输入流加载文件进内存
        3. 指定response的响应头: content-disposition:attachment;filename=xxx
        4. 将数据写出到response输出流

问题:
    中文文件问题
        解决思路:
            1. 获取客户端使用的浏览器版本信息
            2. 根据不同的版本信息,设置filename的编码方式不同

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>文件下载</title>
  </head>
  <body>
  <img src="/img/kyz.jpg" alt="鷇音子" width="500" height="400">
  <a href="/downloadServlet?fileName=kyz.jpg">下载</a>
  <br>
  <img src="/img/素还真.jpg" alt="素还真" width="500" height="400">
  <a href="/downloadServlet?fileName=素还真.jpg">下载</a>
  </body>
</html>

DownloadServlet.java

@WebServlet("/downloadServlet")
public class DownloadServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        //  1. 获取文件名称
        String fileName = request.getParameter("fileName");

        //  2. 使用字节输入流加载文件进内存
        // 2.1 获取文件在服务器端的路径
        String path = request.getServletContext().getRealPath("/img/" + fileName);
        FileInputStream fis=new FileInputStream(new File(path));

        // 3.设置response的响应头
        // 3.1设置响应头类型:content-type
        response.setHeader("content-type",request.getServletContext().getMimeType(fileName));
        //解决中文文件名问题
        // 1.获取user-agent请求头、
        String agent = request.getHeader("user-agent");// 能够判断是什么浏览器打开的
        // 2.使用工具类方法编码文件名即可
        fileName = getFileName(agent, fileName);
        // 3.2设置响应头打开方式:content-disposition
        response.setHeader("content-disposition","attachment;filename="+fileName);

        // 4.将输入流的数据写出到输出流中
        ServletOutputStream sos = response.getOutputStream();
        byte[] buff = new byte[1024 * 8];
        int len = 0;
        while((len = fis.read(buff)) != -1){
            sos.write(buff,0,len);
        }
        fis.close();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    public static String getFileName(String agent, String filename) throws UnsupportedEncodingException, UnsupportedEncodingException {
        // 根据不同的user-agent来判断是什么浏览器,来对应处理编码问题
        if (agent.contains("MSIE")) {
            // IE浏览器
            filename = URLEncoder.encode(filename, "utf-8");
            filename = filename.replace("+", " ");
        } else if (agent.contains("Firefox")) {
            // 火狐浏览器
            BASE64Encoder base64Encoder = new BASE64Encoder();
            filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
        } else {
            // 其它浏览器
            filename = URLEncoder.encode(filename, "utf-8");
        }
        return filename;
    }
}

 


 

7. 会话技术

7.1 概述

  • 1. 会话:一次会话中包含多次请求和响应。
    • 一次会话:浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开为止
  • 2. 功能:在一次会话的范围内的多次请求间,共享数据
  • 3. 方式:
    • 1. 客户端会话技术:Cookie
    • 2. 服务器端会话技术:Session

7.2 Cookie

7.2.1 概念

Cookie:客户端会话技术,将数据保存到客户端。

7.2.2 快速入门

使用步骤:

  • 1. 创建Cookie对象,绑定数据:new Cookie(String name, String value)
  • 2. 发送Cookie对象:response.addCookie(Cookie cookie)
  • 3. 获取Cookie,拿到数据:Cookie[]  request.getCookies() 

7.2.3 实现原理

  • (1)客户端浏览器向服务器发送请求,如果有需要,服务器就将数据保存到cookie并通过响应头随着响应客户端传到客户端浏览器保存。
  • (2)此时客户端浏览器就已经有了cookie,又是一次新的请求,应该说以后每一次新的请求就将cookie放到请求头中随着请求传输到服务器,服务端就可以读取到该cookie。

我们创建CookieServlet1.java和CookieServlet2.java

@WebServlet("/cookie1")
public class CookieServlet1 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 创建Cookie对象
        Cookie cookie=new Cookie("msg","hello");
        // 2. 发送Cookie对象
        response.addCookie(cookie);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}
@WebServlet("/cookie2")
public class CookieServlet2 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 3. 获取Cookie
        Cookie[] cookies = request.getCookies();
        for (Cookie cookie : cookies) {
            String name = cookie.getName();
            String value = cookie.getValue();
            System.out.println(name+"="+value);
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

输入http://localhost:8080/cookie1后发现响应头有了set-cookie:msg=hello。即表示当服务器在接收到请求后根据代码把cookie又响应到浏览器端了。

输入http://localhost:8080/cookie2后发现请求头中带的cookie中有"msg=hello"。

同时控制台还有结果打印:

7.2.4 cookie细节

1. 一次可不可以发送多个cookie?

可以。可以创建多个Cookie对象,使用response调用多次addCookie方法发送cookie即可。

代码实例:

@WebServlet("/cookie3")
public class CookieServlet3 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Cookie cookie1=new Cookie("username","zhangsan");
        Cookie cookie2=new Cookie("password","123456");
        response.addCookie(cookie1);
        response.addCookie(cookie2);

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

2. cookie在浏览器中保存多长时间?

(1)默认情况下,当浏览器关闭后,Cookie数据被销毁。

(2)持久化存储:setMaxAge(int seconds),参数表示秒数

①. 正数:将Cookie数据写到硬盘的文件中。持久化存储。并指定cookie存活时间,时间到后,cookie文件自动失效

②. 负数:默认值

③. 零:删除cookie信息

@WebServlet("/cookie4")
public class CookieServlet4 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Cookie cookie =new Cookie("tempInfo","save time....");
        cookie.setMaxAge(20);// 存活20秒
        response.addCookie(cookie);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

3. cookie能不能存中文?

  • 在tomcat 8 之前 cookie中不能直接存储中文数据。需要将中文数据转码---一般采用URL编码(%E3)
  • 在tomcat 8 之后,cookie支持中文数据。特殊字符还是不支持,建议使用URL编码存储,URL解码解析

CookieServlet5_1.java

@WebServlet("/cookie5_1")
public class CookieServlet5_1 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // URLEncoder.encode()编码
        String encodeString = URLEncoder.encode("中文信息","utf-8");
        Cookie cookie=new Cookie("chinese",encodeString);
        response.addCookie(cookie);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

CookieServlet5_2.java

@WebServlet("/cookie5_2")
public class CookieServlet5_2 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Cookie[] cookies = request.getCookies();
        for (Cookie cookie : cookies) {
            if ("chinese".equals(cookie.getName())) {
                String value = cookie.getValue();
                // 解码前打印
                System.out.println(value);
                // URLDecoder.decode()解码
                value = URLDecoder.decode(value,"gbk");
                System.out.println(value);
            }
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

4. cookie共享问题?

1. 假设在一个tomcat服务器中,部署了多个web项目,那么在这些web项目中cookie能不能共享?

  • 默认情况下cookie不能共享
  • setPath(String path):设置cookie的获取范围。默认情况下,设置当前的虚拟目录。如果要共享,则可以将path设置为"/"

2. 不同的tomcat服务器间cookie共享问题?

  •  setDomain(String path):如果设置一级域名相同,那么多个服务器之间cookie可以共享。例如:setDomain(".baidu.com"),那么tieba.baidu.com和news.baidu.com中cookie可以共享

7.2.5 Cookie的特点和作用

cookie的特点:

  • 1. cookie存储数据在客户端浏览器
  • 2. 浏览器对于单个cookie 的大小有限制(4kb) 以及 对同一个域名下的总cookie数量也有限制(20个)

cookie的作用:

  • 1. cookie一般用于存出少量的不太敏感的数据(因为cookie是保存在客户端浏览器的,不太安全)
  • 2. 在不登录的情况下,完成服务器对客户端的身份识别(例如实现‘记住我’功能)

7.2.6 案例-记住上一次访问时间

1. 需求:
        1. 访问一个Servlet,如果是第一次访问,则提示:您好,欢迎您首次访问。
        2. 如果不是第一次访问,则提示:欢迎回来,您上次访问时间为:显示时间字符串

2. 分析:
        1. 可以采用Cookie来完成
        2. 在服务器中的Servlet判断是否有一个名为lastTime的cookie
            1. 有:不是第一次访问
                1. 响应数据:欢迎回来,您上次访问时间为:2018年6月10日11:50:20
                2. 写回Cookie:lastTime=2018年6月10日11:50:01
            2. 没有:是第一次访问
                1. 响应数据:您好,欢迎您首次访问
                2. 写回Cookie:lastTime=2018年6月10日11:50:01

3. 代码实例(CookieDemoServlet.java):

@WebServlet("/cookieDemo")
public class CookieDemoServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置响应的消息体的数据格式以及编码
        response.setContentType("text/html;charset=utf-8");

        //1.获取所有Cookie
        Cookie[] cookies = request.getCookies();
        boolean flag = false;//没有cookie为lastTime
        //2.遍历cookie数组
        if (cookies != null && cookies.length > 0) {
            for (Cookie cookie : cookies) {
                //3.获取cookie的名称
                String name = cookie.getName();
                //4.判断名称是否是:lastTime
                if ("lastTime".equals(name)) {
                    //有该Cookie,不是第一次访问

                    flag = true;//有lastTime的cookie

                    //设置Cookie的value
                    //获取当前时间的字符串,重新设置Cookie的值,重新发送cookie
                    Date date = new Date();
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
                    String str_date = sdf.format(date);
                    System.out.println("编码前:" + str_date);
                    //URL编码
                    str_date = URLEncoder.encode(str_date, "utf-8");
                    System.out.println("编码后:" + str_date);
                    cookie.setValue(str_date);
                    //设置cookie的存活时间
                    cookie.setMaxAge(60 * 60 * 24 * 30);//一个月
                    response.addCookie(cookie);

                    //响应数据
                    //获取Cookie的value,时间
                    String value = cookie.getValue();
                    System.out.println("解码前:" + value);
                    //URL解码:
                    value = URLDecoder.decode(value, "utf-8");
                    System.out.println("解码后:" + value);
                    response.getWriter().write("<h1>欢迎回来,您上次访问时间为:" + value + "</h1>");
                    break;
                }
            }
        }


        if (cookies == null || cookies.length == 0 || flag == false) {
            //没有,第一次访问

            //设置Cookie的value
            //获取当前时间的字符串,重新设置Cookie的值,重新发送cookie
            Date date = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
            String str_date = sdf.format(date);
            System.out.println("编码前:" + str_date);
            //URL编码
            str_date = URLEncoder.encode(str_date, "utf-8");
            System.out.println("编码后:" + str_date);

            Cookie cookie = new Cookie("lastTime", str_date);
            //设置cookie的存活时间
            cookie.setMaxAge(60 * 60 * 24 * 30);//一个月
            response.addCookie(cookie);

            response.getWriter().write("<h1>您好,欢迎您首次访问</h1>");
        }

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

7.3 Session

7.3.1 概念

session是服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中。

7.3.2 快速入门

可以通过HttpSession对象来共享数据。

  • 1. 获取HttpSession对象:
    • HttpSession session = request.getSession();
  • 2. 使用HttpSession对象:
    • Object getAttribute(String name)  :获取名为name的session数据
    • void setAttribute(String name, Object value):设置session数据
    • void removeAttribute(String name) :清除指定session

7.3.3 原理

Session的实现是依赖于Cookie。

下面用两个Servlet.java类演示:

SessionDemo1.java

@WebServlet("/session1")
public class SessionDemo1 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取session
        HttpSession session = request.getSession();
        System.out.println("SessionDemo1 id: "+session.getId());
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

SessionDemo2.java

@WebServlet("/session2")
public class SessionDemo2 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取session
        HttpSession session = request.getSession();
        System.out.println("SessionDemo2 id: " + session.getId());
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

然后运行tomcat,输入http://localhost:8080/session1

可以在浏览器的抓包工具面板和IDEA的控制台看到如下信息:

即服务端在内存中创建了一个Session对象,该对象有一个ID,然后在响应客户端浏览器时作为响应头传输过去。

接着在浏览器地址栏输入:http://localhost:8080/session2

观察浏览器抓包工具该请求的请求头与IDEA控制台打印结果

可以发现在发送这一次请求时将保存在cookie中的JSESSIONID作为请求头数据又传输到服务器端了,所以SessionDemo2.java打印的id是同一个,因此SessionDemo1.java和SessionDemo2.java中的session对象是同一个,就可以共享数据。

7.3.4 细节

1. 当客户端关闭后,服务器不关闭,两次获取session是否为同一个?

  • 默认情况下。不是。(为什么?因为默认情况下,当浏览器关闭后,Cookie数据被销毁,而session实现是基于cookie的。)
  • 如果需要相同,则可以创建Cookie,键为JSESSIONID,设置最大存活时间,让cookie持久化保存。
Cookie c = new Cookie("JSESSIONID",session.getId());
c.setMaxAge(60*60);
response.addCookie(c);

代码实例演示:

还是上面两个代码,这次先输入http://localhost:8080/session1然后关闭浏览器,关闭后再打开浏览器,输入http://localhost:8080/session2查看控制台打印结果

发现两次获取的session不是同一个了。

将SessionDemo1.java改为如下:

@WebServlet("/session1")
public class SessionDemo1 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取session
        HttpSession session = request.getSession();
        Cookie cookie=new Cookie("JSESSIONID",session.getId());
        cookie.setMaxAge(60*60);// 保存一小时
        response.addCookie(cookie);
        System.out.println("SessionDemo1 id: "+session.getId());
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

再次重复上面的试验,这次发现IDEA控制台打印结果一致了。

2. 客户端不关闭,服务器关闭后,两次获取的session是同一个吗?

  • 不是同一个,但是要确保数据不丢失。tomcat自动完成以下工作,但IDEA无法完成,因为idea会自动删除work目录
    • session的钝化:在服务器正常关闭之前,将session对象系列化到硬盘上
    • session的活化:在服务器启动后,将session文件转化为内存中的session对象即可。

代码实例:

还是上面的两段代码,运行输入http://localhost:8080/session1控制台打印如下:

复制下面的目录,等会会用到,这是idea产生的目录,并不是本地tomcat的部署目录。

去到刚才复制的目录下,进入到work目录下

里面的目录并没有任何文件,不过有空文件夹,因为session的钝化发生在服务器正常关闭之前,将session对象系列化到硬盘上,因此将服务器正常关闭。

然后再进入到ROOT目录下,会发现生成的SESSION文件

用记事本打开,尽管都是乱码,不过还是发现了保存下来的session的id

当再次启动项目时会自动删除work目录,重新生成

打开新生成的work目录就是一个空的,没有保存下来的session文件了,所以获取的不是同一个session了。

但如果是本地的tomcat则不会删除,会自动完成。

将IDEA生成的下面这个目录复制到tomcat的webapps下,并重命名demo13

去bin目录下启动tomcat,然后在浏览器输入http://localhost:8080/demo13/session1,黑框会输出如下:

在未正常关闭tomcat前,work目录内是没有文件的,但有一些文件夹

现在正常关闭tomcat

再去work目录,demo13就有生成的session文件了

打开查看,和上面控制台输出的一致

现在又重启tomcat,输入http://localhost:8080/demo13/session2,查看控制台打印

两次输入是同一个session对象,虽然work目录没有了这个文件,因为再次被读取了。
3. session什么时候被销毁?

  • 1. 服务器关闭
  • 2. session对象调用invalidate() 。
  • 3. session默认失效时间 30分钟,可以选择修改配置文件web.xml文件   
    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>

7.3.5 session特点

  • 1. session用于存储一次会话的多次请求的数据,存在服务器端
  • 2. session可以存储任意类型,任意大小的数据

session与Cookie的区别:

  • 1. session存储数据在服务器端,Cookie在客户端
  • 2. session没有数据大小限制,Cookie有
  • 3. session数据安全,Cookie相对于不安全

 

 

  • 3
    点赞
  • 0
    评论
  • 13
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:程序猿惹谁了 设计师:我叫白小胖 返回首页

打赏作者

二木成林

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值