Servlet

一:了解Servlet

(1)Servlet的概念

💗Servlet是一种实现动态页面的技术;是一组Tomcat提供给程序猿的API(类和方法),帮助程序猿简单高效的开发一个web app


💙优点:不必关注Socket,HTTP协议格式,多线程并发等技术细节,降低了web app的开发门槛,提高了开发效率

(2)动态页面

🌟用户不同/时间不同/输入的参数不同,页面内容会发生变化


💜例如:以B站为例子;B站的每个用户它的视频类型喜欢偏好都是不同的,因此不同用户的主页推荐的视频也不相同

(3)静态页面

🌟静态页面也就是内容始终固定的页面;即使用户不同/时间不同/输入的参数不同,页面内容也不会发生变化

(除非网站的开发人员修改源代码, 否则页面内容始终不变)

(4)Servlet的工作

二:第一个Servlet程序

(1)创建Maven项目

💗使用IDEA创建Maven项目


Maven:Maven是Java常用的构建工具;一个程序在编写过程中往往需要涉及到一些第三方库的依赖,另外还需要针对这个写好程序进行打包部署;Maven存在的意义就是为了更方便的依赖管理和打包


①创建步骤


②了解不同目录的作用

(1)pom.xml:主要是Maven项目最主要的配置文件

(2)src:主要用来放Java源代码

(3)main:主要放一些业务代码

(4)main下面的Java目录:主要用来放Java代码

(5)main下面的resources目录:主要用来放一些依赖的资源(图片、音频.....)

(6)test:主要放一些测试代码

(2)引入依赖

①打开中央仓库➜中央仓库


②搜索 "servlet", 一般第一个结果就是


③如果Tomcat是8.0版本就选择3.1.0版本

(Tomcat与Servlet的版本是有对应关系的,一定要匹配版本)

🌟如果Tomcat是其他版本,可以点击这里查询对应版本


④复制这段代码到pom.xml


⑤复制到pom.xml之前,需要加入一个<dependencies></dependencies>标签

此时IDEA就会自动的通过Maven从中央仓库下载这里的依赖

💜(关于dependencies标签,标签里是专门放依赖的,里面可以有多个dependency,就证明有多个依赖)

🌟如果是首次使用,红色框框这里可能会标红,这时就看一下Sync这里会有一个圈圈,点击圈圈进行手动下载

(3)创建一些必要的目录/文件

1.创建webapp目录

💗在main目录下,和 java 目录并列,创建一个webapp目录

(注意英文单词拼写)


2.创建web.xml文件

💗在刚刚创建的webapp目录内部创建一个WEB-INF目录,并创建一个web.xml文件

(注意英文单词,注意大小写,注意这个-是英文的)


🌟 web.xml文件的作用:告诉Tomcat,我现在这个目录里的东西就是一个webapp,你要把我加载起来

3.编写web.xml

💗web.xml是不能为空的;必须写内容,当然这些内容也是固定的,我们不需要背,需要使用的时候就从别的项目复制粘贴即可


🌟web.xml文件的内容:

<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
  <display-name>Archetype Created Web Application</display-name>
</web-app>

(4)编写代码

1.代码与分析

🌟在main/Java目录下创建一个HelloServlet来编写代码


①代码如图
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    @Override  //重写doGet方法
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //此时打印的代码hello world是在服务器日志中产生的,即是在Tomcat日志中打印的,此时客户端还不能看见
        System.out.println("hello world");

        //要想客户端能够看见,还得需要下面这段代码
        resp.getWriter().write("hello world");
    }
}

②分析代码

(1)首先创建一个类 HelloServlet , 继承自HttpServlet,因为编写Servlet程序一般都需要继承这个类;而且HttpServlet类提供了处理HTTP协议的方法,这些方法包括处理HTTP请求和响应的方法,如doGet、doPost、doPut等

(继承不是目的,真正的目的是重写父类HttpServlet的方法)


(2)这里我们写一个doGet方法,且这个方法不需要我们手动调用,因为doGet方法本质上是一个“回调函数”,把这个方法写好之后就会交给Tomcat,Tomcat会在收到一个合适的GET请求时自动调用doGet方法,当调用doGet方法时,Tomcat就会自动解析HTTP请求,生成一个HttpServletRequest对象,这个对象里的属性啥的都是和HTTP协议格式匹配的,相当于Tomcat自动帮我们把HTTP请求解析好,不需要我们再去写什么解析的代码;同时,Tomcat也会构造一个空的HttpServletResponse对象,空指的不是null,而是一个new好了但是没有任何属性的对象,再把这个HttpServletResponse对象传递到deGet里,doGet要做的事就是根据请求(req)计算出响应(resp),也就是说doGet会根据req里不同参数的细节,生成一个具体的resp对象,即往resp空对象里添加属性,Tomcat就会根据doGet返回的具体的resp对象转换成符合HTTP协议的响应报文,返回给浏览器

🌟总而言之,doGet要做的事就是根据请求计算响应

(HttpServletRequest req, HttpServletResponse resp两个参数,req代表HTTP的请求内容;resp代表要返回HTTP的响应内容)


(3)关于异常

ServletException是Servlet程序中常见的异常, IOException是读写文件之类常见的异常


(4)在刚打出doGet方法的时候,会出现super()这行代码,切记这段代码一定要删掉

(否则访问网页时会出现405,下文会详细说)


(5)此时的这个resp是未初始化的空的响应对象

(6)通过getWriter()就可以获取到resp中内置的输出流对象,Writer就是字符流

(7)再使用wirte方法就可以把一个字符串写到resp对象中,此时resp对象就有属性了,字符串具体是写到HTTP响应的Body中,浏览器拿到Body的内容,就可以显示在界面上了


(8)最后要在类的上方加个注解,即@WebServlet("/hello")

🌟注意:必须要加 /

💗作用:配置路由;表示 Tomcat 收到的请求中, 路径为/hello的请求才会调用HelloServlet这个类的代码


💜问题:为什么没有main方法,那程序怎么运行?

💚答:Servlet程序不需要main方法,换句话说,main方法其实是在Tomcat里,我们写的这些doGet、doPost等等之类的方法都是让Tomcat调用的,Tomcat去执行我们写的代码

2.整体思路与步骤

(5)打包程序

1.为什么需要打包

