Tomcat&Servlet

WEB概念回顾:

  1. 软件架构

    1. C/S:客户端/服务器端
    2. B/S:浏览器/服务器端
  2. 资源分类

    1. 静态资源:所有用户访问后,得到的结果都是一样的,称为静态资源.静态资源可以直接被浏览器解析。
      如: html,css,JavaScript
    2. 动态资源:每个用户访问相同资源后,得到的结果可能不一样。称为动态资源。动态资源被访问后,需要先转换为静态资源,在返回给浏览器。
      如:servlet/jsp,php,asp....
  3. 网络通信三要素

    1. IP:电子设备(计算机)在网络中的唯一标识。
    2. 端口:应用程序在计算机中的唯一标识。 0~65536
    3. 传输协议:规定了数据传输的规则

      基础协议:

      1. tcp:安全协议,三次握手。 速度稍慢
      2. udp:不安全协议。 速度快

WEB服务器软件概述:

  • 服务器:软件+硬件 安装了服务器软件的计算机
  • 服务器软件:具有软件产品 接口用户的一些请求,处理用户请求,给用户返回结果。
  • web服务器软件:一般用于网站的服务器,可以处理浏览器客户端一些请求并返回一些响应。目的就是让网络种的任何用户都可以访问服务器中部署的一些web资源。Apache Niginx
  • web容器:安装服务器软件
  • 常见的Java相关的web服务器软件
    • tomcat :免费,开源,服务中小型项目,支持一部分的Java规范:Servlet/JSP。使用用户群体非常的大,范围很广,知名度很高。
    • weblogic:Oracle公司 服务器与大型Java EE项目。支持所有的Java EE规范。
    • webSphere:IBM公司 服务与大型Java EE项目。支持所有的Java EE规范。
    • Jboss:Jboss公司 服务与大型Java EE项目。支持所有的Java EE规范。

Java EE:规范13项

  • JDBC,JNDI、EJB、RMI、JSP、Servlet、XML、JMS、Java IDL、JTS、JTA、JavaMail、JAF
  1. JDBC规范(Java Database Connectivity):

主要是对关系型数据库提供统一的访问方式。针对不同的数据库只使用一套代码即可。

  1. JNDI(Java Naming Directory Interface):

Java命名和目录接口,提供了一套统一可以在网络中查找和访问服务的方式。通过指定一个服务器,该名称对于数据库或者命名服务中的一个记录。

在DataSource中可以事先建立多个数据库连接,保存在数据库连接池中。当程序中需要连接时从数据库连接池中取出一个空闲状态的数据库连接即可。

  1. EJB(Enterprise JavaBean):

一套服务器组件模型:包含有四种状态的对象类型:无状态会话bean,有状态会话bean,实体bean,消息bean。

  1. RMI(RemoteMethod Invoke):

远程方法调用:它能够在一台计算机中去访问另一台计算机中的应用(方法)

  1. JSP(Java Server Page)

Java服务器页面,是一个动态的内容模板,jsp可以使用servlet提供api方法。一般情况下需要和JavaBean结合使用。从而可以将页面表现和业务逻辑分离。

  1. Servlet(Java Servlet Applet)

使用Java语言编写的运行在服务器端的小程序。扩展了web服务器的功能。

  1. XML(Extensible Markup Language)

是一种可扩展的标记语言。被用来在不同的业务中实现共享数据的。它主要用于数据传输的。

  1. JMD(Java Message Service)

jms是Java消息服务,它主要应用在不同的客户端中实现异步的消息传输。也是一个和Java平台无关的面向消息服务的中间件。

  1. Java IDL(Interface Description Language)

Java接口定义语言 描述软件组件的一种计算机语言。

  1. Java Mail

Java Mail 是用于存取邮件服务的器的 支持Smtp服务 也支持Imap服务还支持POP服务

  1. JTA(Java Transaction API)

Java事物api 保证用户操作的acid(原子,一致,隔离,持久)属性。

  1. JAF(Java Activities Framework)

是Java中一套关于处理数据的框架。

  1. JTS(Java Transaction Service)

Java 事物服务,是一套组件进行事物监听的。


Tomcat:web服务器

  1. 安装:找到你需要用的TomCat版对应的zip压缩包,解压到需要安装的目录即可。
    安装
    配置 环境 utf-8
  2. 目录介绍
    目录名 描述
    bin 专门用来存放Tomcat服务器的可执行程序。
    conf 专门用来存放Tomcat服务器的配置文件。
    lib 专门用来存放Tomcat服务器的jar包。
    logs 专门用来存放Tomcat服务器的运行时输出的日记信息。
    temp 专门用来存放Tomcat服务器运行产生的临时数据。
    webapps 专门用来存放部署的web工程。
    work 是Tomcat工作时的目录,用来存放Tomcat运行时jsp翻译为Servlet的源码,和Session钝化的目录。
  3. 如果启动Tomcat服务器
    • 找到Tomcat目录下的bin目录下的startup.bat文件,双击,就可以启动Tomcat服务器。
      如何测试Tomcat服务启动成功??
      打开浏览器,在浏览器地址栏中输入以下地址测试:

      1. -->http://localhost:8080
      2. -->http://127.0.0.1:8080
      3. -->http://真实 ip:8080
        如果显示页面,说明Tomcat服务启动成功。
    • 常见的启动失败的情况有,双击startup.bat文件,就会出现一个小黑窗口一闪而过。说明没有配置好环境变量。

    • 配置环境变量:

      此电脑右键属性 --》 高级系统设置 --》环境变量 --》新建环境变量:变量名:CATALINA_HOME,变量值:Tomcat的安装位置不包含bin目录 --》 path环境配置:%CATALINA_HOME%in

      • 常见Java_HOME 配置错误有以下几种情况:
        -. Java_HOME 必须全大写。
        • Java_HOME 中间必须是下划线,不是减号。
        • Java_HOME 配置的路径只需要配置到jdk的安装目录即可。不需要带上bin目录。
    • 启动报错

      1. 暴力:找到占用的端口号,并且找到对应的进程,杀死该进程
        netstat -ano //查看端口
      2. 温柔:修改自身的端口号
        在conf/server.xml
    • 另一种启动Tomcat 服务的方式

      1. 打开命令行
      2. cd到你的Tomcat的bin目录下
      3. 敲入启动命令:catalina run
  4. Tomcat的停止
    1. 点击 tomcat 服务器窗口的 x 关闭按钮
    2. 把 Tomcat 服务器窗口置为当前窗口,然后按快捷键 Ctrl+C
    3. 找到Tomcat 的bin 目录下的shutdown.bat 双击,就可以停止Tomcat 服务器
  5. 如何修改Tomcat的端口号
    mysql默认的端口号是:3306
    Tomcat默认的端口号是:8080
    找到Tomcat目录下的conf目录,找到server.xml配置文件。
    image
    平时上百度:http://www.baidu.com:80
    HTTP协议默认的端口号是:80
  6. 如何部署web工程到Tomcat中
  • 第一种部署方法:只需要把web工程的目录拷贝到Tomcat的Webapps目录下即可
    1. 在webapps目录下创建一个工程。
    2. 写入需要的html文件。
    3. 在浏览器中输入访问地址格式:
      http://ip:port/工程名/目录下/文件名
  • 第二种部署方法
    • 找到Tomcat下的conf目录Catalinalocalhost 下创建abc.xml配置文件:
    • 配置文件内容如下:
      <!-- Context 表示一个工程上下文
          path 表示工程的访问路径:/abc  "/"--》后面根的是虚拟目录,如果后面无信息代表虚拟目录,直接访问web目录
          docBase 表示你的工程目录在哪里
      -->
      <context path="/abc" docbase="E:ook">
      
      访问这个工程的路径如下::http://ip:port/abc/ 就表示访问E:ook 目录
  • 第三种部署方法
    • 配置conf/server.xml文件
      <!-- 在Host标签体中配置
      	docBase:项目存放的路径
      	path:虚拟目录
      -->
      <context docbase="D:hello" path="/hehe">
      
  1. 手托html页面到浏览器在浏览器中输入http://ip:端口号/工程名/王访问的区别

    1. 手托页面的原理:
      image
    2. 输入访问地址的原因:
      image
  2. Root的工程的访问,以及默认index.html页面的访问

    • 当我们在浏览器地址栏中输入访问地址如下:

      http://ip:port/     -->没有工程名的时候,默认访问的是root工程
      
    • 当我们在浏览器地址栏中输入访问地址如下:

      http://ip:port/工程名/  --> 没有资源,默认访问index.html页面
      

Servlet

1.概念

  • Server Applet 运行在服务端的小程序使用Java语言编写的运行在服务端的小程序接口。在接口中定义的有很多具体的标准 ----》抽象方法

  • 它是作为浏览器客户端和服务器端上的数据库或者程序之间的中间层,起到一个桥梁作用,使用Servlet可以做到获取浏览器客户端中的表单数据和客户端发送的一些请求信息,关联后端数据昨晚业务处理,把处理后的结果值在返回给浏览器客户端展示。

2.步骤

  1. 先部署当前项目到tomcat服务中
  2. 让浏览器客户端找到项目中对外提供的某个服务,路径映射 url-pattern
  3. 启动浏览器tomcat服务器

快速入门:

import javax.servlet.*;
import java.io.IOException;
//实现一个类变成一个Servlet类需要遵从Servlet规范
//servlet是用于连接客户端和服务端的中间层 tomcat
public class ServletDemo1 implements Servlet {
    //init() 初始化当前Servlet类的信息  该类被初始化的时候只会被调用一次
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("给类已经被初始化了。。。");
    }

    //getServletConfig() 获取当前servlet配置的信息
    @Override
    public ServletConfig getServletConfig() { return null;}

    //service() 当前servlet类提供的具体服务  每次请求调用一次
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        //请求该服务,让页面显示一句话“请求成功”
//        System.out.println("");//在编辑工具console窗口中
        //数据传输 使用流 打印流 打印到客户端中
        servletResponse.getWriter().write("hello word");
    }

    //getServletInfo() 获取当前servlet类的具体信息
    @Override
    public String getServletInfo() {return null;}

    //destroy() 销毁当前创建servlet类对象信息  当servlet对象被销毁的时候被调用到
    @Override
    public void destroy() { }
}
<!--?xml version="1.0" encoding="UTF-8"?-->
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">

    <!--在web.xml中注册服务  注册目的映射浏览器映射浏览器客户端中的url地址 具体的某个服务请求-->
    
	 <!--servlet标签给Tomcat配置Servlet程序-->
    <servlet>
        <!--servlet-name标签 Servlet程序起一个别名(一般是类名)-->
        <servlet-name>servletDemo1</servlet-name>
        <!--servlet-class是Servlet程序的全类名-->
        <servlet-class>com.zhiyou100.demo1.ServletDemo1</servlet-class>
         <!--load-on-startup手动指定正数值越小越先加载-->
        <!-- <load-on-startup>1</load-on-startup>-->
    </servlet>
    
    <!--servlet-mapping标签给Servlet程序配置访问地址-->
    <servlet-mapping>
        <!--servlet-name标签的作用是告诉服务器,我配置的地址给哪个Servlet程序使用-->
        <servlet-name>servletDemo1</servlet-name>
        <!--url-pattern标签配置访问地址
		/斜杠在服务器解析的时候,表示地址为:http://in:port/工程路径
		/aa 表示地址为:http://ip:port/工程路径/hello
		-->
        <url-pattern>/aa</url-pattern>
    </servlet-mapping>

    
    <!--
    对于tomcat服务器来说,默认打开的页面是以index开启的页面 index.html/index.jsp
    这个叫欢迎页面:使用tomcat默认指定的,也可以手动指定
    <welcome-file-list> 欢迎界面列表
    一般也不指定,使用tomcat默认的欢迎界面,一般都是以index开头的欢迎页面
    -->
    <welcome-file-list>
        <welcome-file>/admin.html</welcome-file><!--如果第一个没有找到则向下找像cath-->
        <!--        <welcome-file>/admin.html</welcome-file>
                <welcome-file>/admin.html</welcome-file>-->
    </welcome-file-list>
</web-app>

3.执行原理

Servlet的内部实现原理:

  • Servlet是sun提供的一套规范

  • servlet规范:包含三个技术点:servlet技术,filter(过滤器)技术,listener(监听器)技术

  • 原理:
    当一个客户端发送一个请求过来时,tomcat会先解析请求的url路径,在web.xml文件中去找对应的路径<url-pattern></url-pattern>,如果找到了,就会读取<servlet-name></servlet-name>的名字,通过servlet-name找到对应的服务类,记载该服务信息,tomcat会将该类字节码文件加载内存并创建,通过servlet()方法,执行service()方法中的内容,如果该服务是第一次请求,那么会先调用init()方法,初始化该类对象的信息,然后在调用service()方法。