💗我们写好的代码及程序是被Tomcat调用去运行的,我们需要打成一个Tomcat能识别的包的格式,此时代码才会被Tomcat执行

2.打包整体流程

①打包我们也是借助Maven来完成的


②打开IDEA右侧的Maven

(如果右侧没有,就可以:菜单➜View➜Tool Window➜Maven打开)


③然后展开Lifecycle,双击package或者右键package选择运行即可进行打包


④如果出现了BUILD SUCCESS,就说明打包成功了


⑤此时看一下左侧的目录,可以看到新出现的target目录

target目录下有一个jar包,这个jar包就是我们刚刚通过Maven打包生成的内容


⑥但是对于Tomcat来说,它需要的并非是jar包,而是war包!


⑦此时我们就需要修改成war包

🌟以下这些操作都是在<project></project>标签内

(1)先打开pom.xml,加上一个标签<packaging></packaging>,在里面写一个war

(2)再在pom.xml中新增一个build标签, 内置一个finalName标签, 表示打出的war包的名字是 HelloServlet


⑧修改完上述代码之后,我们再重新打包

依然是打开IDEA右侧的Maven,展开Lifecycle,双击package或者右键package选择运行

此时出现了BUILD SUCCESS,也说明打包成功了


⑨此时再看看左侧的target目录,就出现了我们刚刚打包的war包


⑩然后再右键➜open in➜Explorer,选择在文件中打开

这就代表打包完成,就可以把包放到Tomcat运行了

3.完整的pom.xml代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>hello_servlet</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <packaging>war</packaging>

    <build>
        <finalName>HelloServlet</finalName>
    </build>
</project>

(6)部署程序

💗即把这个war包拷贝到Tomcat的webapps目录中


🌟当复制进来之后,Tomcat就能够识别到有新的webapp来了,就会自动对这个war包进行解压缩

💚再重新启动Tomcat,出现以下日志说明Tomcat已经正确识别了HelloServlet这个webapp

(7)验证程序

此时通过浏览器访问 127.0.0.1:8080/HelloServlet/hello 即可看到

💓如果一直转圈打不开,就去打开Tomcat的cmd里敲几下回车


🌟注意: URL 中的 PATH 分成两个部分,也就是两个路径

💜第一级路径也称为Context Path,也就是解压缩的war包名

💜第二级路径也称为Servlet Path,也就是与@WebServlet相对应的内容

①HelloServlet 为Context Path

②hello为Servlet Path


三:更方便的部署方式

(1)为什么需要更方便的部署

💜从上文可看出手动拷贝war包到Tomcat的步骤很麻烦,因为如果修改内容,就每次都得重新打包,重复上述(5)(6)(7)步骤


💗更方便的部署方式:使用IDEA中的Smart Tomcat插件完成这个工作

(插件就是对程序的一些特定场景, 做出一些特定的功能的扩展)

(2)安装Smart Tomcat插件

①File➜Settings


②Editor➜选择Plugins➜选择Marketplace➜搜索"tomcat"➜点击"Install"


③安装完毕之后, 重启下IDEA

(3)配置Smart Tomcat插件

①点击右上角的 "Add Configuration"


②点击左顶角的+号;选择"Smart Tomcat"


③配置细节解析

Name:这个是可选的;配不配置都行,名字可任取

Tomcat Server:这一栏选择Tomcat所在的目录

Deployment  directory:IDEA会把这个目录加载到Tomcat中

Context Path:默认填写的值是项目名称;这会影响到后面咱们的访问页面

(其他的选项不必做出修改)


④点击OK,右上角变成如下图所示


⑤此时点击绿色的三角号,IDEA 就会自动进行编译部署,启动Tomcat的过程

出现以下图片所示,证明启动成功,此时Tomcat日志就会输出在 IDEA 的控制台中, 可以看到现在就不再乱码了

(4)重新运行即可修改内容

①比如说我在这里修改了打印的内容


②此时我只需要重新运行启动Tomcat即可,不用再打包之类的繁琐步骤


③再次访问网页

🌟注意:hello_servlet是你刚刚配置的Context path;hello即是@WebServlet里的路径

四:访问出错怎么办

(1)出现404

💗常见原因:404表示用户访问的资源不存在;大概率是URL的路径写的不正确


🌟原因一:少写了端口号


🌟原因二:少写了Context Path或者Servlet Path


🌟原因三:Servlet Path写的和@WebServlet注解的路径不同


🌟原因四:web.xml写错了

(2)出现405

💗常见原因:405表示对应的HTTP请求方法没有实现


🌟原因一:写的方法和请求发起的方法是不匹配的

(比如浏览器发起的是GET请求,服务器代码写的是doPost方法)


🌟原因二:写的方法和请求发起的方法是匹配的,但是没有删除IDEA自动给的super代码

(3)出现500

💗常见原因:往往是Servlet代码中抛出异常导致的


例如:

①空指针异常

②再次访问网页时

③根据异常修改代码

(4)出现空白页面

💗常见原因:少写了响应需要返回的内容

(比如少写了resp.getWritter().write()操作)

(5)无法访问此网站

💗常见原因:Tomcat启动失败了或者没有正确启动


🌟原因一:@WebServlet中的内容少写了“/”


🌟原因二:IP或者端口号编写错误

五:Servlet API详解

🌟Servlet的类是非常多的,我们只需要学习三个常用的类即可

(1)HttpServlet

1.作用

💗我们写Servlet代码,首先第一步就是先创建类,继承自HttpServlet,并重写其中的方法

2.核心方法


①init:webapp被加载的时候执行

💙作用:适合使用这个方法进行一些初始化

💛注意:使用情况不多


②destory:webapp被销毁的时候执行,即Tomcat结束时执行

💙作用:适合使用这个方法进行一些收尾工作

💛注意:使用情况也不多;这个方法不一定被调用到,当强制杀死Tomcat进程的情况下不一定能被执行


③service:每次收到HTTP请求时都需要调用

💙作用:适合使用这个方法处理每个请求

💛注意:使用情况也不多;并且这个方法一般都会被doXXX方法替代


④doGet、doPost、doPut等等这些方法是用的比较多的

3.Servlet生命周期(面试题)

①webapp刚被加载时(Tomcat启动时),调用servlet的init方法