4.生命周期(方法)

  1. 执行servlet构造器方法

  2. 执行init初始化方法(只会执行一次,加载资源)

    • 默认情况下:第一二步,是在第一次访问的时候创建servlet程序会调用。
    • 手动指定Servlet
      通过<servlet> 标签中配置 <load-on-startup>标签
      • 第一次访问时被创建出来。
        <load-on-startup>标签的值为负数

      • 在服务器启动的时候,被创建出来
        <load-on-startup>的值为0或正整数。

        注意:
        <load-on-startup> 是用来标记容器是否在启动的时候就加载这个Servlet(实例化并执行init方法)
        <load-on-startup>它的值必须是一个整数,表示这个Servlet类的加载顺序。
        当值为>=0 的时候,表示容器应用启动的时候就加载并初始化这个Servlet类
        当值为<0的时候,表示该Servlet类被选择的时候才加载。
        正数值越小,表示该Servlet类在容器中的优先级越高,应用启动的时候就越先加载。
        当值相同的时候,容器自己选择一个Servlet去加载。

        备注:

        • Servlet接口中的init方法,只会执行一次,说明一个Servlet在内存当中只会存在一个对象。

          Servlet是单例的。

          • 侠义:一个类在内存当中有且仅有一个类对象,而且这个类对象自始至终都是同一个对象

            做法:把该类的构造器私有化,new的动作只在类内部使用。

          • 广义:只要满足在整个系统中或者应用中有且仅有一个实例即可。(不保证同一个)

          • 在Servlet 3.1 规范中,当Servlet类没有实现一个SingleThreadModel接口的时候Servlet类才是单例的。

            如果实现给接口,那么每次请求该Servlet类的时候,容器都会创建一个新的Servlet类对象

        面试题:Servlet容器如何保证在多线程环境下访问数据的安全性问题?
        Servlet容器默认情况下采用多线程处理多个请求。
        ​当web容器启动的时候(或者客户端发送请求的时候),Servlet就会被加载并实例化。
        ​容器初始化Servlet主要就是读取配置文件(web.xml),在tomcat容器中也有一个web.xml,在servlet.xml中可以设定线程中的数目,初始化线程池通过web.xml。
        ​当客户端请求到达时,Servlet容器可以通过线程调度它管理下的线程池中的等待执行的线程。
        ​线程执行Servlet类中的sevice方法。
        ​当线程执行完毕,归还线程到线程中。
        ​当多个用户访问同一个Servlet类的时候,可能存在线程安全问题。
        ​做法:不要再Servlet中定义成员变量,如果定义了,也不要修改成员变量

  3. 执行service方法

    第三步,每次访问都会调用。

  4. 执行destroy销毁方法
    第四步,Servlet被销毁时执行。服务器关闭时(正常关闭),Servlet被销毁.destory方法在 Servlet被销毁之前执行,一般用于释放资源。

其他方法
ServletConfig getServletConfig();获取 ServletConfig(Servlet的配置对象)对象
String getServletInfo(); 获取 Servlet的一些信息,版本,作者等等。

5.Servlet3.0 注解配置

  • 好处:不需要再到web.xml文件中去注册Servlet类信息

  • 步骤:

    1. 创建一个Servlet类,选择Serrvlet3.0版本以上

    2. 在该Servlet类上面添加@WebServlet("url")

    3. 在该注解中配置客户端url请求资源路径

    4. @WebServlet("url/请求路径")

      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>
      }
      

6.Servlet的体系结构

image

Servlet -- 接口
	↓
GenericServlet -- 抽象类
	↓
HttpServlet  -- 抽象类

GenericServlet:将Servlet接口中其他的方法做了默认空实现,只将service()方法作为抽象
 	将来定义Servlet类时,可以继承GenericServlet,实现service()方法即可

 HttpServlet:对http协议的一种封装,简化操作
  1. 定义类继承HttpServlet
  2. 复写doGet/doPost方法

7.Servlet相关配置

  1. urlpartten:Servlet访问路径
    1. 一个Servlet可以定义多个访问路径 : @WebServlet({"/d4","/dd4","/ddd4"})
    2. 路径定义规则:
      1. /xxx:路径匹配
      2. /xxx/xxx:多层路径,目录结构
      3. *.do / *.action 扩展名匹配一般要写具体的哪一个服务:如 userEdit.do
      4. / 缺省配置(当访问web应用中的资源(Servlet)都不匹配,此时会找缺省配置的Servlet信息,让这个缺省配置的Servlet来处理这个请求),当前这个Servlet它能够处理资源包括Servlet和静态资源,jsp资源解决不了
      5. * 全部处理,处理包括静态资源和jsp资源 只能借助与Servlet中方式访问:内部转发和重定向

欢迎界面

  • 当我们向浏览器输入我们服务器地址,路径url格式:http://localhost:8080/day48 此时tomcat会自动按照在we.xml文件中配置的 列表从上到下一次查找对应的web资源,如果全部找完发现没有对用的资源,服务器给浏览器端返回一个404状态码(web资源在服务器中没有找到),表示web资源路径不匹配。如果找打了,就是我们需要的欢迎界面。

    注意:

    • 访问的是当前web应用根目录下的web文件资源,一般情况下我们访问的都是web文件夹下面道德资源
    • 有一个文件下面的资源直接通过浏览器访问不到 WEB-INF(安全目录,保护目录)
    • 只能通过服务器内部来访问
    • 标签内部配置的欢迎界面一种服务器行为,在浏览器客户端上直接访问web-inf 里面的资源不可行。

Http协议

概念:

  • Hyper Text Transfer Protocol 超文本传输协议
    是互联网应用最广泛的一种协议

  • 传输协议:定义了,客户端和服务端通信时,发送数据的格式
    image

  • 特点:

    1. 基于TCP/IP的高级协议
    2. 默认端口号:80
    3. 基于请求/响应的:一次请求对应一次响应
    4. 无状态:每次请求之间相互独立,不能交互数据
  • 历史版本:

    • 1.0 每一次请求响应都需要建立一次连接,请求响应结束后随着断开,

    • 1.1 (版本最大变化)持久连接。

      1.1版本作为目前主流的版本:请求响应结束后,默认情况下是不断开的,可以被多个请求所复用不用再声明:Connection:keep-alive。

      客户端和服务器端发现对方与一段时间活动,就会主动关闭连接,。如果在客户端发送最后一个请求时,这个请求发送了 Connection:close,明确要求关闭TCP连接。

      目前大多数浏览器,对于同一个域名最多允许建立6个持久连接。提高了带宽的利用率。改善了HTTP协议的效率

      加入了管道机制。在同一个连接里面,允许同时发送多个请求,增加了并发性。

    • 2.0

      为了解决利用率不高,继续优化提出了2.0版本操作。增加了双工模式。即不仅客户端能够同时发送多个请求,服务器同时处理多个请求。解决了对头阻塞问题。

协议的组成和过程

  • HTTP协议它是由HTTP请求和HTTP响应组成的。当在客户端中输入一个url地址回车,浏览器会将你的请求消息封装为一个HTTP请求的格式发送给服务器端,服务器端收到这个消息之后,解析这个请求消息之后,解析这个请求消息将他装配到ServletRequest,做完业务逻辑之后组织响应数据封装成一个HTTP响应的格式返回给浏览器客户端,这个过程就是请求和响应。如果没有请求就没有响应。

  • 抓包分析:

    使用Chrome自带的工具(F12) 可以看到数据之间传输的具体过程

HTTP请求

get请求

  • get请求--HTTP默认的请求时get请求(直接在客户端的地址栏中输入url和使用<a href="请求资源路径">),对于from表单可以指定请求的方式post

    // 请求行:请求方式   请求路径  http版本号
    GET /checkUser?username=admin&password=123456 HTTP/1.1
    // 本机服务器的ip地址  域名    
    Host: localhost:8080
    // 连接的方式  长连接    
    Connection: keep-alive
    // 告诉服务器,自己支持你这中操作,我能读懂服务器发送的的信息,建议使用https而不是用http    
    Upgrade-Insecure-Requests: 1
    //用户使用的浏览器客户端的内核 还包括操作系统相关的信息
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36
    // 接收的数据格式   
    // 告诉服务器端浏览器客户端能够接收的哪种类型的数据
    Accept:
    // mime类型  大范围/小范围
    text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/;q=0.8,application/signed-exchange;v=b3;q=0.9
    Sec-Fetch-Site: same-origin
    Sec-Fetch-Mode: navigate
    Sec-Fetch-User: ?1
    Sec-Fetch-Dest: document
    // 来源地--->浏览器通知服务器,当前的请求时来自于哪个地方
    // Referer:经常用于防盗链  防止盗取我的连接信息 如果你是直接请求资源,没有referer
    Referer: http://localhost:8080/
    // 接收的压缩格式  浏览器通知服务器我可以接收哪些类型压缩格式的数据
    Accept-Encoding: gzip, deflate, br
    // 浏览器可支持的语言
    Accept-Language: zh-CN,zh;q=0.9
    // Cookie  JSESSIONID
    Cookie: Idea-ac70df0d=96318eda-906d-4909-81b5-89db73f5358a; JSESSIONID=43EE4E93151302CD4B10579871CB8328
    

post请求

//请求方式 url  版本
POST /checkUser HTTP/1.1
//请求 主机
Host: localhost:8080
Connection: keep-alive
// 内容的长度    
Content-Length: 30
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://localhost:8080
// 如果是post请求在请求体中的数据使用url编码
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost:8080/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: Idea-ac70df0d=96318eda-906d-4909-81b5-89db73f5358a; JSESSIONID=43EE4E93151302CD4B10579871CB8328
// 请求体内容
username=admin&password=123456

HTTP响应的消息

// 响应行 组成:协议/版本 响应状态码 状态码描述
HTTP/1.1 200
// 响应头 组成:头名称:值
/*
 常见的响应头:
	1. Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式
	2. Content-disposition:服务器告诉客户端以什么格式打开响应体数据值
	 in-line :默认值,在当前页面内打开
	 attachment;filename=Xxx :以附件形式打开响应体.文件下载.
*/
// 响应正文的内容长度    当前为0
Content-Length: 0
// 响应的时间    
Date: Tue, 12 Jan 2021 07:49:14 GMT
Keep-Alive: timeout=20
Connection: keep-alive
//响应体:传输的数据

常用的状态码

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

get 和 post的区别

分类 get post
后退按钮/刷新 无害 数据会被重新提交(浏览器告知用户数据会被重新提交)
书签 可收藏为书签 不能收藏为书签
缓存 能被缓存 不能缓存
编码类型 application/x-www-form-urlencoded application/x-www-form-urlencoded 或 multipart/form-data。为二进制数据使用多重编码。
历史 参数保留在浏览器历史中 参数不会保存在浏览器历史中
对数据长度限制 url的长度是受限制的,url最大长度 2048个字符 无限制
对数据类型的限制 只允许 ASCLL字符 没有限制。也允许二进制数据
安全性 与 post相比,get的安全性较差,因为发送的数据是 url的一部分 post 比 get更安全,因为参数不会保存在浏览器历史或者 web服务器中
可见性 数据在 url中对所有人都是可见的 数据不会显示在 url中
参考连接

Request 对象

1. request对象和response对象的原理

  1. tomcat 服务器会根据请求 url中的资源路径,创建对应的 servletXxx对象,。
  2. tomcat 服务器,会创建 request和 response对象,request对象中封装请求消息数据。。
  3. tomcat 将 request 和 response两个对象传递给 service方法。并且调用 service方法。
  4. 程序员,可以通过 request对象获取请去消息数据,通过 response对象设置响应消息数据。
  5. 服务器在给浏览器做出响应之前,会从 response对象中拿程序员设置的响应消息数据。

2. request对象继承体系结构:

image
ServletRequest -- 接口
↑ 继承
HttpServletRequest -- 接口
↑ 实现
org.apache.catalina.connector.RequestFacade 类(tomcat)

3. request功能:

3.1获取请求消息数据

3.1.1 获取请求行数据
示例:GET /day14/demo1?name=zhangsan HTTP/1.1
方法:

  1. 获取请求方式 :GET
    String getMethod()

  2. 获取虚拟目录:/day14
    String getContextPath()

  3. 获取Servlet路径: /demo1
    String getServletPath()

  4. 获取get方式请求参数:name=zhangsan
    String getQueryString()

  5. 获取请求URI:/day14/demo1
    String getRequestURI() /day14/demo1
    StringBuffer getRequestURL() :http://localhost/day14/demo1
    URL:统一资源定位符 : http://localhost/day14/demo1 中华人民共和国
    URI:统一资源标识符 : /day14/demo1 共和国

  6. 获取协议及版本:HTTP/1.1
    String getProtocol()

  7. 获取客户机的IP地址:
    String getRemoteAddr()

3.1.2 获取请求头数据
方法:
String getHeader(String name): 通过请求头的名称获取请求头的值(不区分大小写 )
Enumeration getHeaderNames(): 获取所有的请求头名称

Enumeration<E> 该接口的功能由 iterator接口复制.
boolean hasMoreElements(); 测试此枚举是否包含更多元素
E nextElement();如果此枚举对象至少有一个提供的元素,则返回此枚举的下一个元素.

3.1.3 获取请求体数据

  • 请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数
  • 步骤:
    1. 获取流对象
      BufferedReader getReader():获取字符输入流,只能操作字符数据
      ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据
      在文件上传知识点后讲解
    2. 再从流对象中拿数据

3.2 其他功能

  1. 获取请求参数通用方式:不论get还是post请求方式都可以使用下列方法来获取请求参数

    String getParameter(String name):根据参数名称获取参数值    username=zs&password=123
    String[] getParameterValues(String name):根据参数名称获取参数值的数组  hobby=xx&hobby=game (多用于复选框)
    Enumeration<string> getParameterNames():获取所有请求的参数名称(复选框只能获取一次)
    Map<string,string[]> getParameterMap():获取所有参数的map集合
    

    中文乱码问题:
    get方式:tomcat 8 已经将get方式乱码问题解决了
    post方式:会乱码
    解决:在获取参数前,设置request的编码
    request.setCharacterEncoding("utf-8");

  2. 请求转发(内部)(留头不留体,留体抛异常):一种在服务器内部的资源跳转方式
    image

    • 步骤
      通过request对象获取请求转发器对象:
      RequestDispatcher getRequestDispatcher(String path);
      使用RequestDispatcher对象来进行转发:
      forward(ServletRequest request, ServletResponse response);
      
    • 特点:
      1. 浏览器地址栏路径不发生变化。
      2. 只能转发到当前服务器内部资源中。
      3. 转发是一次请求
      4. 他们共享 Request域中的数据
  3. 请求包含(留头又留体)