②每次收到请求时,调用servlet的service方法

③webapp要结束时,调用servlet的destory方法


(2)HttpServletRequest

1.作用

💗当Tomcat通过Socket API读取HTTP请求(字符串)时, 并且按照HTTP协议的格式把字符串解析成HttpServletRequest对象


🌟一个HTTP请求里有哪些信息,都会在这个HttpServletRequest类里面体现

(HttpServletRequest实际就是用来操作请求的)


💜请求对象req,我们拿到之后,是为了获取里面的属性(读)

(一般是读取数据,所以是InputStream)

2.核心方法


🌟针对HTTP请求中的URL

①String getRequestURI():如果请求的URL为“http://example.com/myapp/servlet1”,那么req.getRequestURI将返回/myapp/servlet

②String getContextPath():如果请求的URL为“http://example.com/myapp/servlet1”,那么req.getContextPath将返回/myapp


🌟针对HTTP请求中的query string

①Enumeration getParameterNames():拿到键值对中的key

②String getParameter(String name):通过键值对中的key拿到对应的value

(getParameter的传参传的是key)

(我们需要知道,可以通过query string给服务器传输自定义的数据,而query string本身就是键值对结构的数据,Tomcat在收到请求之后,就会把query string解析成Map这样的键值对,使用getParameter就可以根据key获取value)


🌟针对HTTP请求中Header的键值对

①Enumeration getHeaderNames() 拿到Header键值对的key

②String getHeader(String name)通过Header键值对中的key拿到对应的value

(getHeader传参传的是key)


🌟针对HTTP请求中Header的特殊情况

①String getCharacterEncoding():拿到当前请求使用的字符编码,如果Header中Content-Type没有指定就返回默认的字符编码

②String getContentType():根据Header的Content-Type拿到Body的类型

③int getContentLength():根据Header的Content-Length拿到Body的长度


🌟针对HTTP请求中的Body

InputStream getInputStream():读取请求的Body内容

3.测试方法

①代码案例:

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
//<br>是HTTP特定的换行标签,HTTP的换行不可以使用\n什么的

@WebServlet("/request")
public class RequestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //这个resp响应的操作,它的作用在于告诉浏览器(客户端)的响应的内容类型是html,浏览器要按照html的格式显示出内容
        resp.setContentType("text/html");

        //调用req的各种方法,把得到的结果汇总到一个字符串中,统一返回到页面上
        StringBuilder respBody = new StringBuilder();
        respBody.append(req.getProtocol());      //获取请求的名称和版本号
        respBody.append("<br>");
        respBody.append(req.getMethod());        //获取请求的方法
        respBody.append("<br>");
        respBody.append(req.getRequestURI());    //获取请求的URI
        respBody.append("<br>");
        respBody.append(req.getContextPath());   //获取请求的目录路径
        respBody.append("<br>");
        respBody.append(req.getQueryString());   //获取请求的URL中的查询字符串(query string)
        respBody.append("<br>");

        //拼接Header
        Enumeration<String> headers = req.getHeaderNames();   //获取Header中所有的key
        while(headers.hasMoreElements()){                     //使用迭代器,判断有没有下一个key
            //使用header接收获取到的每一个key
            String header = headers.nextElement();
            //获取到key对应的value追加到字符串
            respBody.append(header+":"+req.getHeader(header));
            respBody.append("<br>");
        }

        //统一返回结果
        resp.getWriter().write(respBody.toString());
    }
}

②运行结果:

4.获取自定义的query string数据

①代码案例:

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

@WebServlet("/parameter")
public class ParameterServlet extends HttpServlet {
    //约定客户端使用query string传递数据
    //query string形如:username=zhangsan&password=123

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username= req.getParameter("username");
        String password= req.getParameter("password");
        System.out.println("username="+username);  //获取到query string键值对中key为username的value值
        System.out.println("password="+password);  //获取到query string键值对中key为password的value值
        resp.getWriter().write("ok");
    }
}

②测试结果:

💖利用POSTMAN构造请求

(1)当query string是正常情况下


(2)当password是没有赋值的情况下


(3)只有username没有password的情况下

5.获取请求的Body(只考虑form表单的格式)

①form表单格式:form表单格式和query string键值对格式一样,都是使用&和=分割


②代码案例:

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


@WebServlet("/parameter2")
public class Parameter2Servlet extends HttpServlet {
    //预期让客户端发送一个Post请求,同时使用form表单格式的数据,在Body中把数据传递过来
    //Body形如:username=zhangsan&password=123

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        System.out.println("username="+username);
        System.out.println("password="+password);
        resp.getWriter().write("ok,i get it");
    }
}

③使用POSTMAN利用form表单构造Body:


④测试结果:

6.获取请求的Body(只考虑json格式)

①json格式:

🌟注:Servlet自身是不能对json格式进行解析,需要引入第三方库解析json


②jackson库:解析json格式的第三方库;Spring里面解析json用的就是jackson

(因为jackon库也是属于第三方库,所以也要通过maven从中央仓库下载并导入到项目中来)

(1)打开中央仓库➜中央仓库

(2)搜索jackon,然后选择jackon Databind

(3)这里我们就选择2.14.2的版本(其实这里的版本都可以选,但是建议不要使用太新的版本)

(4)同样复制如下图的代码

(5)在编写到pom.xml的时候,同样得放进<dependencies></dependencies>(前面说过依赖可以有多个)


③ObjectMapper类:这个类提供的方法可以将Java对象和json字符串相互转换

🌟(1)readValue():作用就是把json字符串解析成Java对象

(版本有很多个,我们选择参数类型是如下图所示的)

🖤第一个参数:表示一个流对象,表示json从哪里来读,一般来说请求的body是通过getInputStream得到的一个流对象

🖤第二个参数:指定对象类型,表示当前得到的json字符串你需要转成什么样的Java对象,需要指定一下对象的类型 

🌟(2)writeValueAsString():作用是把Java对象转换成json字符串;参数填的是Java对象


④代码案例:

import com.fasterxml.jackson.databind.ObjectMapper;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

class User{
    public String username;
    public String password;
}