request.getRequestDispatcher("/路径").include(request, response);

请去转发和包含区别

  • 相同点
    请求转发和请求包含都是在处理一个相同的请求,多个Servlet之间使用同一个 request 对象和 response 对象。
  • 不同点
    1. 如果在AServlet中请求转发到BServlet,那么在AServlet中不允许再输出响应体,即不能使用response.getWriter() 和response.getOutputStream() 向客户端输出,这一工作交由BServlet来完成;如果是由AServlet请求包含BServlet,则没有这个限制。
    2. 请求转发不能设置响应体,但是可以设置响应头,简单来说就是“留头不留体”,例如:response.setContentType("text/html;charset=utf-8”) 是可以留下来的;请求包含不仅可以设置响应头,还可以设置响应体,简单来说就是“留头又留体“。
    3. 请求转发大多应用在Servlet中,转发目标大多是jsp页面;请求包含大多应用在jsp页面中,完成多页面的合并。一般情况下经常使用的是请求转发。
  1. 共享数据:
    域对象:一个有作用范围的对象,可以在范围内共享数据
    request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据
    方法
void setAttribute(String name,Object obj):存储数据
Object getAttitude(String name):通过键获取值
void removeAttribute(String name):通过键移除键值对
Enumeration<String> getAttributeNames();:获取所有域属性名
  1. 获取ServletContext:
ServletContext getServletContext()
  1. 获取 cookie
public Cookie[] getCookies();

案例:用户登陆

用户登陆案例需求

  1. 编写 login.html 登陆页面
    username&password 两个输入框
  2. 使用 Druid数据库连接池技术, 操作 mysql
  3. 使用 JDBCTemplate 技术封装 jdbc
  4. 登陆成功跳转到 successServlet展示:登陆成功
  5. 登陆失败跳转套 failServlet展示:登陆失败
    步骤
  • 创建项目,编写 登陆html,配置文件,jar包,创建 User Bean
    image

db.properties

# 连接数据库的四大组件
driverClassName=com.mysql.jdbc.Driver
username=root
password=root
url=jdbc:mysql:///test?characterEncoding=utf-8

jdbc工具类 使用 durid连接池

public class JDBCUtils {
    private static DataSource dataSource;
    static {
        //加载配置文件
        Properties properties = new Properties();
        //使用 反射 ClassLoader加载配置文件,获取字节输入流
        InputStream is = JDBCUtils.class.getClassLoader()
                .getResourceAsStream("db.properties");
        try {
            properties.load(is);
            //初始化了对象
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
	//获取连接池对象
    public static DataSource getDataSource() {
        return dataSource;
    }
	//获取连接 Connection对象
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
}

UserDao

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

    /**
     * 登陆方法
     *
     * @param check 只有用户名和密码
     * @return User 包含用户全部数据,没有查询到返回 null
     */
    public User login(User check) {
        try {
            //编写 sql
            String sql = "select * from user where username= ? and password=?";
            //调用 query方法
            User user = template.queryForObject(
                    sql, new BeanPropertyRowMapper<>(User.class),
                    check.getUsername(), check.getPassword());
            return user;
        } catch (DataAccessException e) {
//            e.printStackTrace();//记录日志
        }
        return null;
    }
}

ServletLogin

@WebServlet("/ServletLogin")
public class ServletLogin extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置鞭名马
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        //获取请去参数
        Map<String, String[]> map = request.getParameterMap();
//        String username = request.getParameter("username");
//        String password = request.getParameter("password");
        //封装 User对象
//        User check = new User(username, password);
        //使用 beanUtils 封装 apache下的
        User check = new User();
        try {
            BeanUtils.populate(check, map);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();

        }
        //调用 UserDao的 login方法
        UserDao userDao = new UserDao();
        User user = userDao.login(check);
        //判断 user
        if (user == null) {
            response.getWriter().write("用户名或密码错误");
        } else {
            response.getWriter().write("登陆成功<br>欢迎" + user.getUsername() + "用户");
        }
    }
}

org.apache.commons.beanutils.BeanUtils;
BeanUtils的其他方法
static void populate(Object bean, Map properties)
static void setProperty(Object bean, String name, Object value)
static String getProperty(Object bean, String name)
login.html 中form 表单的 action 路径写法

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <title>登陆</title>
</head>
<body>
<!-- action=/tomcat中的路径/服务器的资源路径-->
<form action="/ServletLogin" method="post">
    用户名:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>
    <input type="submit" value="提交">
</form>
</body>
</html>

Response对象

1. 设置响应消息

  1. 设置响应行: setStatus(int sc);
  2. 设置响应头: setHeader(String name,String value)
  3. 设置响应体:(通过流的方式) getWriter() getOutputStream() --->write()

2. 重定向

image

/*
* 重定向:访问 /ServletResponseDemo1 会自动跳转到 /ServletResponseDemo2
* 1. 设置状态码 302
* 2. 设置响应头 location
* 注意:tomcat设置有虚拟目录记得加上
* */
// response.setStatus(302);
// response.setHeader("location", request.getContextPath() + "/ServletResponseDemo2");
//简单的重定向
response.sendRedirect(request.getContextPath() + "/ServletResponseDemo2");

3. 内部转发和重定向区别

forward 和 redirect 区别

  1. 重定向地址栏会发生改变,而内部转发地址栏不会发生改变。
  2. 重定向相当于发送了两次请求,而内部转发只有一次请求。
  3. 重定(路径)向相对于tomcat服务器,而内部转发是相对于web应用的。
  4. 重定向可以访问外部服务器资源,而内部转发只能访问本服务器下的资源。
  5. 内部转发需要通过 request对象来实现,而重定向需要同过 response对象来实现的。
  6. 内部转发可以访问到 web应用中安全目录信息(web-inf)(服务器行为),而重定向访问不到(浏览器行为)
  7. 内部转发可以在转发的过程中把数据保存到 request域中,重新向不可以.

4. 路径写法:

4.1 相对路径:

  • 相对路径: 通过相对路径不可以确定唯一资源

    如:   ./index.html
    不以 / 开头,以 . 开头

  • 规则: 找到当前资源和目标资源之间的相对位置关系
    ./ : 当前目录
    ../ : 后退一级目录

4.2 绝对路径:

  • 绝对距今: 通过绝对路径可以确定唯一资源

    如: http://localhost/response/demo2      /response/demo2
    以 /开头的路径

  • 规则:判断的路径是给谁用的?判断请去将来从哪儿发出
    给客户端浏览器使用:需要加虚拟目录(项目的访问路径)建议虚拟目录动态获取: request.getContextPath();
    给服务器使用的:不需要加虚拟目录

5. 响应乱码

//获取流对象之前,设置流的默认编码: iso-8859-1 -->GBK
// response.setCharacterEncoding("utf-8"); 

//告诉浏览器,服务器发送的消息体数据的编码.建议浏览器使用该编码解码
//设置响应头的编码:text/html;charset=utf-8
//response.setHeader("content-type","text/html;charset=utf-8");
//这句话不仅仅设置了流的编码,还告诉浏览器我要使用的编码

//简单的形式
response.setContentType("text/html;charset=utf-8");

6. 案例:验证码

编写 验证码图片

@WebServlet("/ServletResponseDemo5")
public class ServletResponseDemo5 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //response.setContentType("text/html;charset=utf-8");
        //1. 创建一对象,在内存中画图(验证图片对象)
        int width = 100;
        int height = 50;
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        //2. 美化图片
        //填充背景色
        Graphics graphics = image.getGraphics();//画笔对象
        graphics.setColor(Color.pink);//设置画笔的颜色
        graphics.fillRect(0, 0, width, height);//填充
        //画边框
        graphics.setColor(Color.blue);//设置颜色
        graphics.drawRect(0, 0, width - 1, height - 1);//画矩形
        //写验证码
        String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();

        for (int i = 1; i <= 4; i++) {
            String c = str.charAt(random.nextInt(str.length())) + "";
            graphics.drawString(c, width / 5 * i, height / 2);
        }
        //画干扰线
        graphics.setColor(Color.green);
        for (int i = 0; i < 10; i++) {
            int x1 = random.nextInt(width);
            int x2 = random.nextInt(width);
            int y1 = random.nextInt(height);
            int y2 = random.nextInt(height);
            graphics.drawLine(x1, x2, y1, y2);
        }

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

    }
}

register.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>注册</title>
</head>
<body>
<!--注意tomcat有虚拟目录记得加上-->
<img id="changeCode" src="/ServletResponseDemo5">
<a id="change" href="/ServletResponseDemo6">看不清换一张</a>
</body>
<script>
    /*
    * 分析:
    * 点击超链接或图片,需要换一张
    * 1. 给找链接和图片绑定点击事件
    * 2. 重新设置图片的 src属性
    * */
	//给图片绑定点击事件,就更换一张
    let img = document.getElementById("changeCode");
    img.onclick = function () {
        //加时间戳
        var date = new Date().getTime();
        img.src = "/ServletResponseDemo5?" + date;
    }
	//超链接同理 省略
</script>
</html>

src 超链接方式 更换

@WebServlet("/ServletResponseDemo6")
public class ServletResponseDemo6 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.sendRedirect(request.getContextPath() + "/register.html");
        //都可以
        /*request.getRequestDispatcher(request.getContextPath()
                + "/register.html").forward(request, response);*/

    }
}

域对象

  • 域对象:可以在指定区域中实现数据共享的对象

request 对象也是一个域对象,在这个域对象中可以存放一些数据

  • 数据的存储,读取,删除
    • 向request域对象中存储一些值:setAttribute(String key,Object obj)
    • 从request域中去读一些值:getAttribute(String key)
    • 从request域对象中删除指定的值:removeAttribute(String key)
    • 获取所有域属性名:Enumeration getAttributeNames();
  • request域对象的声明周期
    • 创建:第一次访问该Servlet服务的时候就创建出来
    • 销毁:一次请求结束后就自动销毁了
    • 重定向获取不到request域对象中的值

ServletContext 对象

1. 概念:

  • ServletContext域对象,代表整个web应用对象,整个对象内部封装了web应用的一些信息,注意:这个ServletContext对象无论何时何地获取的都是同一个对象。单例Servlet

2. 获取

  • 通过当前的 Servlet对象来获取ServletContext对象
    this.getServletContext();
  • 通过 request对象获取
    request.getServletContext();
  • 该对象在整个应用中始终是唯一的。

3. 功能(方法):

  1. 获取 MIME类型:
    • MIME 类型:在互联网通信过程中定义的一种文件数据类型
      格式:大类型/小类型   text/html   image/jpeg
    • 获取:String getMimeType(String file);
  2. 域对象:共享数据
    • setAttribute(String name,object value);
    • getAttribute(String name);
    • removeAttribute(String name);
    • Enumeration getAttributeNames();
    • ServletContext 对象范围: 所有用户请去的数据(服务器启动他就被创建,服务器关了才被销毁,我们很谨慎用它,存储数据多了会造成服务器压力很大)
  3. 获取文件的真实(服务器)路径
    • String getRealpath(String path)

4. ServletContext声明周期

  • 创建:web应用已启动,该对象就随之创建,随着服务器的启动而创建
  • 销毁:服务器关闭的时候,web应用从服务器移除的时候

5.SetContext作用

  • 配置web应用的参数信息
  • Spring框架 --》xml配置文件 ---》对象管理容器
  • applicationContext.xml
  • 该对象中存储的值可以被整个应用所共享

案例:文件下载

需求:

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

分析:

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

步骤:

  1. 定义页面,编辑超链接 href属性,执行 servlet,传递资源名称 filename
  2. 定义 servlet
    • 获取文件名称
    • 使用字节输入流加载文件进内存
    • 指定 response的响应头: content-disposition:attachment;filename=Xxx
    • 将数据写入到 response输入流
      servlet
@WebServlet("/ServletContextDownload")
public class ServletContextDownload extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 获取请去参数文件名称
        String filename = request.getParameter("filename");
        //2. 使用字节输入流加载文件进内存
        //找到文件服务器路径
        ServletContext servletContext = this.getServletContext();
        String realPath = servletContext.getRealPath("/image/" + filename);
        //用字节路关联
        FileInputStream fis = new FileInputStream(realPath);
        //3. 设置 response 的响应头
        //设置响应头类型:content-type
        String mimeType = servletContext.getMimeType(filename);//获取文件的mime类型
        response.setHeader("content-type", mimeType);

        //设置响应头打开方式: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);
        }
        fis.close();
    }
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <img src="/image/a.jpeg" style="height: 500px;500px">
<a href="/ServletContextDownload?filename=a.jpeg">下载美女图片</a>
</body>
</html>

问题:

  • 中文文件问题
  • 解决思路
    获取客户端使用的浏览器版本信息
    根据不同的版本信息,响应不用的数据(设置 filename的编码)
public class DownLoadUtils {
    public static String getFileName(String agent, String filename) throws UnsupportedEncodingException {
        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;
    }
}
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 获取请去参数文件名称
        String filename = request.getParameter("filename");
        //2. 使用字节输入流加载文件进内存
        //找到文件服务器路径
        ServletContext servletContext = this.getServletContext();
        String realPath = servletContext.getRealPath("/image/" + filename);
        //用字节路关联
        FileInputStream fis = new FileInputStream(realPath);
        //3. 设置 response 的响应头
        //设置响应头类型:content-type
        String mimeType = servletContext.getMimeType(filename);//获取文件的mime类型
        response.setHeader("content-type", mimeType);
        /*
         * 解决 中文文件名问题
         * 1. 使用工具类
         * 2. 使用工具类方法编码文件几节课
         * */
        String agent = request.getHeader("user-agent");
        filename = DownLoadUtils.getFileName(agent, filename);


        //设置响应头打开方式: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);
        }
        fis.close();
    }