@WebServlet("/json")
public class JsonServelt extends HttpServlet {
    //此处约定客户端的body按照json格式进行传输
    //body格式形如:
    //{
    //   username:"zhangsan"
    //   password:"123"
    //}
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        User user = objectMapper.readValue(req.getInputStream(),User.class);
        System.out.println("username="+user.username+",password="+user.password);
        resp.getWriter().write("ok");
    }
}

⑤使用POSTMAN利用json格式构造Body:

🌟步骤:Body➜raw➜JSON


⑥测试结果:

(3)HttpServletResponse

1.作用

①Servlet中的doXXX方法的目的就是根据请求计算得到相应,然后把响应的数据设置到HttpServletResponse对象中

②然后Tomcat就会把这个HttpServletResponse对象按照HTTP协议的格式,转成一个字符串, 并通过Socket写回给浏览器


🌟HttpServletResponse实际就是用来操作响应的


💜响应对象resp,我们拿到之后,是为了设置里面的属性(写)

(一般是写入数据,所以是OutPutStream)

2.核心方法


💙注意: 对于状态码/Header的设置要放到getWriter / getOutputStream之前;否则可能设置失效(很重要!下文详细说)


🌟针对HTTP响应的Header 

①void setHeader(String name, String value):给定的Header若不存在则直接添加,存在则覆盖旧的使用新的

②void addHeader(String name, String value):给定的Header若不存在则直接添加,存在也不覆盖旧的,新旧都一起用,此时会出现key相同的键值对

③void setContentType(String type):设置Body的内容格式

(同时,字符集也可以在ContentType里面设置)

(当你返回文字的时候,切记一定要在getWriter / getOutputStream前面加上这条代码,设置一下格式和字符集,否则文字会无法显示)


🌟针对HTTP响应的字符集

void setCharacterEncoding(String charset):告诉浏览器按照哪种字符集的编码方式进行解析响应的Body;如果没有说清楚,那么浏览器显示的内容就会乱码


🌟设置重定向响应

void sendRedirect(String location):这个方法用来设置“重定向”响应;即状态码为302或者301的


🌟针对HTTP响应中的Body

①PrintWriter getWriter():往Body写入文本数据

②OutputStream getOutputStream():往Body写入二进制数据

3.测试方法

①使用void setStatus(int sc)方法设置状态码

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

@WebServlet("/status")
public class StatusServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setStatus(200);
    }
}


②使用void sendError()设置Tomcat内置自带的状态码页面格式

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

@WebServlet("/status")
public class StatusServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.sendError(404);
    }
}


③使用void setHeader(String name, String value)设置报头

(此处我们设置一个key为refresh属性,用来表示浏览器每隔value秒自动刷新)

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

@WebServlet("/refresh")
public class RefreshServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setHeader("refresh","2");
        //返回一个系统时间,方便我们去观察效果
        resp.getWriter().write("time:"+System.currentTimeMillis());
    }
}

💘打开页面之后每隔2秒就会自动刷新,具体现象你们得运行代码打开浏览器后才可观察

💘通过Fiddler抓包也可以看到多了refresh的属性


④使用void sendRedirect(String location)构造一个重定向响应

(重定向响应,一定要设置一个key为Location的属性,代表我们重定向转去哪个网页)

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

//让页面被重定向到搜狗主页
@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //方法一:
        //设置重定向的状态码302
        resp.setStatus(302);
        //重定向响应,一定要设置一个key为Location的属性,代表我们重定向转去哪个网页
        resp.setHeader("Location","https://www.sogou.com");
        
        //方法二:
        //使用sendRedirect方法一步到位
        resp.sendRedirect("https://www.sogou.com");
    }
}

💘可以看到,自动转换为了搜狗的主页

💘通过Fiddler抓包可以看到响应的状态码302和Location属性

(当浏览器见到这两个东西之后,就知道下一步要重定向到搜狗)


⑤使用PrintWriter getWriter()往响应的Body写入一个文本数据

(此时还需要使用setContentType设置字符集,否则页面无法显示汉字)

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

@WebServlet("/body")
public class BodyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //需要声明一下字符集是utf8,因为我下面传输的是一个文本,是汉字
        resp.setContentType("text/html;charset=utf8");
        //让服务器响应返回一个html数据(html本身是一个文本)
        resp.getWriter().write("<div>你好</div>");
    }
}

💛注意:核心方法那里我们说过Header的设置要放到getWriter / getOutputStream之前,否则会失效

如果设置Header的ContentType放在getWriter之后,我们看看会触发什么样的效果

可以看到字符集效果失效了,没有显示文字

六:表白墙-前后端交互