案例:文件上传

地址

会话技术

由于HTTP协议是无敌状态的协议,就是说每次客户端请求服务器端,对于服务器来说都是新的,它并不知道是谁在访问我,如果需要保证服务器来识别到底是哪一个客户端来访问我的,就需要会话技术来实现。

  • 什么是会话:
    从用户打开客户端访问我们应用开始到用户关闭客户端这个过程称之为一次会话。
    从客户端访问web应用开始,这个时候就会在系统中开辟一个内存空间存储访问过程中产生的数据,知道用户关闭浏览器,内存中的数据才会被清除掉。
    目的就是为了:共享数据的

  • 会话技术分类

    • Cookie :把数据存储到客户端本地中,减少了服务器存储数据的压力,安全性不高,客户端也可以手动清除掉Cookie值
    • Session :把数据存储到web应用服务器中,安全性较高,服务器压力较大。
  • 把数据存储到客户端本地中

快速入门

- 创建 Cookie对象,绑定数据
	new Cookie(String name String value)
- 发送 Cookie 对象
	response.addCookie(Cookie cookie)
- 获取 Cookie,拿到数据
	Cookie[] request.getCookies()
@WebServlet("/ServletCookieDemo1")
public class ServletCookieDemo1 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 创建 cookie
        Cookie cookie = new Cookie("msg", "发送cookie");
        //2. 发送 cookie对象
        response.addCookie(cookie);

    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}
@WebServlet("/ServletCookieDemo2")
public class ServletCookieDemo2 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //3. 获取cookie
        Cookie[] cookies = request.getCookies();
        //获取数据,遍历cookie
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                String key = cookie.getName();
                String value = cookie.getValue();
                System.out.println(key + "," + value);
            }
        }


    }

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

结果

Webstorm-8654f43,e72bb3aa-1ca5-4165-8179-94ceb8dd6bec
JSESSIONID,E5A45D39135B282D6CC458503FAD2B77
msg,发送cookie

存储过程:

服务器经常需要把程序中的数据放到cookie中,通过响应头形式发送给客户端,客户端收到这个头信息,就会将头信息中的数据保存起来,客户端再次请求服务器端将cookie信息放到请求头中,那么服务器获取请求头,请求头中有cookie信息
image

cookie的细节

  1. 一次可不可以发送多个 cookie?
    可以,创建多个 cookie对象,使用 response调用多次 addCookie方法发送 cookie即可.
  2. cookie在浏览器中保存多长时间?
    默认情况下,当浏览器关闭后,Cookie数据被销毁.
    设置 cookie生命周期,持久化存储

    setMaxAge(int seconds)
    正数:将 Cookie数据西写入到硬盘的文件中.持久化存储.seconds(秒)就是存活时间
    负数:默认值
    零:删除cookie信息

  3. cookie能不能存中文?
    在 tomcat 8 之前,cookie中不能直接存储中文数据.需要将中文数据转码-->一般采用URL编码(%E3)
    在 tomcat 8 之后,cookie支持中文数据
  4. cookie共享问题?
    • 假设在一个 tomcat服务器中,部署了多个web项目,那么这些 web项目中,能不能共享?
      默认情况下 cookie不能共享
      setPath(String path):设置 cookie的获取范围.默认情况下,设置当前的虚拟目录
      如果要共享,则将 path设置为 /
    • 不同的 tomcat服务器间 cookie共享问题?
      setDomain(String path);如果设置一级域名相同,那么多个服务器之间的 cookie可以共享
      setDomain(".baidu.com"),那么 tieba.baidu.com 和 news.baidu.com中的 cookie可以共享

Cookie的特点和作用

特点

  1. cookie 存储数据在客户端浏览器(容易被篡改,不安全)
  2. 浏览器对于单个 cookie的大小有限制(4kb),以及 对同一个域名下的总 cookie数量有限制的(20个以内)

作用

  1. cookie 一般用于存储少量的不太敏感的数据
  2. 在不登陆的情况下,完成服务器对客户端的身份识别

案例:记住上一次访问时间

案例需求

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

分析:

  1. 用 cookie来完成
  2. 在服务器的 Servlet 判断是否有一个名为 lastTim 的cookie
    • 有:不是第一次访问
      响应数据:欢迎回来,你上一次访问的时间为第一次的时间:xxxx年xx月xx日 xx:xx:xx
    • 没有:是第一次访问
      响应数据:你好欢迎首次访问.
      写 cookie:lastTime=xxxx年xx月xx日 xx:xx:xx
@WebServlet("/CookieTest")
public class ServletCookieDemo3 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        boolean flag = false;
        Cookie[] cookies = request.getCookies();
        if (cookies != null && cookies.length > 0) {
            for (Cookie cookie : cookies) {
                String name = cookie.getName();
                if ("lastTime".equals(name)) {
                    flag = true;
                    String value = cookie.getValue();
                    //解码
                    String encode = URLDecoder.decode(value, "utf-8");
                    response.getWriter().write("欢迎回来,你上一次访问时间为" + encode);
                    break;
                }
            }
        }
        if (cookies == null || cookies.length == 0 || flag) {
            //第一次访问 添加 cookie
            Cookie firstCookie = new Cookie("lastTime",
                    //出现 500 是字符集问题
                    //编码
                    URLEncoder.encode
                            (new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss")
                                    .format(System.currentTimeMillis()), "utf-8"));
            //设置 存活时间 一个月
            firstCookie.setMaxAge(60 * 60 * 24 * 30);
            response.addCookie(firstCookie);
            response.getWriter().write("欢迎首次访问");
        }

    }

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

Session

  1. 概念:服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中。HttpSession

  2. 快速入门:

    • 获取HTTPSession对象:
      HttpSession session = request.getSession();
    • 使用HttpSession对象:
      Object getAttribute (String name);
      void setAttribute(String name,Object value);
      void removeAttribute(String name);
  3. 原理:Session的实现是依赖于Cookie的。
    image

  4. 细节:

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

      默认情况下。不是。

      如果需要相同,则可以创建Cookie,键位jsessionid,设置最大存活时间,让cookie持久化保存。

      Cookie cookie = new Cookie("JSESSIONID",session.getId());
      cookie.setMaxAge(60*60);
      response.addCookie(cookie);
      
    2. 客户端不关闭,服务器关闭后,两次获取的session是同一个吗?

      不是同一个,但是要确保数据不丢失。tomcat自动完成以下工作

      • session的钝化:在服务器正常关闭之前,将session对象序列化到硬盘上
      • session的活化:在服务器启动后,将session文件转化为内存中的session对象即可
    3. session什么时候被销毁?

      1. 服务器关闭
      2. session对象调用invalidate()
      3. session默认失效时间30分钟
      4. 选择性配置修改(在 tomcat的 conf的 web.xlm中)
        <session-config>
        	<session-timeout>30</session-timeout>
        </session-config>
        
  5. session的特点

    1. session用于存储一次会话的多次请求的数据,存在服务端
    2. session可以存储任意类型,任意大小的数据
    • session于Cookie的区别:
      • session存储数据在服务器端,Cookie在客户端
      • session没有数据大小限制,Cookie有
      • session数据安全,Cookie相对于不安全
  • 将数据保存到服务器端,她会给每一个客户端创建一块独自的内存空间,在用户第一次请求过来的时候通过响应头形式将这个块内存空间的编号发送给客户端,客户端每次请求时会携带jessionid放到请求头中访问服务器。服务器会找到对应的jessionid识别时那一块的内存空间。

  • 获取HTTPSession对象:

    request.getSession();

    • 两件事:
      1. 获取当前会话的session对象,如果服务器端没有session对象,服务器自动创建一个新的session对象。如果有就会回去对应的session对象。
      2. 并将session中的jessionid放入到cookie中,并设置的携带路径为当前web应用,自动通过response对象返回。