(1)Web前端代码(未与后端交互版)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>表白墙</title>
    <style>
        /* * 通配符选择器, 是选中页面所有元素 */
        * {
            /* 消除浏览器的默认样式. */
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        .container {
            width: 600px;
            margin: 20px auto;
        }

        h1 {
            text-align: center;
        }

        p {
            text-align: center;
            color: #666;
            margin: 20px 0;
        }

        .row {
            /* 开启弹性布局 */
            display: flex;
            height: 40px;
            /* 水平方向居中 */
            justify-content: center;
            /* 垂直方向居中 */
            align-items: center;
        }

        .row span {
            width: 80px;
        }

        .row input {
            width: 200px;
            height: 30px;
        }

        .row button {
            width: 280px;
            height: 30px;
            color: rgb(253, 253, 253);
            background-color: rgb(147, 7, 240);
            /* 去掉边框 */
            border: none;
            border-radius: 5px;
        }

        /* 点击的时候有个反馈 */
        .row button:active {
            background-color: grey;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>表白墙</h1>
        <p>输入内容后点击提交, 信息会显示到下方表格中</p>
        <div class="row">
            <span>匿名者: </span>
            <input type="text">
        </div>
        <div class="row">
            <span>表白对象: </span>
            <input type="text">
        </div>
        <div class="row">
            <span>悄悄话: </span>
            <input type="text">
        </div>
        <div class="row">
            <button id="submit">提交</button>
        </div>
        <div class="row">
            <button id="revert">撤销</button>
        </div>
        <!-- <div class="row">
            xxx 对 xx 说 xxxx
        </div> -->
    </div>

    <script>
        // 实现提交操作. 点击提交按钮, 就能够把用户输入的内容提交到页面上显示. 
        // 点击的时候, 获取到三个输入框中的文本内容
        // 创建一个新的 div.row 把内容构造到这个 div 中即可. 
        let containerDiv = document.querySelector('.container');
        let inputs = document.querySelectorAll('input');
        let button = document.querySelector('#submit');
        button.onclick = function() {
            // 1. 获取到三个输入框的内容
            let from = inputs[0].value;
            let to = inputs[1].value;
            let msg = inputs[2].value;
            if (from == '' || to == '' || msg == '') {
                return;
            }
            // 2. 构造新 div
            let rowDiv = document.createElement('div');
            rowDiv.className = 'row message';
            rowDiv.innerHTML = from + ' 对 ' + to + ' 说: ' + msg;
            containerDiv.appendChild(rowDiv);
            // 3. 清空之前的输入框内容
            for (let input of inputs) {
                input.value = '';
            }
        }
        let revertButton = document.querySelector('#revert');
        revertButton.onclick = function() {
            // 删除最后一条消息. 
            // 选中所有的 row, 找出最后一个 row, 然后进行删除
            let rows = document.querySelectorAll('.message');
            if (rows == null || rows.length == 0) {
                return;
            }
            containerDiv.removeChild(rows[rows.length - 1]);
        }
    </script>
</body>
</html>

(2)Java后端代码(与前端交互版)

1.创建Maven项目,取名为messageWall
2.打开中央仓库,在pom.xml中添加Servlet3.1版本和jackson2.14.2版本的依赖
3.创建必要的项目/目录,往web.xml里写必要的代码

(🌟前面1、2、3步就是重复上文讲过的准备工作,忘记了可以回到目录二去查看)

4.把由前端代码组成的messageWall.html复制到webapp目录下

5.配置好后并运行Smart Tomcat

6.使用浏览器打开表白墙

7.编写后端代码前的思路整理

8.简单编写后端代码(服务器是List的版本)

①正式代码

import com.fasterxml.jackson.databind.ObjectMapper;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

//这个类的属性必须和约定好的JSON格式中的数据匹配
class Message{
    public String from;
    public String to;
    public String message;

    @Override
    public String toString() {
        return "Message{" +
                "from='" + from + '\'' +
                ", to='" + to + '\'' +
                ", message='" + message + '\'' +
                '}';
    }
}

@WebServlet("/message")
public class MessageServlet extends HttpServlet {
    //定义ObjectMapper对象,这是jackson库,我们用它来解析JSON格式数据
    private ObjectMapper objectMapper = new ObjectMapper();

    //这里我们使用List来保存Message对象,相当于一个服务器的功能
    //只不过我们是把数据消息保存到内存当中,一旦重启服务器,内存数据就丢失了(更科学的做法其实是保存到数据库)
    private List<Message> messageList = new ArrayList<>();


    //1.doPost用来实现存档功能
    //因为前端我们约定Body数据以JSON格式传递给后端,这里后端我们就需要把JSON格式数据转化为类对象
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //(1)读取请求的Body转换成Java类对象,我们用message表示对象
        Message message = objectMapper.readValue(req.getInputStream(),Message.class);
        //(2)得到message后,我们就需要把它保存到服务器中
        messageList.add(message);
        System.out.println("服务器收到message"+message);
        //(3)返回响应(响应返回的ok这个写不写都行;但是状态码必须写)
        resp.setStatus(200);
        resp.getWriter().write("ok");
    }


    //2.doGet用来实现读档功能
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //(1)把内存中的Message,组织成JSON格式返回到响应中
        String respJson = objectMapper.writeValueAsString(messageList);
        //(2)把生成的JSON格式字符串返回到响应对象中
        resp.setContentType("application/json;charset=utf8");
        resp.getWriter().write(respJson);
    }
}

②代码解析

9.正式编写后端代码(服务器是数据库的版本)

🌟上述List的服务器版本,在重启之后并不能保存原有的数据,要想保存原有数据,还得依靠数据库

①先引入数据库的依赖(驱动包)

(同样可以使用maven,把数据库驱动包下载并进行管理)


步骤:

(1)点击➜中央仓库

(2)搜索mysql,选下图所示

(3)选5.1.49版本,复制Maven代码

(4)复制到pom.xml里

(注意:依然得放到<dependencies>标签里)


②创建数据库,创建数据表


③编写代码

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

//这个类的属性必须和约定好的JSON格式中的数据匹配
class Message{
    public String from;
    public String to;
    public String message;

    @Override
    public String toString() {
        return "Message{" +
                "from='" + from + '\'' +
                ", to='" + to + '\'' +
                ", message='" + message + '\'' +
                '}';
    }
}

@WebServlet("/message")
public class MessageServlet extends HttpServlet {
    //定义ObjectMapper对象,这是jackson库,我们用它来解析JSON格式数据
    private ObjectMapper objectMapper = new ObjectMapper();


    //1.doPost用来实现存档功能
    //因为前端我们约定Body数据以JSON格式传递给后端,这里后端我们就需要把JSON格式数据转化为类对象
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //(1)读取请求的Body转换成Java类对象,我们用message表示对象
        Message message = objectMapper.readValue(req.getInputStream(),Message.class);
        //(2)得到message后,我们就需要把它保存到数据库中
        try {
            save(message);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        System.out.println("服务器收到message"+message.toString());
        //(3)返回响应(响应返回的ok这个写不写都行;但是状态码必须写)
        resp.setStatus(200);
        resp.getWriter().write("ok");
    }


    //2.doGet用来实现读档功能
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //(1)把保存在数据库中的Message,组织成JSON格式返回到响应中
        List<Message> messageList = null;
        try {
            messageList = load();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        String respJson = objectMapper.writeValueAsString(messageList);
        //(2)把生成的JSON格式字符串返回到响应对象中
        resp.setContentType("application/json;charset=utf8");
        resp.getWriter().write(respJson);
    }


    //通过JDBC,往数据库存一个数据
    private void save(Message message) throws SQLException {
        //(1)创建数据源
        DataSource dataSource = new MysqlDataSource();
        ((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/servlet?characterEncoding=utf8&AllowPublicKeyRetrieval=True");
        ((MysqlDataSource)dataSource).setUser("root");
        ((MysqlDataSource)dataSource).setPassword("hlizoo777");

        //(2)与数据库服务器建立连接
        Connection connection = dataSource.getConnection();

        //(3)构造sql语句
        String sql = "insert into message values(?,?,?)";
        PreparedStatement statement = connection.prepareStatement(sql);
        statement.setString(1,message.from);
        statement.setString(2,message.to);
        statement.setString(3,message.message);

        //(4)执行sql语句
        statement.executeUpdate();

        //(5)释放资源,关闭连接(先创建的对象后关闭)
        statement.close();
        connection.close();
    }


    //通过JDBC,从数据库读取出数据
    private List<Message> load() throws SQLException {
        //(1)创建数据源
        DataSource dataSource = new MysqlDataSource();
        ((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/servlet?characterEncoding=utf8&AllowPublicKeyRetrieval=True");
        ((MysqlDataSource)dataSource).setUser("root");
        ((MysqlDataSource)dataSource).setPassword("hlizoo777");

        //(2)与数据库服务器建立连接
        Connection connection = dataSource.getConnection();

        //(3)构造sql语句
        String sql = "select * from message";
        PreparedStatement statement = connection.prepareStatement(sql);

        //(4)执行sql语句
        ResultSet resultSet = statement.executeQuery();

        //(5)遍历结果集合
        List<Message> messageList = new ArrayList<>();
        while(resultSet.next()){
            Message message = new Message();
            message.from = resultSet.getString("from");
            message.to = resultSet.getString("to");
            message.message = resultSet.getString("message");
            messageList.add(message);
        }

        //(6)释放资源,关闭连接(先创建的对象后关闭)
        resultSet.close();
        statement.close();
        connection.close();

        //(7)返回
        return messageList;
    }
}

④结果显示

(1)由于现在数据库没有数据,结果不容易显现出来,我们可以先添加点数据

(2)此时打开网页,就可以直接看到文本

(3)当然也可以进行输入,然后到数据库也可以查询到输入的数据

(4)就算关闭页面重新打开,文本也依旧存在没有消失,因为数据已经保存在数据库中了

(3)修改编写前端代码(与后端交互版)

1.引入jQuery

💗要让前端和后端交互,就需要使用ajax


🌟这里的ajax我们不使用原生的,而是使用jQuery提供的(具体可查看应用层-详解HTTP协议(目录九))


2.存档

①正式代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>表白墙</title>
    <style>
        /* * 通配符选择器, 是选中页面所有元素 */
        * {
            /* 消除浏览器的默认样式. */
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        .container {
            width: 600px;
            margin: 20px auto;
        }

        h1 {
            text-align: center;
        }

        p {
            text-align: center;
            color: #666;
            margin: 20px 0;
        }

        .row {
            /* 开启弹性布局 */
            display: flex;
            height: 40px;
            /* 水平方向居中 */
            justify-content: center;
            /* 垂直方向居中 */
            align-items: center;
        }

        .row span {
            width: 80px;
        }

        .row input {
            width: 200px;
            height: 30px;
        }

        .row button {
            width: 280px;
            height: 30px;
            color: rgb(253, 253, 253);
            background-color: rgb(147, 7, 240);
            /* 去掉边框 */
            border: none;
            border-radius: 5px;
        }

        /* 点击的时候有个反馈 */
        .row button:active {
            background-color: grey;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>表白墙</h1>
        <p>输入内容后点击提交, 信息会显示到下方表格中</p>
        <div class="row">
            <span>匿名者: </span>
            <input type="text">
        </div>
        <div class="row">
            <span>表白对象: </span>
            <input type="text">
        </div>
        <div class="row">
            <span>悄悄话: </span>
            <input type="text">
        </div>
        <div class="row">
            <button id="submit">提交</button>
        </div>
        <div class="row">
            <button id="revert">撤销</button>
        </div>
        <!-- <div class="row">
            xxx 对 xx 说 xxxx
        </div> -->
    </div>

    <!--这段代码的意思是引入了jQuery -->
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>

    <script>
        // 实现提交操作. 点击提交按钮, 就能够把用户输入的内容提交到页面上显示. 
        // 点击的时候, 获取到三个输入框中的文本内容
        // 创建一个新的 div.row 把内容构造到这个 div 中即可. 
        let containerDiv = document.querySelector('.container');
        let inputs = document.querySelectorAll('input');
        let button = document.querySelector('#submit');
        button.onclick = function() {
            // 1. 获取到三个输入框的内容
            let from = inputs[0].value;
            let to = inputs[1].value;
            let msg = inputs[2].value;
            if (from == '' || to == '' || msg == '') {
                return;
            }
            // 2. 构造新 div
            let rowDiv = document.createElement('div');
            rowDiv.className = 'row message';
            rowDiv.innerHTML = from + ' 对 ' + to + ' 说: ' + msg;
            containerDiv.appendChild(rowDiv);
            // 3. 清空之前的输入框内容
            for (let input of inputs) {
                input.value = '';
            }
            //4.把用户输入的数据,构造出HTTP请求并发送给服务器
            //注意:这个存档操作写到button.onclick = function()里,表示用户一点提交按钮就发送请求
            let body = {
                from:from,
                to:to,
                message:msg
            };
            $.ajax({
                type:'post',
                url:'message',
                contentType:'application/json; charset=utf8',
                data:JSON.stringify(body),
                success:function(body){
                    console.log(body);
                }
            });
        }
    </script>
</body>
</html>

②分析代码:


③显示结果:

3.读档

①正式代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>表白墙</title>
    <style>
        /* * 通配符选择器, 是选中页面所有元素 */
        * {
            /* 消除浏览器的默认样式. */
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        .container {
            width: 600px;
            margin: 20px auto;
        }

        h1 {
            text-align: center;
        }

        p {
            text-align: center;
            color: #666;
            margin: 20px 0;
        }

        .row {
            /* 开启弹性布局 */
            display: flex;
            height: 40px;
            /* 水平方向居中 */
            justify-content: center;
            /* 垂直方向居中 */
            align-items: center;
        }

        .row span {
            width: 80px;
        }

        .row input {
            width: 200px;
            height: 30px;
        }

        .row button {
            width: 280px;
            height: 30px;
            color: rgb(253, 253, 253);
            background-color: rgb(147, 7, 240);
            /* 去掉边框 */
            border: none;
            border-radius: 5px;
        }

        /* 点击的时候有个反馈 */
        .row button:active {
            background-color: grey;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>表白墙</h1>
        <p>输入内容后点击提交, 信息会显示到下方表格中</p>
        <div class="row">
            <span>匿名者: </span>
            <input type="text">
        </div>
        <div class="row">
            <span>表白对象: </span>
            <input type="text">
        </div>
        <div class="row">
            <span>悄悄话: </span>
            <input type="text">
        </div>
        <div class="row">
            <button id="submit">提交</button>
        </div>
        <div class="row">
            <button id="revert">撤销</button>
        </div>
        <!-- <div class="row">
            xxx 对 xx 说 xxxx
        </div> -->
    </div>

    <!--这段代码的意思是引入了jQuery -->
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>

    <script>
        // 实现提交操作. 点击提交按钮, 就能够把用户输入的内容提交到页面上显示. 
        // 点击的时候, 获取到三个输入框中的文本内容
        // 创建一个新的 div.row 把内容构造到这个 div 中即可. 
        let containerDiv = document.querySelector('.container');
        let inputs = document.querySelectorAll('input');
        let button = document.querySelector('#submit');
        button.onclick = function() {
            // 1. 获取到三个输入框的内容
            let from = inputs[0].value;
            let to = inputs[1].value;
            let msg = inputs[2].value;
            if (from == '' || to == '' || msg == '') {
                return;
            }
            // 2. 构造新 div
            let rowDiv = document.createElement('div');
            rowDiv.className = 'row message';
            rowDiv.innerHTML = from + ' 对 ' + to + ' 说: ' + msg;
            containerDiv.appendChild(rowDiv);
            // 3. 清空之前的输入框内容
            for (let input of inputs) {
                input.value = '';
            }
            //4.把用户输入的数据,构造出HTTP请求并发送给服务器
            //注意:这个存档操作写到button.onclick = function()里,表示用户一点提交按钮就发送请求
            let body = {
                from:from,
                to:to,
                message:msg
            };
            $.ajax({
                type:'post',
                url:'message',
                contentType:'application/json; charset=utf8',
                data:JSON.stringify(body),
                success:function(body){
                    console.log(body);
                }
            });
        }
        //5.在页面加载的时候,发起一个GET请求给服务器,从服务器拿到提交过的数据
        //注意:这个读档操作我是写到了<script>标签里,而没有写到button.onclick = function()里,表示页面一加载就发起请求
            $.ajax({
                type:'get',
                url:'message',
                success:function(body){
                    //此处的body其实是一个JS数组(即前面我们约定好的格式)
                    //此处我们就直接按照数组的方式操作body,每个元素都是JS对象

                    //(1)遍历数组,取出每一个JS对象
                    //(2)根据这里的JS对象,构造出页面的内容显示到页面上
                    //这句代码就在于找到上述代码的container选择器并使用它
                    let container = document.querySelector('.container');
                    for(let i =0 ; i < body.length ; i++){
                        let message = body[i];
                        message.from;
                        //这里我们需要构造html并显示到页面
                        //使用浏览器提供的api(DOM API)创建div标签
                        let div = document.createElement('div');
                        //设置一个CSS的类,应用到CSS样式
                        div.className = 'row'; 
                        //给div标签里头设置内容,此处显示一行文本(这里的文本就要显示的内容)
                        div.innerHTML = message.from + "对" + message.to + "说:" + message.message;
                        container.appendChild(div);
                    }
                }
            });
    </script>
</body>
</html>

②分析代码:


③结果显示:

💛当你把网页关掉之后又打开,下面的文本不会被删除,依旧在那里显示

七:Cookie与Session

(1)区分概念

①Cookie是浏览器存储(临时)数据的机制


②Session是服务器存储(临时)数据的机制

(作用:存储用户的详细信息;并给用户分配一个唯一值sessionid;后续再访问网站的其他页面时,HTTP请求就会自动带上sessionid,通过sessionid就能找到对应的Session即对应的用户)


③两者的区别

(2)详解Cookie

①Cookie是浏览器本地存储数据的一种机制


②作用:实现 "身份标识" 的功能;每个不同的域名下都可以有不同的Cookie, 不同网站之间的Cookie并不冲突

🌟(Cookie会存储很多键值对,往往会有一个很重要的键值对是用来表示用户的“身份信息”,即标识当前请求是来自于哪个用户的;就会产生这么一种场景,比如你登陆一个网站,后续再访问这个网站页面,就无需登录了,而且就算关了电脑,第二天重开网页,依然不需要登录)


③Cookie的原理:Cookie是按键值对的形式来存储了一些字符串,这些键值对往往都是由服务器返回回来的,浏览器把这些键值对按照“域名”维度进行分类存储,意思就是说不同网站就有不同的Cookie,例如百度有百度的Cookie,搜狗有搜狗的Cookie,这些Cookie的内容都是由程序猿自己定义的


④Cookie的保存机制:


⑤总结:

(1)Cookie从哪来?

答:Cookie是从服务器返回给浏览器的

(2)Cookie是如何保存的?保存在哪?

答:浏览器按照不同的域名分别存储Cookie,域名与域名之间的Cookie是不能互相干扰的,即每一组域名都有自己的Cookie;Cookie保存在浏览器所在电脑的硬盘上,就算关机也不会影响到

(3)Cookie中的内容是啥?

答:Cookie中的内容是键值对结构的数据,这里的键值对是由程序猿自己定义的

(4)Cookie中的内容到哪里去?

答:后续访问该网站的各个页面时,就都会在请求中带上Cookie,服务器就可以进一步知道客户端用户的详细情况 


🌟问:浏览器要保存数据为啥要先保存到Cookie再让Cookie保存到硬盘,而不能直接往硬盘写入一个文件保存?

答:往硬盘写入是绝对不行的!因为如果你让网页能够轻易的访问你的文件系统,这是一件非常危险的事情;想一下如果你上一种病毒网站,网站直接给你的电脑上下个病毒或者直接把你硬盘上已有的数据删除掉了,那不就完蛋了?

💓因此为了保证安全,浏览器会对网页的功能作出限制,禁止访问硬盘就是对网页的其中一个限制;所以为了既能保证安全也能保存数据,浏览器就提供了一个Cookie功能!

(3)了解Session

🌟Sessionid保存在Cookie中


(4)Cookie的构造方法

 Cookie cookie = new Cookie(String key,String value);

🌟设置一个Cookie,构造出它的键值对

(5)Cookie与Session的核心方法

①HttpServletRequest(请求)类中的相关方法


(1)HttpSession getSession():在服务器中获取会话;Session中最核心的API

(它会返回一个HttpSession对象,就可以调用下文③HttpSession类中的相关方法)

(参数如果为true, 则当前用户不存在Session,就新建出Session;当前用户有Session,则查询该Session)

(参数如果为false, 则当前用户存在Session,就直接查询;当前用户不存在Session时返回null)


(2)Cookie[] getCookies():获取请求中所有Cookie内容

(返回值是一个数组,数组里的每个元素都是一个Cookie对象;包含客户端发送该请求的所有的Cookie对象,会自动把Cookie中的格式解析成键值对)


②HttpServletResponse(响应)类中的相关方法


void addCookie(Cookie cookie):把指定的Cookie添加到响应中返回给浏览器

(本质上就是在HTTP响应报文里,加了一个set-Cookie的Header)


③HttpSession类中的相关方法

(一个HttpSession对象里面包含多个键值对;我们可以往HttpSession中存任何我们需要的信息,更像是一个Map<String,object>)


(1)Object getAttribute(String name):该方法返回在该session会话中具有指定名称的对象,即获取key对应的value;如果没有指定名称的对象,则返回null


(2)void setAttribute(String name, Object value):该方法使用指定的名称绑定一个对象到该session会话,即设置一个键值对

(我们可以看到value是object类的,所以value可以存任何数据)


(3)boolean isNew():判定当前是否是新创建出的会话


④Cookie类中的相关方法

(每个Cookie对象就是一个键值对)


(1)String getName():获取键值对的key;该方法返回Cookie的名称;名称在创建后不能改变

(这个值是Set Cooike字段设置给浏览器的)


(2)String getValue():获取键值对key对应的value值


(3)void setValue(String newValue):设置键值对key对应的value值

(6)测试Cookie方法

①设置自定义Cookie,期望通过这个doGet方法,把一个自定义的Cookie数据返回到浏览器

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

@WebServlet("/setcookie")
public class SetCookieServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         //设置Cookie,期望通过这个doGet方法,把一个自定义的Cookie数据返回到浏览器这边
         Cookie cookie1 = new Cookie("date","2023-9-25");
         resp.addCookie(cookie1);

         Cookie cookie2 = new Cookie("time","14:52");
         resp.addCookie(cookie2);

         resp.getWriter().write("set cookie,ok!");
    }
}

(1)页面效果

(2)抓包显示响应内容

(3)页面显示Cookie


②期望通过这个doGet方法,获取到请求中所有的Cookie

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

@WebServlet("/getcookie")
public class GetCookieServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //期望通过这个doGet方法,获取到请求中的Cookie
        Cookie[] cookies = req.getCookies();
        if (cookies!=null){
            for (Cookie cookie:cookies) {
                System.out.println(cookie.getName()+":"+cookie.getValue());
                resp.getWriter().write("get cookie,ok!");
            }
        }else{
            System.out.println("当前请求没有Cookie!");
        }
    }
}

💖当没有Cookie时:

(1)页面效果

(2)服务器效果

💖当有Cookie时:

(1)页面效果

(2)服务器效果

(7)实现登录页面效果

①实现前端代码

1.首先在webapp目录下创建一个login.html,将其用VSCode打开


2.利用form表单实现一个简单的登录页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>登录</title>
</head>
<body>
    <form action="login" method="post">
        <input type="text" name="username">
        <input type="password" name="password">
        <input type="submit" value="登录">
    </form>
</body>
</html>

3.分析逻辑


4.效果展示

②实现后端代码

💗登录的逻辑其实是固定的;首先获取账号密码,判断是否正确,正确则创建对话,并且跳转到主页


1.实现登录后端代码

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

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //第一步:获取用户名和密码
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        //判断一下username和password本身是否为空和内容是否为空
        if (username==null || password==null || username.equals(" ") || password.equals(" ") ){
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("该请求的参数不完整!");
            return;
        }

        //第二步:验证用户名和密码是否正确
        //(正常情况下,是从数据库中读取数据;在注册账号的时候,就会给数据库插入用户名和密码,登录的时候就算验证当前用户名是否存在以及密码是否正确)
        //当前为了简单,先不引入数据库,而是直接用编码判定用户名密码
        //此处约定,合法的用户名为zhangsan,密码是123,如果两个都对了,我们就认为登录成功
        if (!username.equals("zhangsan")){
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("当前用户名不正确!");
            return;
        }
        if (!password.equals("123")){
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("当前密码不正确!");
            return;
        }

        //第三步:如果前两步都没问题,此时就代表登录成功了
        //此时我们就可以给该用户创建会话
        HttpSession session = req.getSession(true);
        //此时在会话中可以顺便保存点自定义的数据,比如保存一个登录的时间戳
        session.setAttribute("username",username);
        session.setAttribute("time",System.currentTimeMillis());

        //第四步:登录成功后让页面自动跳转到网站主页
        //此处我们假设主页路径是index(到时候我们也使用Servlet生成一个动态页面)
        resp.sendRedirect("index");
    }
}

2.实现主页Index代码

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

@WebServlet("/index")
public class IndexServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //先验证一下用户登录状态(比如未登录与否)
        //验证是否登录,就看看有没有Session,有Session就证明登录成功,也就证明已经登录了
        //此时我们的参数就得是false,有会话则返回会话,没有则直接返回null,既然返回null,就证明没有登录或者登录失败
        HttpSession session = req.getSession(false);
        if (session==null){
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("该用户尚未登录!请先登录再访问主页!");
            return;
        }

        //已经登录成功,此时我们就可以取出之前的attribute
        String username = (String)session.getAttribute("username");
        Long time = (Long)session.getAttribute("time");
        System.out.println("username="+username+",time="+time);

        //根据这样的内容构造一个页面
        resp.setContentType("text/html;charset=utf8");
        resp.getWriter().write("欢迎您"+username+"! 上次您登录时间是"+time);
    }
}

3.直接登录index页面(此时还没有用户登录的效果)


4.先登录后再访问主页

(1)登录错误时

(2)登录正确时

​​​​​​​

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值