验证码案例

需求:

  1. 访问带有验证码的登陆页面 login.jsp
  2. 用于输入用户名,密码以及验证码
    • 如果用户名和密码输入有误,跳转到登陆页面,提示:用户名或密码错误
    • 如果验证码输入有误,跳转登陆页面,提示:验证码错误
    • 如果全部输入正确,则跳转到主页 success.jsp ,显示:用户名,欢迎你

分析

  1. 设置 request的编码

  2. 获取参数 map集合

  3. 获取验证码

  4. 将用户信息封装到 user对象

  5. 判断程序验证码和用户输入的验证码是否一致。从 session中获取程序生成的验证码
    一致

    • 在判断用户名和密码是否正确
      正确:登陆成功,存储数据 session,跳转 success.jsp 重定向
      不正确:给提示信息:跳转登陆页面

    不一致

    • 给用于提示信息;验证码错误 request
    • 跳转登陆页面

验证码 的 servlet

@WebServlet("/checkCodeServlet")
public class CheckCodeServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //response.setContentType("text/html;charset=utf-8");
        //1. 创建一对象,在内存中画图(验证图片对象)
        int width = 100;
        int height = 50;
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        //2. 美化图片
        //填充背景色
        Graphics graphics = image.getGraphics();//画笔对象
        graphics.setColor(Color.pink);//设置画笔的颜色
        graphics.fillRect(0, 0, width, height);//填充
        //画边框
        graphics.setColor(Color.blue);//设置颜色
        graphics.drawRect(0, 0, width - 1, height - 1);//画矩形
        //写验证码
        String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        //验证码
        StringBuilder checkCode_session = new StringBuilder();

        for (int i = 1; i <= 4; i++) {
            String c = str.charAt(random.nextInt(str.length())) + "";
            checkCode_session.append(c);
            graphics.drawString(c, width / 5 * i, height / 2);

        }
        //将验证码存入 session中
        request.getSession().setAttribute(
                "checkCode_session", checkCode_session.toString());
        //画干扰线
        graphics.setColor(Color.green);
        for (int i = 0; i < 10; i++) {
            int x1 = random.nextInt(width);
            int x2 = random.nextInt(width);
            int y1 = random.nextInt(height);
            int y2 = random.nextInt(height);
            graphics.drawLine(x1, x2, y1, y2);
        }

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

    }
}

更换验证码 servlet

@WebServlet("/codeReplaceServlet")
public class CodeReplaceServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.sendRedirect(request.getContextPath() + "/login.jsp");
    }
}

登陆 的servlet

@WebServlet("/loginServlet")
public class loginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 设置 request 编码
        request.setCharacterEncoding("utf-8");
        //2. 获取参数 这里不用 map获取
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String checkCode = request.getParameter("checkCode");
        //3. 先获取生成的验证码
        String checkCode_session = (String) request.getSession().getAttribute("checkCode_session");
        //获取 验证码立马删除
        //删除验证码
        request.getSession().removeAttribute("checkCode_session");
        //先判断 验证码是否正确
        if (checkCode_session != null && checkCode_session.equalsIgnoreCase(checkCode)) {
            //忽略大写判断
            //判断用户名和密码是否一致
            //判断用户名 和 密码是否一致
            if ("admin".equals(username) && "123456".equals(password)) {//需要调用 UserDao查询数据库
                //登陆成功
                //存储用户信息
                request.getSession().setAttribute("user", username);
                //重定向 success.jsp
                response.sendRedirect(request.getContextPath() + "/success.jsp");
            } else {
                //登陆失败
                // 存储提示信息到 request
                request.setAttribute("login_error", "用户名或密码错误");
                //转发到登陆页面
                request.getRequestDispatcher("/login.jsp").forward(request, response);
            }

        } else {
            //验证码不一致
            // 存储提示信息到 request
            request.setAttribute("verification_error", "验证码错误");
            //转发到登陆页面
            request.getRequestDispatcher("/login.jsp").forward(request, response);
        }

    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws
            ServletException, IOException {

    }
}

登陆页面 jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登陆</title>
</head>
<body>
<form action="/loginServlet" method="post">
    <table>
        <tr>
            <td>用户名</td>
            <td><input type="text" name="username"></td>
        </tr>
        <tr>
            <td>密码</td>
            <td>
                <input type="password" name="password">
                <%--使用三木运算隐藏 null在页面显示--%>
                <div style="color: red">
                    <%=request.getAttribute("login_error") == null ? "" : request.getAttribute("login_error")%>
                </div>
            </td>
        </tr>

        <tr>
            <td>验证码</td>
            <td><input type="text" name="checkCode"></td>
        </tr>
        <tr>
            <%--注意 tomcat中的虚拟目录--%>
            <td colspan="2">
                <img src="/checkCodeServlet" id="changeCode">
                <a href="/codeReplaceServlet">看不清</a>
                <%--使用三木运算隐藏 null在页面显示--%>
                <div style="color: red">
                    <%=request.getAttribute("verification_error") == null ? "" : request.getAttribute("verification_error") %>
                </div>

            </td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="登陆"></td>
        </tr>
    </table>
</form>
</body>
<script>
    /*点解图片更换验证码*/
    let img = document.getElementById("changeCode");
    img.onclick = function () {
        //加时间戳
        var date = new Date().getTime();
        img.src = "/checkCodeServlet?" + date;
    }
</script>
</html>

成功页面 jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>成功</title>
</head>
<%--使用三木运算隐藏 null在页面显示--%>
<body>
<h1><%=(request.getSession().getAttribute("user") != null) ? request.getSession().getAttribute("user") : ""%>欢迎你</h1>
</body>
</html>

细节

  1. 页面显示 null使用三木运算符解决
  2. 登陆成功之后点击浏览器回退 验证码session还有,可以直接点解登陆,我们得到 数据后删除 session解决
原文地址:https://www.cnblogs.com/zk2020/p/15142995.html