Servlet应用——JavaWeb教案(四)

文章目录

JavaWeb组件

1. ServletRequest

1. 介绍

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

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

解析

image-20211216135811805

Request体系

image-20211216140020719

获取请求消息数据

获取请求行

GET /servlet /demo1?name=zhangsan HTTP/1.1

  1. 获取请求方式: GET

    String getMethod()

  2. 获取虚拟目录: /servlet

    String getContextPath()

  3. 获取Servlet路径: /demo1

    String getServletPath( )

  4. 获取get方式请求参数: name= zhangsan

    String getQueryString()

  5. 获取请求URI : /servlet / demo1

    string getRequestURI():

  6. 获取url : http://localhost/servlet / demo1

    1. StringBuffer getRequestURL()
    2. URL:统一资源定位符: http://localhost/day14/demo1 中华人民共和国 url+文件路径
    3. URI :统一资源标识符: /day14/demo1 共和国 文件路径
  7. 获取协议及版本: HTTP/1.1

​ String getProtocol()

  1. 获取客户机的IP地址:

    String getRemoteAddr()

@WebServlet("/req/demo1")
public class RequestDemo1 extends HttpServlet {


    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("username:"+req.getParameter("username"));
        System.out.println("password:"+req.getParameter("password"));
        System.out.println("参数集合");
        Map<String, String[]> parameterMap = req.getParameterMap();
        for (String key:parameterMap.keySet()
             ) {
            String[] paramers = parameterMap.get(key);
            System.out.println(Arrays.toString(paramers));
        }
        System.out.println("请求方法"+req.getMethod());
        System.out.println("请求虚拟路径"+req.getContextPath());
        System.out.println("请求接口路径"+req.getServletPath());
        System.out.println("请求URI"+req.getRequestURI());
        System.out.println("请求URL"+req.getRequestURL().toString());
    }

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }

}

jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <link rel="stylesheet" href="/servlet/css/http.css">
</head>

<body>
<a href="/servlet/req/demo1">发送请求</a>
<form action="/servlet/req/demo1" method="get">
    <h1>发送get请求</h1>
    用户名:<input name="username" type="text">
    <br>
    密码:<input name="password" type="text">
    <br>
    <input type="submit" value="提交">
</form>
<br>

<form action="/servlet/req/demo1" method="post">
    <h1>发送post请求</h1>
    用户名:<input name="username" type="text">
    <br>
    密码:<input name="password" type="text">
    <br>
    <input type="submit" value="提交">
</form>

</body>
</html>
获取请求头
  1. String getHeader(String name ) :通过请求头的名称获取请求头的值

    1. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          //1、获取user-agent的头数据
          //告诉服务器我的浏览器版本
          String userHeader = request.getHeader("user-agent");
          System.out.println(userHeader);
          //判断浏览器版本
          if (userHeader.contains("Chrome")) {
              System.out.println("谷歌");
          } else {
              System.out.println("火狐");
          }
          System.out.println(".....................");
       //2、获取referer的头数据
          String refer = request.getHeader("referer");
          System.out.println(refer); 
           //当直接在浏览器输入时,此时为空   问题:为什么我把链接复制到文档里面打开也是null。。
           //当我通过页面访问时,就可以获取访问地址  http://localhost:8888/servlet/Login.html 
      }
      
      
  2. Enumeration getHeaderNames() ;获取所有的请求头名称

    1. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          //获取所有请求头
       Enumeration<String> headerNames= request.getHeaderNames();
          System.out.println("...................");
          System.out.println(headerNames);  //  org.apache.tomcat.util.http.NamesEnumerator@275fd6f4
          while(headerNames.hasMoreElements()){
              String name = headerNames.nextElement();
              //获取头的数据
              String value =request.getHeader(name);
              System.out.println(value);
          }
      }
      
获取超链接的所有头信息
	<a href="/servlet/demo3">访问服务器</a>

image-20211216141609662

防盗链操作

首先我们部署两个项目,一个项目是存放另一个项目的超链接,一个项目是进行消息反馈。

即一个自制电影网站,一个优酷网站。通过自制网站访问优酷内容,和优酷自身访问优酷内容。比较其差别。

项目一(优酷):

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String referer =  request.getHeader("referer");
    System.out.println(referer);
    if(referer.contains("youku")){
        System.out.println("看电影没门");
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write("盗我的视频,没有门");
    }else{
        System.out.println("请看电影");
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write("请看电影哈");
    }
}

// 访问链接:
  <a href="/youku/server">战狼</a>

refer:http://localhost:8800/servlet/

项目二(自建小网站)

访问链接:访问的是项目一

  <a href="http://localhost:8081/youku/server">战狼(盗版)</a>

refer:http://localhost:8808

获取请求体

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

  1. 获取流对象
    1. BufferedReader getReader() 字符输入流,可操作字符串类型的数据
    2. ServletInputStream getInputstream() 字节输入流,可操作所有类型的数据,图片,文档等df
      1. 相关内容在文件上传中
  2. 再从流对象中获取数据
@WebServlet("/req/demo1")
public class ServletDemoTwo extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          //获取请求消息体,即请求参数
        //1.获取字符流
        BufferedReader bf =request.getReader();
        //2.获取数据
        String line= null;
        //line=参数,如果为空,这说明没有数据了
        while ((line=bf.readLine())!=null){
            System.out.println(line);
        }
    }

发送请求:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <link rel="stylesheet" href="/servlet/css/http.css">
</head>

<body>
<a href="/servlet/req/demo1">发送请求</a>
<form action="/servlet/req/demo1" method="get">
    <h1>发送get请求</h1>
    用户名:<input name="username" type="text">
    <br>
    密码:<input name="password" type="text">
    <br>
    <input type="submit" value="提交">
</form>
<br>

<form action="/servlet/req/demo1" method="post">
    <h1>发送post请求</h1>
    用户名:<input name="username" type="text">
    <br>
    密码:<input name="password" type="text">
    <br>
    <input type="submit" value="提交">
</form>

</body>
</html>

其他功能

1. 获取请求参数通用方式
  1. String getParameter(String name) :根据参数名称获取参数值 username=zh&password=12

html代码

<form action="/servlet/req/demo1" method="get">
    <h1>发送get请求</h1>
    用户名:<input name="username" type="text">
    <br>
    密码:<input name="password" type="text">
    <br>
    <input type="submit" value="提交">
</form>
<br>

<form action="/servlet/req/demo1" method="post">
    <h1>发送post请求</h1>
    用户名:<input name="username" type="text">
    <br>
    密码:<input name="password" type="text">
    <br>
    <input type="submit" value="提交">
</form>

servlet代码

@WebServlet("/req/demo1")
public class RequestDemo1 extends HttpServlet {


    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
 System.out.println("username:"+req.getParameter("username"));
       System.out.println("password:"+req.getParameter("password"));
    }

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }
}

运行结果:

image-20211217205618431

  1. String[ ] getParametervalues(String name ) ;根据参数名称获取参数值的数组hobby=xx&hobby=game

  2. Enumeration getParameterNames( ):获取所有让求的参数名称

  3. Map<String, String[]> getParameterMap( ) :获取所有参数的map集合

Servlet代码

@WebServlet("/req/demo2")
public class RequestDemo2 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //一、根据参数名称获取参数值
        String parameter= request.getParameter("username");
       System.out.println(parameter); //1427421650
        //二、根据参数值名称获取参数值数组  针对于复选框
        String[] hobbies = request.getParameterValues("hobby");
        for (String hobby:hobbies) {
           System.out.println(hobby);//篮球,足球,橄榄球
        }
        //三、获取所有参数名称
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()){
            String name= parameterNames.nextElement();
           System.out.println(name); //username password hobby
        }
        //四、获取所有参数名称及其参数值,以键值对的方式保存
        Map<String, String[]> parameterMap = request.getParameterMap();
        //遍历方法1
        for(String key:parameterMap.keySet()) {
            System.out.println(key);
            for (int a=0;a<parameterMap.get(key).length;a++){
                System.out.println(parameterMap.get(key)[a]);
            }
            System.out.println(".......................");
        }
        //遍历方法2
        Map<String, String[]> parameterMap1= request.getParameterMap();
        Set<String> keyset = parameterMap1.keySet();
        for (String name: keyset) {
            //获取键和值
            String [] values = parameterMap.get(name);
            System.out.println(name);
            for (String value:values) {
                System.out.println(value);
            }
            System.out.println("...............................");

        }
    }

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

JSP代码

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="/servlet/req/demo2" method="get">
  <input type="username" name="username" placeholder="用户名">
  <br>
  <input type="username" name="password" placeholder="密码">
  <input type="checkbox" name="hobby" value="篮球">篮球
  <input type="checkbox" name="hobby" value="足球">足球
  <input type="checkbox" name="hobby" value="橄榄球">橄榄球
  <br>
  <input type="submit" value="提交">
</form>

</body>
</html>
2. 请求转发:

我们在写代码时,不可能将代码都写在一个类里面,所以当服务器访问Servlet接口时,通常是一个接口将信息分担给另一个接口,这样便于维护。

image-20211219202552812

步骤:

  1. 通过request对象获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path)
  2. 使用RequestDispatcher对象来进行转发: forward(ServletRequest request, ServletResponse response)

Dispatcher:调度

特点:

  1. 浏览器地址栏路径不发生变化
  2. 只能转发到当前服务器内部资源中。
  3. 转发是一次请求
1. 转发给servlet

发送请求到ForwardDemo1,之后转发到ForwardDemo2 ,再由ForwardDemo2转发到ForwardDemo3

jsp

<a href="/servlet/forward/demo1">please  enter me to servlet 03</a>

ForwardDemo1

@WebServlet("/forward/demo1")
public class ForwardDemo1 extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //方法一:创建对象进行转发
        RequestDispatcher requestDispatcher = request.getRequestDispatcher("/forward/demo2");
        requestDispatcher.forward(request,response);
        System.out.println("hello i am demo1");//转发后,当demo2执行完,后面的代码继续执行。
        //方法二:直接进行转发  注意:在同一个servlet中不能进行两次转发
        //request.getRequestDispatcher("/forward/demo3").forward(request,response);
    }
}

ForwardDemo2

@WebServlet("/forward/demo2")
public class ForwardDemo2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.getRequestDispatcher("/forward/demo3").forward(request,response);
        System.out.println("hello i am demo2");
    }
}

ForwardDemo3

@WebServlet("/forward/demo3")
public class ForwardDemo3 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("hello i am demo3");
    }
}

结果:

image-20211219202640902

2. 转发给jsp页面

请求连接

<a href="/servlet/forward/demo4">please  enter me to  forward01.jsp </a>
@WebServlet("/forward/demo4")
public class ForwardDemo4 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getRequestDispatcher("/request/forward01.jsp").forward(req,resp);
    }
}

前端页面forward01.jsp

  1. 使用EL表达式接收
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>hello demo4</h1>
</body>
3. 共享数据

**域对象:**一个有作用范围的对象,可以在范围内共享数据

request域: 代表一次请求的范围,一般用于请求转发的多个资源中共享数据

方法:

  1. void setAttribute(String name , object obj) :存储数据
  2. object getAttitude(String name) :通过键获取值
  3. void removeAttribute(String name) :通过键移除键值对
1. 转发给servlet

存储数据到request域中

    <a href="/servlet/forward/demo5">please  enter me to  servlet 05 </a>

@WebServlet("/forward/demo5")
public class ForwardDemo5 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setAttribute("name","Tom");
        //转发给servlet
        req.getRequestDispatcher("/forward/demo6").forward(req,resp);
    }
}

在其他类中获取数据

@WebServlet("/forward/demo6")
public class ForwardDemo6 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Object name = req.getAttribute("name");
        System.out.println(String.valueOf(name));
    }
}

结果:

image-20211219205608740

2. 转发给JSP
@WebServlet("/forward/demo7")
public class ForwardDemo7 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       req.setAttribute("name","Lily");
        req.getRequestDispatcher("/request/forward01.jsp").forward(req,resp);
    }
}

jsp获取数据

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>hello demo4</h1>
<span>${name}</span>
</body>
</html>

结果:

image-20211219205754357

中文乱码问题
  1. 发送get请求时,不会发生乱码问题。因为Tomcat8已解决这种问题。

  2. 当发送post请求时,如果请求内容为中文,会出现乱码问题。

    解决措施:请求参数前声明一下编码格式 request.setCharacterEncoding(“utf-8”);

servlet

@WebServlet("/mess-code/demo1")
public class MessCodeDemo1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(req.getParameter("username"));
        System.out.println(req.getParameter("password"));
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //出现乱码问题,没有解决。使用req.getParameter("")获取参数设置utf-8可以解决
        BufferedReader reader = req.getReader();
        String line = null;
        while ((line=reader.readLine())!=null){
            //使用URLDecoder进行解码
          System.out.println(URLDecoder.decode(line,"utf-8"));
        }
    }
}

jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>乱码问题:</h1>
<form action="/servlet/mess-code/demo1" method="get">
    <h1>发送get请求</h1>
    用户名:<input name="username" type="text">
    <br>
    密码:<input name="password" type="text">
    <br>
    <input type="submit" value="提交">
</form>
<br>

<form action="/servlet/mess-code/demo1" method="post">
    <h1>发送post请求</h1>
    用户名:<input name="username" type="text">
    <br>
    密码:<input name="password" type="text">
    <br>
    <input type="submit" value="提交">
</form>

结果:

image-20211219212342617

**解决方式:**设置字符编码

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("post请求");
        req.setCharacterEncoding("UTF-8");
        System.out.println(req.getParameter("username"));
        System.out.println(req.getParameter("password"));
    }

2. 如何同时运行两个项目,部署两个服务器

1. 创建一个空的项目工程文件

image-20211220103857560

2. 创建项目名称

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ihkIdErZ-1652970980045)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20211220110904991.png)]

创建两个子模块 pro_01和pro_02

注意:配置jdk版本。

image-20211220110000082

分别添加框架支持

image-20211220110413080

分别添加Tomcat服务

注意:端口号的Name不能一样。

image-20211220110618716

分别启动Tomcat服务

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZzzeBt1J-1652970980049)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20211220111149430.png)]

3. 案例(见下案例)

2. ServletResponse

1. 介绍

Response体系

image-20211216212434351

设置响应消息

设置响应行

格式 HTTP/1.1 200 OK

设置状态码:set

设置响应头

setHeader(String name, String value)

设置响应体

使用步骤:

  1. 获取输出流

    1. 字符输出流: PrintWriter getWriter()

    2. 字节输出流: Servletoutputstream getoutputStream()

  2. 使用输出流,将数据输出到客户端浏览器

@WebServlet("/reqs/demo1")
public class ResponseDemo1 extends HttpServlet {

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

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String message = "hello response";
        PrintWriter out = resp.getWriter();
        out.println("<html><body>");
        out.println("<h1>" + message + "</h1>");
        out.println("</body></html>");
    }
}

2. 案例

1. 重定向

a. 资源跳转的方式

image-20211220214039655

b. 两种方式实现,推荐选择第二种。

RedirectDemo1

@WebServlet("/redirect/demo1")
public class RedirectDemo1 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setAttribute("message", "tom");
        //方式一
        //1.1设置状态码
        resp.setStatus(302);
        //1.2设置响应头location
        resp.setHeader("location", "/servlet/redirect/demo2");
        //方式二(简单,优先使用)注意:转发需要加虚拟路径
        //resp.sendRedirect("/servlet/redirect/demo2");
    }
}

RedirectDemo2

@WebServlet("/redirect/demo2")
public class RedirectDemo2 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //重定向不支持数据共享
        Object msg = req.getAttribute("message");
        System.out.println(msg);
        String message = "hello i am demo2";
        PrintWriter out = resp.getWriter();
        out.println("<html><body>");
        out.println("<h1>" + message + "</h1>");
        out.println("</body></html>");
    }
}
c. 重定向和转发的区别
区别

面试经常考这两个的区别(forword和redirect的区别)

重定向:

  1. 页面跳转,地址栏发生变化

  2. 重定向可以访问其他站点(服务器),比如访问百度

  3. 重定向是两次请求,不能使用request域来共享数据

requset.setAttribute("name","Tom");//设置共享资源
response.sendRedirect("https://www.baidu.com/");//访问百度

Object msg = request.getAttribute("msg");  //获取共享资源   
System.out.println(msg);     //  null    获取不到

转发:

  1. 页面跳转,地址栏不发生改变
  2. 转发只能访问当前服务器下的资源
  3. 转发是一次请求,可以使用request域来共享资源
什么时候用转发,什么时候用重定向

(101条消息) 什么时候使用请求转发?而什么时候又该使用请求重定向?请求转发和请求重定向的区别,及使用_后端阿一的博客-CSDN博客_转发什么时候用重定向

d. 路径写法

路径分类:

  1. **相对路径 :**当前资源和目录资源之间相对的位置关系。 ./ 开头

    ./当前目录 …/上一级目录

  2. **绝对路径:**通过绝对路径,可以确定唯一资源。 以 / 开头

    1. 如:http://localhost:8888/Response/servletDemo2 /Response/servletDemo2 ,前面的http可以省略
    2. 判断定义的路径给谁用的,判断请求将来从哪里发出(虚拟目录什么时候使用)
      1. 客户端浏览器使用,需要添加虚拟目录(项目所在文件夹的名称)
        1. 如网页上的超链接,点击访问服务器,是客户端浏览器使用,需要添加虚拟目录
        2. ,,重定向
      2. 服务器使用,不需要添加虚拟目录
        1. 转发操作,是服务器内部访问的
        2. 注意:重定向是服务器将状态码和路径传递给客户端,客户端再通过路径访问服务器,是客户端浏览器使用,需要添加虚拟目录
//重定向:需要虚拟目录 /servlet为虚拟路径
resp.sendRedirect("/servlet/redirect/demo2");

//转发:不需要虚拟目录
request.getRequestDispatcher("/forward/demo3").forward(request,response);
e. 动态路径获取

问题:我们通过上述代码可以看出,填写的虚拟目录的路径是固定的/servlet,那么问题来了,以后假如我写非常多的接口,突然我把虚拟目录的路径改了,那么我就得把所有的虚拟目录的路径改了,这样就太麻烦了。所以我们在实际的编程中,都是通过动态获取虚拟目录的方式

动态获取虚拟目录

//动态获取虚拟目录
String contextPath = request.getContextPath();
response.sendRedirect(contextPath+"/redirect/demo2");

2. 服务器输出字符数据到浏览器

步骤:

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

ResponseDemo2

@WebServlet("/reqs/demo2")
public class ResponseDemo2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取字符输出流
        PrintWriter writer = resp.getWriter();
        //输出数据
        writer.write("hello world");
        //输出html格式数据
        writer.write("<h1>hello world<h1>");
        //输出中文数据
        writer.write("你好,我的世界");//中文乱码
    }
}

image-20211220220840974

  1. 中文乱码问题

image-20211216213515298

  1. 浏览器默认的字符编码根据操作系统的语言环境有关,win10的为GBK(中文简体)或gb312
  2. 如果服务器的字符是new出来的,也是GBK,如果是获取的,字符编码是Tomcat定义的ISO-8859-1
  3. 解决:获取流对象之前,设置编码格式 response.setContentType(“text/html;charset=utf-8”)
@WebServlet("/reqs/demo2")
public class ResponseDemo2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //解决乱码
        //方式一:
//        resp.setContentType("text/html;charset=utf-8");
        //方式二:
        //1.1获取流对象之前,把流的默认编码“ISO-8859-1”设置为:utf-8
        resp.setCharacterEncoding("utf-8");
        //1.2告诉浏览器,服务器发送的数据的编码,建议浏览器使用此编码
        resp.setHeader("content-type","text/html;charset=utf-8");
        //获取字符输出流
        PrintWriter writer = resp.getWriter();
        //输出数据
        writer.write("hello world");
        //输出html格式数据
        writer.write("<h1>hello world<h1>");
        //输出中文数据
        writer.write("你好,我的世界");//中文乱码
    }
}

image-20211220220754500

3. 服务器输出字节数据

步骤:

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

ResponseDemo3

@WebServlet("/reqs/demo3")
public class ResponseDemo3 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //方式2 设置编码格式
        resp.setContentType("text/html;charset=utf-8");
        ServletOutputStream outputStream = resp.getOutputStream();
        outputStream.write("hello world".getBytes());
    }
}

4. 验证码响应(见下案例)

3. ServletContext

1. 介绍

概念:

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

2. 获取方式

1.通过request对象获取

request . getServletContext();

2.通过HttpServlet获取

this .getServletContext();

ServletContextDemo1

@WebServlet("/context/demo1")
public class ServletContextDemo1 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext servletContext = request.getServletContext();
        ServletContext servletContext1 = this.getServletContext();
        System.out.println(request==this);//false
        System.out.println(servletContext == servletContext1);//true
        System.out.println(servletContext);
    }
}

3. 功能

1. 获取MIME类型

  1. MIME类型:在互联网通信过程中定义的一种文件数据类型

    格式:大类型/小类型 如:text/html image/jpeg ;

  2. 获取: string getMimeType(String file)

@WebServlet("/context/demo1")
public class ServletContextDemo1 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String file = "a.jpg";
        String mimeType = servletContext.getMimeType(file);
        System.out.println(mimeType);
    }
}

image-20211220223108174

2. 域对象:共享数据

a. setAttribute(String name , object value)

b. getAttribute(String name)

c. removeAttribute(String name )

ServletContextDemo2设置共享数据

@WebServlet("/context/demo2")
public class ServletContextDemo2 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      request.getServletContext().setAttribute("username","tom");
      response.sendRedirect(request.getContextPath()+"/context/demo3");
    }
}

ServletContextDemo3获得共享数据

@WebServlet("/context/demo3")
public class ServletContextDemo3 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object username = request.getServletContext().getAttribute("username");
        System.out.println(username);
        PrintWriter printWriter = response.getWriter();
        printWriter.println("hello i am"+username);
    }
}

image-20211220223502972

注意:

  1. ServletContext对象范围:所有用户请求的数据。。 张三请求的数据,张四也可以看到

  2. 该对象在服务器中贮存的时间特别长,从开始到销毁,所以用的时候需要非常谨慎。

3. 获取文件的真实路径

  1. 获取文件(服务器)的真实路径 String getRealPath(“”);

  2. 文件在不同路径下的获取方法

@WebServlet("/context/demo4")
public class ServletContextDemo4 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext servletContext = request.getServletContext();
        //获取当前路径
        String realPath1 = servletContext.getRealPath("/");
        System.out.println(realPath1);
        //获取类路径下文件
        String realPathA = servletContext.getRealPath("/WEB-INF/classes/ConfigurationA");
        System.out.println(realPathA);
        //获取WEB-INF下文件
        String realPathB = servletContext.getRealPath("/WEB-INF/ConfigurationB");
        System.out.println(realPathB);
        //获取web下文件
        String realPathC= servletContext.getRealPath("/ConfigurationC");
        System.out.println(realPathC);
    }
}

image-20211220224331933

真实路径

G:\ComputerStudy\cloudnote\knowledge\knowledges\mystudy\AllCode\JavaWebCode\knowledges_show\out\artifacts\knowledges_show_war_exploded\

G:\ComputerStudy\cloudnote\knowledge\knowledges\mystudy\AllCode\JavaWebCode\knowledges_show\out\artifacts\knowledges_show_war_exploded\WEB-INF\classes\ConfigurationA

G:\ComputerStudy\cloudnote\knowledge\knowledges\mystudy\AllCode\JavaWebCode\knowledges_show\out\artifacts\knowledges_show_war_exploded\WEB-INF\ConfigurationB

G:\ComputerStudy\cloudnote\knowledge\knowledges\mystudy\AllCode\JavaWebCode\knowledges_show\out\artifacts\knowledges_show_war_exploded\ConfigurationC

4. 案例

文件下载

在浏览器中超链接的图片可以直接查看,但是某些视频需要下载获取.

注意:

在tomcat服务中,静态资源要保存在web目录下,否则无法直接访问图片。

需求:
  1. 页面显示超链接

  2. 点击超链接后弹出下载提示框

  3. 完成图片文件下载

分析:
  1. 超链接指向的资源如果能够被浏览器解析,则在浏览器中展示,如果不能解析,则弹出下载提示框。不满足需求(图片可以直接被浏览器解析,但是视频是不可以的)

  2. 任何资源都必须弹出下载提示框

  3. 使用响应头设置资源的打开方式: content-disposition: attachment ;filename=xxx

步骤:
  1. 定义页面,编辑超链接href属性,指向Servlet,传递资源名称filename

  2. 定义Servlet

    1. 获取文件名称

    2. 使用字节输入流加载文件进内存

    3. 指定response的响应头: content- disposition: attachment; filename=xxx

      image-20211216215325140

    4. 将数据写出到response输出流

代码展示

file.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<a href="/servlet/img/lion.jpg">狮子图片</a>
<a href="/servlet/img/球员.jpg">球员图片</a>
<a href="/servlet/img/movie.mp4">柯南视频</a>
<br>
<a href="/servlet/file?filename=lion.jpg">狮子图片下载</a>
<a href="/servlet/file?filename=球员.jpg">球员图片下载</a>
<a href="/servlet/file?filename=movie.mp4">柯南视频下载</a>

</body>
</html>

FileHandle接口:

接口中,在获取文件名之前,先解析文件名。

@WebServlet("/file")
public class FileHandle extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取请求参数,文件名称
        String fileName = request.getParameter("filename");
        System.out.println(fileName); //photo

        //2.使用字节流,加载文件进内存
        //2.1找到文件所在的服务器路径
        ServletContext servletContext = request.getServletContext();
        String realPath = servletContext.getRealPath("/img/" + fileName);//图片的真是路径
        //2.2使用字节流关联
        FileInputStream fis = new FileInputStream(realPath);
        //自己理解为,通过此步骤,可以获取从浏览器把文件保存到本地的权限


        //3.设置response响应头
        //3.1设置响应头的数据类型:content-type  //因为我们也不知道文件的类型,所以需要设置。
        String mimeType = servletContext.getMimeType(fileName);
        response.setHeader("content-type", mimeType);
        //3.1.1解决中文文件名问题
        //获取user-agent请求头
        String agent = request.getHeader("user-agent");


        //3.2设置响应头的打开方式
        response.setHeader("content-disposition", "attachment;filename=" +  DownLoadUtils.getFileName(agent, fileName));//filename设置的为下载提示框的名字

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

文件夹:

image-20211220234551019

中文文件名称无法显示问题
<a href="/servlet/file?filename=球员.jpg">球员图片下载</a>

image-20211216215541385

解决思路

  1. 获取客户端使用的浏览器版本信息

  2. 根据不同的浏览器版本信息,设置filename的编码 式不同

image-20211216215627980

封装的代码

import sun.misc.BASE64Encoder;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

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;

    }
}

4.Cookie和Session

优秀blog

(104条消息) Session是怎么实现的?存储在哪里?_码农的博客-CSDN博客_session怎么存储数据

cookie和session的详解和区别

https://www.cnblogs.com/accolade/p/10817529.html

会话技术

介绍

Cookie和Session属于会话技术,cookie存储在浏览器上,session存储在服务器中。

**会话:**一次会话中包含多次请求和响应。

image-20211217221820107

**一次会话:**浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开为止。

就好比打电话,A给B打电话,接通之后,会话开始,直到挂断电话,该次会话就结束了,而浏览器访问服务器,就跟打电话一样,浏览器A给服务器发送请求,访问web程序,该次会话就已经接通,其中**不管浏览器发送多少请求(就相当于接通电话后说话一样),都视为一次会话,**直到浏览器关闭,本次会话结束。其中注意,一个浏览器就相当于一部电话,如果使用火狐浏览器,访问服务器,就是一次会话了,然后打开google浏览器,访问服务器,这是另一个会话,虽然是在同一台电脑,同一个用户在访问,但是,这是两次不同的会话。

**功能:**在一次会话的范围内的多次请求间,共享数据

方式:

  1. 客户端会话技术: Cookie
  2. 服务器端会话技术: Session

Cookie和Session之间的区别和联系

假如一个咖啡店有喝5杯咖啡免费赠一杯咖啡的优惠,然而一次性消费5杯咖啡的机会微乎其微,这时就需要某种方式来纪录某位顾客的消费数量。想象一下其实也无外乎下面的几种方案:

  1. 该店的店员很厉害,能记住每位顾客的消费数量,只要顾客一走进咖啡店,店员就知道该怎么对待了。这种做法就是协议本身支持状态。但是http协议本身是无状态的

  2. 发给顾客一张卡片,上面记录着消费的数量,一般还有个有效期限。每次消费时,如果顾客出示这张卡片,则此次消费就会与以前或以后的消费相联系起来。这种做法就是在客户端保持状态。也就是cookie。 顾客就相当于浏览器,cookie如何工作,下面会详细讲解

  3. 发给顾客一张会员卡,除了卡号之外什么信息也不纪录,每次消费时,如果顾客出示该卡片,则店员在店里的纪录本上找到这个卡号对应的纪录添加一些消费信息。这种做法就是在服务器端保持状态。(session)

由于HTTP协议是无状态的,而出于种种考虑也不希望使之成为有状态的,因此,后面两种方案就成为现实的选择。具体来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案。同时我们也看到,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的。

举例:

网上购物时,添加一次购物车,就是一次请求,我将多个物品添加到购物车,就是多次请求,最后计算总价时,就需要将之前请求的数据进行一下总和,cookie就是做这个总和的。

Cookie

cookie介绍

会员卡的第二种方案。

客户端会话技术,将数据保存到客户端。Cookie通过在客户端记录信息确定用户身份Session通过在服务器端记录信息确定用户身份

使用步骤:

  1. 创建cookie对象,绑定数据 new Cookie(String name,String value )
  2. 发送cookie对象 response.addCookie(Cookie c);

CookieDemo1

@WebServlet("/cookie/demo1")
public class CookieDemo1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        //1.创建cookie对象
        Cookie c = new Cookie("msg", "hello");
        //2.发送cookie
        response.addCookie(c);
    }
}
  1. 获取cookie,拿到数据

CookieDemo2

@WebServlet("/cookie/demo2")
public class CookieDemo2  extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        //3.获取cookie
        Cookie[] cookies = request.getCookies();
        if(cookies!=null){
            for (Cookie c:cookies) {
                String name = c.getName();
                String value  =c.getValue();
                System.out.println(name+":"+value);   //msg:hello
            }
        }
    }
}

两次请求的response和request头对比

第一次请求:

第一次请求的请求头是没有cookie的,如果有,是浏览器缓存,清除一下即可。

image-20211221000634348

第二次请求:

image-20211221000746518

总结:

  1. 第一次请求时,是获取浏览器中的数据,所以第一次request请求未携带任何cookie信息。
  2. 第一次请求完毕后,返回的response将第一次创建的cookie数据返回
  3. 第二次请求时,浏览器拿着第一次response返回的cookie数据发送到服务器

image-20211217222503995

Cookie细节

1. 一次请求发送多个Cookie

CookieDemo3

@WebServlet("/cookie/demo3")
public class CookieDemo3 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        //1.创建cookie对象
        Cookie c1 = new Cookie("msg","hello");
        Cookie c2 = new Cookie("asd","hsdlo");
        Cookie c3 = new Cookie("csa","heffo");
//2.发送cookie
        response.addCookie(c1);
        response.addCookie(c2);
        response.addCookie(c3);
    }
}

CookieDemo2

@WebServlet("/cookie/demo2")
public class CookieDemo2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        //3.获取cookie
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie c : cookies) {
                String name = c.getName();
                String value = c.getValue();
                System.out.println(name + ":" + value);   //msg:hello
            }
        }
    }
}

CookieDemo4

@WebServlet("/cookie/demo4")
public class  CookieDemo4    extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        //什么都没写,但是request的请求头里面保存着之前cookie返回的数据
    }
}

第一次请求数据,response的响应头

image-20211217222652820

第二次请求时,resquest的请求头

image-20211217222711391

第三次请求时,resquest的请求头

image-20211217222744438

响应头

image-20211217222755804

从以上的几次请求响应可以看出,除了第一次访问,其他访问请求cookie一直在都在浏览器访问服务器中作为数据传递。不管你调没调用获取cookie的方法。

2. Cookie的存活时间

可以自由设置,默认是关闭浏览器,cookie就没用了。

cookie.setMaxAge(expiry);  //设置cookie被浏览器保存的时间。
expiry:单位秒,默认为-1,
expiry=-1:代表浏览器关闭后,也就是会话结束后,cookie就失效了,也就没有了。
expiry>0:代表浏览器关闭后,cookie不会失效,仍然存在。并且会将cookie保存到硬盘中,直到设置时间过期才会
expiry=0:删除cookie。不管是之前的expiry=-1还是expiry>0,当设置expiry=0时,cookie都会被浏览器给删除。

测试:

@WebServlet("/cookie/demo5")
public class CookieDemo5 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
       Cookie cookie = new Cookie("name","TOM");
       //设置cookie一个小时后过期
       //cookie.setMaxAge(60*60);
       //设置cookie马上到期(清除cookie)
       cookie.setMaxAge(0);
       response.addCookie(cookie);
    }
}
3. Cookie是否支持中文数据
  1. 在tomcat 8之前cookie中不能直接存储中文数据。需要将中文数据转码,一般采用URL编码(%E3)
  2. 在tomcat 8之后,cookie支持中文数据。但是对于特殊字符比如空格不支持。还得使用URL编码和URL解码
存储中文数据
@WebServlet("/cookie/demo6")
public class CookieDemo6 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        Cookie[] cookies = request.getCookies();
        for (int i = 0; i < cookies.length; i++) {
            System.out.println(cookies[i].getValue());
        }
        Cookie cookie = new Cookie("name", "张三");
        cookie.setMaxAge(60 * 60);
        response.addCookie(cookie);
    }
}
存储特殊字符如:空格
Cookie cookie = new Cookie("name", "张 三 ");

报错:

image-20211221132235645

原因:

某些特殊的字符,例如:空格,方括号,圆括号,等于号(=),逗号,双引号,斜杠,问号,@符号,冒号,分号都不能作为Cookie的内容。

解决方案:

对于cookie存储特殊字符,我们可以先把数据用URL编码转化,存储到cookie里,我们需要cookie的时候,再用URL解码。
需要用到两个方法
URLEncoder类的静态方法encode()
URLDecoder类的静态方法decode()

@WebServlet("/cookie/demo6")
public class CookieDemo6 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
        Cookie[] cookies = request.getCookies();
        for (int i = 0; i < cookies.length; i++) {
            System.out.println(URLDecoder.decode(cookies[i].getValue(),"utf-8"));
        }
        Cookie cookie = new Cookie("name", URLEncoder.encode("张 三 ","utf-8"));
        cookie.setMaxAge(60 * 60);
        response.addCookie(cookie);
    }
}
4. cookie的共享问题
一个服务器,一个web项目

默认情况下,cookie共享servlet路径下的第一层路径

@WebServlet("/cookie/demo7")
@WebServlet("/cookie/demo8")
@WebServlet("/cookie-v/demo9")
默认情况下,demo7,demo8下数据cookie共享,demo9下数据不共享。

解决措施:cookie.setPath(“/虚拟路径”);

CookieDemo7

@WebServlet("/cookie/demo7")
public class CookieDemo7 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        //1.创建cookie对象
        Cookie c1 = new Cookie("name","Tom");
        Cookie c2 = new Cookie("age","18");
        Cookie c3 = new Cookie("sex","man");
//2.发送cookie
        response.addCookie(c1);
        response.addCookie(c2);
        response.addCookie(c3);
    }
}

CookieDemo8

控制台输出对应Cookie数据

@WebServlet("/cookie/demo8")
public class CookieDemo8 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        //3.获取cookie
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie c : cookies) {
                String name = c.getName();
                String value = c.getValue();
                System.out.println(name + ":" + value);   //msg:hello
            }
        }
    }
}

CookieDemo9

控制台没有输出对应Cookie数据

@WebServlet("/cookie-v/demo9")
public class CookieDemo9 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        //3.获取cookie
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie c : cookies) {
                String name = c.getName();
                String value = c.getValue();
                System.out.println(name + ":" + value);   //msg:hello
            }
        }
    }
}

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

  1. 默认情况下cookie不能在多个web项目共享

    1. setPath(String path):设置cookie的获取范围。默认情况下,设置当前的虚拟目录setPath(“/cookie”)

    2. 如果要共享,则可以将path设置为"/" setPath(“/”)

    3. 项目一:

      1. @WebServlet("/one/demo1")
        public class ServletDemo1 extends HttpServlet {
            @Override
            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                System.out.println("cookie1");
                Cookie[] cookies = req.getCookies();
                if (cookies!=null){
                    for (int i = 0; i <cookies.length ; i++) {
                        System.out.println("cookie1:"+cookies[i].getValue());
                    }
                }
                Cookie cookie = new Cookie("name","cookieOne");
                cookie.setPath("/");
                resp.addCookie(cookie);
            }
        }
        
    4. 项目二:

      1. @WebServlet("/two/demo1")
        public class ServletDemo1 extends HttpServlet {
                @Override
                protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                    System.out.println("cookie2");
                    Cookie[] cookies = req.getCookies();
                    if (cookies!=null){
                        for (int i = 0; i <cookies.length ; i++) {
                            System.out.println("cookie2:"+cookies[i].getValue());
                        }
                    }
                }
        }
        
同一个服务器的两个web项目

image-20211221202351069

如果我没有设置 setPath(“/”)

image-20211221205317901

设置了 setPath(“/”)

image-20211221205324681

在多个服务器之间,能否实现数据共享

假如:www.tieba.baidu.comwww.new.baidu.com这两个网站肯定都属于百度的项目,但是这两个项目肯定不会运行在一个服务器中,但是这两个网页之间肯定有某些数据的共享。

  1. 二者的域名为都有共同成分.baidu.com。这是一级域名。.news或.tieba 这是二级域名。
  2. 只要我们保证两个网页的一级域名相同,那么我们就可以实现不同浏览器之间的数据共享。
    1. setDomain(String path) :如果设置一级域名相同, 那么多个服务器之间cookie可以共享
    2. setDomain(" . baidu. com") ,那么tieba. baidu. com和news . baidu. com中cookie可以共享

Cookiie总结

工作流程:

  1. servlet创建cookie,保存少量数据,发送浏览器。

  2. 浏览器获得服务器发送的cookie数据,将自动的保存到浏览器端。

  3. 下次访问时,浏览器将自动携带cookie数据发送给服务器。

cookie操作

1.创建cookie:new Cookie(name,value)

2.发送cookie到浏览器:HttpServletResponse.addCookie(Cookie)

3.servlet接收cookie:HttpServletRequest.getCookies() 浏览器发送的所有cookie

cookie特点

  1. 每一个cookie文件大小:4kb , 如果超过4kb浏览器不识别

  2. 只能存字符串

  3. 一个web站点(web项目):发送20个

    3.一个浏览器保存总大小:300个

  4. cookie 不安全,可能泄露用户信息。浏览器支持禁用cookie操作。

  5. 默认情况生命周期:与浏览器会话一样,当浏览器关闭时cookie销毁的。—临时cookie

作用:

  1. cookie一般用于存出少量的不太敏感的数据

  2. 在不登录的情况下,完成服务器对客户端的身份识别

案例

记录访问时间

image-20211217223901386

需求:

在服务器中的Servlet判断是否有一个名为lastTime的cookie

  1. 有:不是第一次访问
    1. 响应数据:欢迎回来,您上次访问时间为:2021年02月21日 21:02:22
    2. 写回Cookie: lastTime=2021年02月21日 21:04:59
  2. 没有:是第一次访问
    1. 响应数据:您好,欢迎您首次访问
    2. 写回Cookie: lastTime=2021年02月21日 21:04:59
@WebServlet("/cookie/visit")
public class CookieItem extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置响应消息体的编码格式
        response.setContentType("text/html;charset=utf-8");
        //1、判断是否有cookie  先获取所有的cookie、
        Cookie[] cookies = request.getCookies();
        //1.1设置一个关于是否有lastname的cookie判断
        boolean flag = false;
        //2.遍历cookie数组
        if (cookies != null && cookies.length > 0) {
            for (Cookie cookie : cookies
            ) {
                //3.获取cookie的名称
                String name = cookie.getName();
                //4.判断名称是否是:lastname
                if ("lastTime".equals(name)) {
                    //有该cookie 表示不是第一次访问
                    flag = true;
                    //响应数据
                    //获取cookie的数据
                    String value = cookie.getValue();
                    System.out.println("解码前" + value);
                    //URL解码
                    value = URLDecoder.decode(value, "utf-8");
                    System.out.println("解码后" + value);
                    response.getWriter().write("<h1>欢迎回来,您上次的访问时间为" + value + "</h1>");//此处为中文消息,需要设置响应消息体


                    //设置cookie的值,现在的值
                    //获取当前时间的字符串,重新设置cookie的值,重新发送
                    Date date = new Date();
                    System.out.println("修改前" + date);
                    //设置时间的格式,默认的为美国的时间格式
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日  HH:mm:ss");//设置日期时间的格式
                    //设置时区,以下三种方式,任选其一。
//                    sdf.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
//                    sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
                    sdf.setTimeZone(TimeZone.getTimeZone("Etc/GMT-8"));
                    String str_date = sdf.format(date);
                    System.out.println("编码前" + str_date);
                    //URL编码
                    str_date = URLEncoder.encode(str_date, "utf-8");
                    System.out.println("编码后" + str_date);
                    //设置cookie
                    cookie.setValue(str_date);
                    //设置cookie的存活时间.存活一个月
                    cookie.setMaxAge(60 * 60 * 24 * 30);
                    //发送cookie
                    response.addCookie(cookie);

                    //找到名字为lastname的cookie之后,就不要循环了
                    break;
                }
            }
        }
        if (cookies == null || cookies.length == 0 || flag == false) {
            Date date = new Date();
            //设置时间的格式,默认的为美国的时间格式
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy年mm月dd日  HH:mm:ss");//设置日期时间的格式
            //设置时区,以下三种方式,任选其一。
//                    sdf.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
//                    sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
            sdf.setTimeZone(TimeZone.getTimeZone("Etc/GMT-8"));
            //将时间生成字符串
            String str_date = sdf.format(date);
            //URL编码
            str_date = URLEncoder.encode(str_date, "utf-8");
            System.out.println("编码后" + str_date);
            //设置cookie
            Cookie cookie = new Cookie("lastTime", str_date);
            cookie.setValue(str_date);
            //设置cookie的存活时间.存活一个月
            cookie.setMaxAge(60 * 60 * 24 * 30);
            //发送cookie
            response.addCookie(cookie);
            //响应数据
            //获取cookie的数据
            String value = cookie.getValue();
            //URL解码
            value = URLDecoder.decode(value, "utf-8");
            System.out.println("解码后" + value);
            response.getWriter().write("<h1>你好,欢迎您首次访问</h1>" + value);//此处为中文消息,需要设置响应消息体
        }

Session

Session介绍

会员卡的第三种方案。

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

image-20211217224200226

使用步骤:

  1. 获取HttpSession对象 :

    1. HttpSession session = request . getSession();
  2. 使用HttpSession对象 :

    1. object getAttribute(String name)
    2. void setAttribute(String name, object value)
    3. void removeAttribute(String name)

demo1:创建数据

@WebServlet("/session/demo1")
public class SessionDemo1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.获取session对象
        HttpSession session = req.getSession();
        //2.存储数据
        session.setAttribute("name","Tome");
    }
}

demo2:获取数据

@WebServlet("/session/demo2")
public class SessionDemo2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //使用session共享数据
        //1.获取session对象
        HttpSession session = req.getSession();
        //2.获取数据
        Object name = session.getAttribute("name");
        System.out.println(name);
    }
}

原理:

session是依赖于cookie的。

第一次访问,请求头里面无cookie

image-20211221211216118

第二次访问请求头里面包含第一次返回的cookie

image-20211221211209724

他们的JESSIONID的值是一样的,这也就是为什么一次会话,确定的session对象是同一个。

image-20211221211425216

强制清除浏览器cookie后

image-20211221211531389

image-20211221211608589

关闭后重新打开,session会话是否结束

Session原理

参考文章:(104条消息) Session是怎么实现的?存储在哪里?_码农的博客-CSDN博客_session怎么存储数据

session在访问tomcat服务器HttpServletRequest的getSession(true)的时候创建,tomcat的ManagerBase类提供创建sessionid的方法:随机数+时间+jvmid。

存储在服务器的内存中,tomcat的StandardManager类将session存储在内存中,也可以持久化到file,数据库,memcache,redis等。客户端只保存sessionid到cookie中,而不会保存session,session销毁只能通过invalidate或超时,关掉浏览器并不会关闭session。

在Java中是通过调用HttpServletRequest的getSession方法(使用true作为参数)创建。

在创建了Session的同时,服务器会为该Session生成唯一的Session id,而这个Session id在随后的请求中会被用来重新获得已经创建的Session;在Session被创建之后,就可以调用Session相关的方法往Session中增加内容了,而这些内容只会保存在服务器中,发到客户端的只有Session id;当客户端再次发送请求的时候,会将这个Session id带上,服务器接受到请求之后就会依据Session id找到相应的Session,从而再次使用之。

Session细节

1. 当客户端关闭后,服务器不关闭,两次获取session是否为同一个?
  1. 默认情况下:不是。
  2. 如果需要相同,则可以创建cookie,键为JSESSIONID,设置最大存活时间,让cookie持久化保存。
@WebServlet("/session/demo3")
public class SessionDemo3 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //使用session共享数据
        //1.获取session对象
        HttpSession session = req.getSession();
        session.setAttribute("name","Lily");
        //打印session
        System.out.println(session);
        //2. 设置cookie过期时间。
        Cookie cookie =new Cookie("JSESSIONID",session.getId());
        cookie.setMaxAge(60*60);
        resp.addCookie(cookie);
    }
}
2.客户端不关闭,服务器关闭后,两次获取的session是同一个吗?
  1. session对象都是存在服务器里面的,如果关闭服务器后,再打开,那么就又重新创建了两个服务器对象,肯定不一样。
  2. 不是同一个,但是要确保对象不丢失(假如我添加了一些商品到购物车中,我有点事情出去了,京东服务器重启了,那么我的购物车的内容不都清空了吗?)
    1. session钝化
      1. 在服务器正常关闭之前,将session对象系列化到硬盘上
    2. session活化
      1. 在服务器启动后,将session文件转化为内存中的session对象即可
  3. session钝化和活化Tomcat已经自动帮我们解决了,只不过我们是用IDEA时,IDEA不会活化,获取不到,所以我们在本地部署Tomcat即可。
3. session的失效时间?

session什么时候被销毁?

  1. 服务器关闭
  2. session对象调用invalidate() 。
  3. session默认失效时间30分钟

设置session过期时间的方式

  1. 选择性配置修改(全局配置)

    1. <session-config>
      
      <session-timeout>30</session-timeout>
      
      </session-config>
      

​ 在web-xml里面,可以进行相关的配置。

  1. 通过session对象进行修改

    1. HttpSession session = req.getSession();
      session.setMaxInactiveInterval(100);
      
4. Session和cookie区别
  1. session存储数据在服务器端,Cookie在客户端
  2. session没有数据大小限制,Cookie有
  3. session数据安全,Cookie相对于不安全

5. Filter和Listener

JavaWeb三大组件:Servlet,Filter,Listener

Filter

介绍

概念

生活中的过滤器:净水器,空气净化器,土匪、

web中的过滤器:当访问服务器的资源时,过滤器可以将请求拦截下来,完成一些特殊的功能。

过滤器的作用:

一般用于完成通用的操作。如:登录验证、统一编码处理、敏感字符过滤…

入门:

1.步骤:

1.定义一个类,实现接口Filter(见下面的注解)

2.重写方法

3.配置拦截路径

拦截路径
  1. 方式一:在web.xml中配置

    1. 创建过滤器

      public class FilterDemo implements Filter {
          @Override
          public void init(FilterConfig filterConfig) throws ServletException {
      
          }
      
          @Override
          public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
              //放行前,通常执行request的相关代码
              System.out.println("");
              filterChain.doFilter(servletRequest,servletResponse);
              //放行后,通常执行response的相关代码
              System.out.println("");
          }
      
          @Override
          public void destroy() {
      
          }
      }
      
    2. <?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">
          <filter>
              <filter-name>demo1</filter-name>
              <filter-class>filter.FilterDemo</filter-class>
          </filter>
          <filter-mapping>
              <filter-name>demo1</filter-name>
      <!--    过滤所有路径-->
              <url-pattern>/*</url-pattern>
          </filter-mapping>
      </web-app>
      
  2. 方式二:使用注解中配置@WebFilter(“/*”)

    1. @WebFilter("/*")   //所有页面被访问之前都被过滤
      public class FilterDemo implements Filter {
          @Override
          public void init(FilterConfig filterConfig) throws ServletException {
      
          }
      
          @Override
          public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
              //放行前,通常执行request的相关代码
              System.out.println("");
              //放行,进行数据访问
              filterChain.doFilter(servletRequest,servletResponse);
              //放行后,通常执行response的相关代码
              System.out.println("");
          }
      
          @Override
          public void destroy() {
      
          }
      }
      
      

Filter细节

1. 过滤器执行流程

1.执行过滤器(通常操作request相关数据)

2.执行放行后的资源(servlet中的代码)

3.回来执行过滤器放行代码下边的代码(通常操作response相关数据)

@WebFilter("/loginSec")  //拦截该页面,此时页面数据访问不到
public class FilterDemo implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //放行前,通常执行request的相关代码
        System.out.println("");
        filterChain.doFilter(servletRequest,servletResponse);
        //放行后,通常执行response的相关代码
        System.out.println("");
    }

    @Override
    public void destroy() {

    }
}
2. 过滤器生命周期方法
  1. init:在服务器启动后,会创建Filter对象,然后调用init方法。只执行一次。用于加载资源

  2. doFilter:每一次请求被拦截资源时,会执行。执行多次

  3. destroy :在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执行destroy方法。只执行一次。用于释放资源

3. 过滤器配置详解

拦截路径配置:

@WebFilter(“/*”)

1.具体资源路径: /index. jsp 只有访问index. jsp资源时,过滤器才会被执行

2.拦截目录,/user/* 访问/user下的所有资源时,过滤器都会被执行

3.后缀名拦截: *.jsp 访问所有后缀名为jsp资源时,过滤器都会被执行

4.拦截所有资源: /* 访问所有资源时,过滤器都会被执行|

拦截方式配置:

浏览器访问服务器可以直接访问,也可以访问服务器后通过servlet转发到其他的servlet,即服务器内部访问。

注解配置:

设置dispatcherTypes属性

  1. REQUEST :默认值。浏览器直接请求资源

  2. FORWARD :转发访问资源

  3. INCLUDE : 包含访问资源

  4. ERROR :错误跳转资源

  5. ASYNC :异步访问资源

//拦截方式配置   当请求或访问index.jsp时,会被拦截
@WebFilter(value = "/index.jsp" ,dispatcherTypes = {DispatcherType.FORWARD,DispatcherType.REQUEST})
@WebFilter(value = "/index.jsp" ,dispatcherTypes = DispatcherType.FORWARD)

xml配置

image-20211218213609702

4. 过滤器链

image-20211218214917584

执行顺序:如果有两个过滤器:过滤器1和过滤器2

1.过滤器1

2.过滤器2

3.资源执行

4.过滤器2

5.过滤器1

过滤器先后顺序问题:

  1. 注解配置:按照类名的字符串比较规则比较,值小的先执行

如: AFilter 和BFilter, AFilter就先执行 了。

  1. web. xml配置: 谁定义在上边,谁先执行

FilterDemo1

@WebFilter("/filter/*")
public class FilterDemo1 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //放行前,通常执行request的相关代码
        System.out.println("i am filter1 begin");
      filterChain.doFilter(servletRequest,servletResponse);
        //放行后,通常执行response的相关代码
        System.out.println("i am filter1 over");
    }

    @Override
    public void destroy() {

    }
}

FilterDemo2

@WebFilter("/filter/*")
public class FilterDemo2 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //放行前,通常执行request的相关代码
        System.out.println("i am filter2 begin");
        filterChain.doFilter(servletRequest,servletResponse);
        //放行后,通常执行response的相关代码
        System.out.println("i am filter2 over");
    }

    @Override
    public void destroy() {

    }
}

FilterTest1

@WebServlet("/filter/test1")
public class FilterTest1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("I am test1");
    }
}

FilterTest2

@WebServlet("/filter/test2")
public class FilterTest2  extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("I am test2");
    }
}

结果

image-20211221235820957

Listener

概念: Listener表示监听器,是JavaWeb三大组件(Servlet、Filter、Listener)之一。

事件监听机制:

  1. 事件:一件事情

  2. 事件源:事件发生的地方

  3. 监听器:一个对象

  4. 注册监听:将事件、事件源、监听器绑定在一起。 当事件源上发生某个事件后,执行监听器代码

监听实现

  1. 实现ServletContextListener接口 :监听ServletContext对象的创建和销毁
    1. void contextDestroyed(ServletContextEvent sce) : ServletContext对象被销毁之前会调用该方法

    2. void contextInitialized(ServletContextEvent sce) : ServletContext对象创建后会调用该方法

监听器可以监听就是在application,session,request三个对象创建、销毁或者往其中添加修改删除属性时自动执行代码的功能组件
Listener分类:

JavaWeb中提供了8个监听器。

image-20211222090405685

创建Listener

  1. 实现ServletContextListener接口
  2. 添加@WebListener注解
@WebListener
public class ListenerDemo implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        //加载资源
        System.out.println("服务器启动了。监听器,执行了。");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        //释放资源.
        System.out.println("服务器关闭了。监听器,执行了。");
    }
}

image-20211222091135559

6. 案例

1. 用户登陆(request)

案例需求:

  1. 编写login. html登录页面username & password两个输入框

  2. 使用Druid数据库连接池技术,操作mysql数据库中tb_students_info表(这里使用的是DRUID链接池)

  3. 使用JdbcTemplate技术封装JDBC(使用返回的数据直接封装)

  4. 登录成功跳转到SuccessServlet展示:登录成功!用户名,欢迎您

  5. 登录失败跳转到FailServlet展示:登录失败,用户名或密码错误

逻辑图示:

[外链图片转存中…(img-AwbxpJPO-1652970980102)]

报错:

Could not initialize class cn.itcast.Util.JDBCUtils

[外链图片转存中…(img-DZj6wmxL-1652970980103)]

解决:

jar包放在lib文件夹里面,而我却放在了libs里面

核心代码

建表语句

CREATE TABLE `tb_students_info` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id\r\n',
  `username` varchar(255) NOT NULL DEFAULT '' COMMENT '用户名',
  `password` varchar(255) NOT NULL DEFAULT '' COMMENT '密码',
  `name` varchar(255) NOT NULL DEFAULT '' COMMENT '姓名',
  `dept_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '部门id',
  `age` smallint(6) unsigned NOT NULL DEFAULT '0' COMMENT '年龄',
  `sex` bit(1) NOT NULL DEFAULT b'0' COMMENT '性别  0 男  1 女',
  `height` smallint(6) unsigned NOT NULL DEFAULT '0' COMMENT '身高',
  `money` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '存款',
  `login_date` datetime DEFAULT NULL COMMENT '登陆时间',
  PRIMARY KEY (`id`),
  KEY `name_age` (`name`,`age`)
) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8;

/*Data for the table `tb_students_info` */

insert  into `tb_students_info`(`id`,`username`,`password`,`name`,`dept_id`,`age`,`sex`,`height`,`money`,`login_date`) values (3,'2019002','2019002','Henry',2,100,'',185,'0.00','2021-12-08 23:27:08'),(4,'2019003','2019003','Jane',1,22,'',162,'100.00','2021-12-08 23:27:44'),(5,'2019004','2019004','Jim',6,21,'\0',175,'50.00','2021-12-08 23:27:47'),(6,'2019005','2019005','John',5,25,'',172,'50.00','2021-12-04 23:27:52'),(7,'2019006','2019006','Lily',1,0,'\0',165,'0.00','2021-11-30 23:27:57'),(8,'2019007','2019007','Susan',1,20,'',170,'0.00','2021-11-02 23:28:01'),(9,'2019008','2019008','Thomas',4,35,'\0',178,'0.00','2021-12-03 23:28:06'),(10,'2019009','2019009','Tom',3,15,'',165,'0.00','2021-12-26 23:28:10'),(11,'2019010','2019010','Jerry',1,15,'\0',170,'0.00','2021-12-08 23:27:47'),(22,'2019012','2019012','王五',1,18,'\0',178,'0.00','2021-12-11 00:00:00'),(23,'2019012','2019012','王五',1,18,'\0',178,'100.00','2021-12-11 00:00:00'),(24,'2019012','2019012','王五',1,18,'\0',178,'100.00','2021-12-12 13:56:25');

实体类

public class Student {
    private Long id;
    private String username;
    private String password;
    private String name;
    private Long deptId;
    private int age;
    private Boolean sex;
    private int height;
    private BigDecimal money;
    private LocalDateTime login_date;

    public Student() {
    }

    public Student(Long id, String username, String password, String name, Long deptId, int age, Boolean sex, int height, BigDecimal money, LocalDateTime login_date) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.name = name;
        this.deptId = deptId;
        this.age = age;
        this.sex = sex;
        this.height = height;
        this.money = money;
        this.login_date = login_date;
    }

    public BigDecimal getMoney() {
        return money;
    }

    public void setMoney(BigDecimal money) {
        this.money = money;
    }

    public Long getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

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

    public String getPassword() {
        return password;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Long getDeptId() {
        return deptId;
    }

    public void setDeptId(Long deptId) {
        this.deptId = deptId;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Boolean getSex() {
        return sex;
    }

    public void setSex(Boolean sex) {
        this.sex = sex;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public LocalDateTime getLogin_date() {
        return login_date;
    }

    public void setLogin_date(LocalDateTime login_date) {
        this.login_date = login_date;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", name='" + name + '\'' +
                ", deptId=" + deptId +
                ", age=" + age +
                ", sex=" + sex +
                ", height=" + height +
                ", money=" + money +
                ", login_date=" + login_date +
                '}';
    }
}

login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="/student_system/login" method="post">
    用户名:<input type="text" name="username">
    密码:<input type="text" name="password">
    <input type="submit" value="登陆">
</form>
</body>
</html>

请求代码:

@WebServlet("/login")
public class Login extends HttpServlet {
    private static final IStudentService studentService = new StudentServiceImpl();

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.设置编码
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        //2.获取请求参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        if ("".equals(username) || "".equals(password) || username == null || password == null) {
            request.setAttribute("message", "fail");
            request.getRequestDispatcher("/login/fail.jsp").forward(request, response);
            return;
        }
        //3.查询数据库
        Student student = studentService.login(username, password);
        if (student == null) {
            request.setAttribute("message", "fail");
            request.getRequestDispatcher("/login/fail.jsp").forward(request, response);
        } else {
            //获取所有学生
            List<Student> allStundents = studentService.getAllStundents();
            request.setAttribute("message", allStundents);
//            //获取当前学生
//            request.setAttribute("message", student);
            request.getRequestDispatcher("/login/homepage.jsp").forward(request, response);
        }
    }

}

homepage.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
${message}

</body>
</html>

fail.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
${message}

</body>
</html>

参数传递优化(BeanUtils)

问题:

进行参数的共享时,我们是将每个参数对象先保存起来,再进行共享。那么问题来了,我们按照之前的共享的方式,一次只能保存一个数据,假如我有多个数据要传递呢?

之前的方式

    //2.获取请求参数  并封装成对象    
	String username = request.getParameter("username");
  	String password = request.getParameter("password");
	User loginUser = new User(1, username, password);

使用BeanUtils工具类

  1. 简化数据封装

    1. 引入BeanUtils下载网站:http://commons.apache.org/proper/commons-beanutils/

    2. 导入jar包

[外链图片转存中…(img-jTzUynbR-1652970980105)]

**注意:**commons-logging这个jar包一定要导入,作用:

[外链图片转存中…(img-1QK4RnRc-1652970980106)]

简单使用:

@WebServlet("/add")
public class AddStudent extends HttpServlet {
    private static final IStudentService studentService = new StudentServiceImpl();

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.设置编码
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        //2.获取参数
        Map<String, String[]> parameterMap = request.getParameterMap();
        //3.创建空对象
        Student student = new Student();
        //4. 使用beanutils封装
        try {
            BeanUtils.populate(student, parameterMap);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        student.setDeptId(18L);
        student.setMoney(BigDecimal.valueOf(100.00));
        student.setSex(false);
        student.setLogin_date(LocalDateTime.now());
        int i = studentService.addStudent(student);
        if (i <= 0) {
            request.setAttribute("message", "插入失败");
            request.getRequestDispatcher("/login/fail.jsp").forward(request, response);
        } else {
            List<Student> allStudents = studentService.getAllStundents();
            request.setAttribute("message", allStudents);
            request.getRequestDispatcher("/login/homepage.jsp").forward(request, response);
        }
    }
}

扩展1:BeanUtils.工具类

简化数据封装用于封装JavaBean的

  1. JavaBean :标准的Java类

    1. 要求:
      1. 类必须被public修饰
      2. 必须提供空参的构造器
      3. 成员变量必须使用private修饰
      4. 提供公共setter和getter方法
    2. 功能:封装数据
  2. 概念:

  3. 成员变量:

    1. 属性: setter和getter方法截取后的产物
      1. 例如: getUsername() --> Username–> username
      2. 假如我有一个成员变量交name,但是他的set/get方法为getHisName,那么截取后的产物为hisName。hisName为属性,name为成员变量。但是大多数情况下,属性的成员变量的名称是一样的
  4. 方法:

  5. setProperty( )。操作的是属性,而不是成员变量

设置对象

1.成员变量gender  setSex/getSex

BeanUtils.setProperty(user,"gender","male");

System.out.println(user);   //  User{id=0, username='null', password='null', gender='null'}

BeanUtils.setProperty(user,"sex","male");

System.out.println(user);   //  User{id=0, username='null', password='null', gender='male'}

 

根据结果我们可以看出,成员变量gender,但他的set/get方法是sex。此时属性和成员变量不一样。

而setProperty( )操作的是属性,所以运行出来的结果不一样
  1. getProperty( )
//获取对象
try {

  String genders = BeanUtils.getProperty(user, "sex");

  System.out.println(genders); male

} catch (NoSuchMethodException e) {

  e.printStackTrace();

}
  1. populate(object obj,Map map) :将map集合的键值对信息,封装到对应的JavaBean对象中
//A.1获取参数的map集合
Map<String, String[]> parameterMap = request.getParameterMap();
//A.2创建一个User对象
User loginUser = new User();
//A.3使用BeanUtils封装
try {
   BeanUtils.populate(loginUser,parameterMap);
} catch (IllegalAccessException e) {
   e.printStackTrace();
} catch (InvocationTargetException e) {
   e.printStackTrace();
}

扩展2:使用JdbcTemplate工具类

使用参考:[mystudy/Mysql备课课件/3. JDBC的基本使用.md · Zhang-HaoQi/Knowledge - 码云 - 开源中国 (gitee.com)](https://gitee.com/zhang-haoqi/knowledge/blob/develop/mystudy/Mysql备课课件/3. JDBC的基本使用.md)

public class StudentDaoImpl implements IStudentDao {
    private JdbcTemplate template = new JdbcTemplate(DRUIDDateSource.getDataSource());

//获取一条学生数据
    @Override
    public Student login(String username, String password) {
        String sql = "SELECT * FROM tb_students_info WHERE username=? AND password=?";
        Student student = template.queryForObject(sql,new BeanPropertyRowMapper<Student>(Student.class),username,password);
        System.out.println(student);
        return student;
    }

    //获取所有学生
     @Override
    public List<Student> getAllStudent() {
        String sql = "SELECT * FROM tb_students_info ";
        List<Student> query = template.query(sql, new BeanPropertyRowMapper<Student>(Student.class));
        System.out.println(query);
        return query;
    }

    
    //添加学生
    @Override
    public int addStudent(Student student) {
        String sql = "INSERT INTO tb_students_info VALUE (?,?,?,?,?,?,?,?,?,?)";
        Date loginDate = new Date(Date.from(student.getLogin_date().atZone(ZoneOffset.ofHours(8)).toInstant()).getTime());
        int update = template.update(sql,
                null,
                student.getUsername(),
                student.getPassword(),
                student.getName(),
                student.getDeptId(),
                student.getAge(), student.getSex(), student.getHeight(), student.getMoney(), loginDate);
        return update;
    }
}

2. 验证码响应(response)

Servlet代码

@WebServlet("/reqs/verify")
public class VerifyCodingDemo extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.创建一个对象,在内存中存图片(验证码图片对象)
        int width =100;
        int height= 50;
        BufferedImage image  =new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);//宽,高,格式
        //2.美化图片
        //2.1化背景颜色
        Graphics graphics = image.getGraphics();//画笔对象
        graphics.setColor(Color.pink);//设置画笔颜色
        graphics.fillRect(0,0,width,height);//填充一个蓝色的矩形  填充的位置和大小
        //2.2画边框
        graphics.setColor(Color.BLUE);//设置颜色
        graphics.drawRect(0,0,width-1,height-1);//画边框
        //2.3写验证码
        String str ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";  //验证码包含的所有字符数字
        Random random = new Random();//画验证码验证符
        for (int i = 1; i < 5; i++) {
            int s = random.nextInt(str.length());//随机获取字符串的角标,长度在字符串长度的范围内
            char c = str.charAt(s);//获取随机的字符
            graphics.drawString(c+"",i*20,25);//字符串的内容和位置
        }
        //2.4画干扰线
        graphics.setColor(Color.black);
        for (int i = 0; i < 10; i++) {
            int x1 = random.nextInt(100);
            int x2 = random.nextInt(100);
            int y1 = random.nextInt(50);
            int y2 = random.nextInt(50);
            graphics.drawLine(x1,y1,x2,y2);
        }
        //3.将图片输入到页面展示
        ImageIO.write(image,"jpg",response.getOutputStream());//输出对象,后缀名,输出流输出
    }
}

JSP代码

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        window.onload = function () {
            //点击图片切换
            //获取图片
            let elementById = document.getElementById("img");
            //更改图片,其实就是更改图片的路径,因为我们的图片是随机生成的,所以只需要图片重新加载一次即可。
            elementById.onclick = function () {
                // elementById.src="/Response/checkCodeServlet";
                //这时你会发现点击图片没有变化,这是因为之前浏览器已经访问过这个路径了,此时路径的内容已经存储在了浏览器的内存中,所以访问的还是之前的图片
                // 解决措施:假意传参.   如果参数传入相同的值,那么就会造成只能切换一次。跟上面一个道理。所以要传一个随机的值
                //  elementById.src="/Response/checkCodeServlet?1";

                //因为随机数有时候也可能相同,所以遇到这种情况,我们可以通过把时间戳当做参数来解决。
                let date = new Date().getTime();
                elementById.src = "/servlet/reqs/verify?" + date;
                alert(data)
            }
            //点击文字切换
            let elementById1 = document.getElementById("change");
            elementById1.onclick = function () {
                let date = new Date().getTime();
                elementById.src = "/servlet/reqs/verify?" + date;
            }
        }
    </script>
</head>
<body>
<!--图片的点击切换-->
<img id="img" src="/servlet/reqs/verify">
<a id="change">看不清,换一张</a>
</body>
</html>

3. 用户登陆(session)

session免登录

需求:

  1. 第一次登录之后,进入主页,看到所有学生的数据
  2. 第二次不需要登录,直接请求登录接口即可获取所有学生数据

[外链图片转存中…(img-mKrMYDIZ-1652970980108)]

login.jsp

<form action="/students_system/loginSec" method="post">
    用户名:<input type="text" name="username">
    密码:<input type="text" name="password">
    <input type="submit" value="登陆">
    <br>
    ${message}
</form>

登录接口

@WebServlet("/loginSec")
public class LoginSec extends HttpServlet {
    private static final IStudentService studentService = new StudentServiceImpl();

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, IOException {
        //1.设置编码
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        HttpSession session = request.getSession();
        //2. 判断当前用户服务器是否已保存
        Student user = (Student) session.getAttribute("user");
        if (user != null) {
            response.sendRedirect("/students_system/all");
            return;
        }
        //3.获取请求参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        if ("".equals(username) || "".equals(password) || username == null || password == null) {
            request.setAttribute("message", "失败:用户名和密码不能为null");
            request.getRequestDispatcher("/login/login.jsp").forward(request, response);
            return;
        }
        //4.查询数据库
        Student student = studentService.login(username, password);
        if (student == null) {
            request.setAttribute("message", "失败:用户不存在");
            request.getRequestDispatcher("/login/login.jsp").forward(request, response);
        } else {
            session.setAttribute("user",student);
            response.sendRedirect("/students_system/all");
        }
    }

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

}

AllStudent

@WebServlet("/all")
public class AllStudent extends HttpServlet {
    private static final IStudentService studentService = new StudentServiceImpl();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        List<Student> allStudents = studentService.getAllStundents();
        req.setAttribute("message", allStudents);
        req.getRequestDispatcher("/login/homepage.jsp").forward(req, resp);
    }
}

homepage.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
${message}

</body>
</html>

4. 用户登陆(Filter)

session案例里面,我们在登陆接口里面判断了当前用户是否进行登陆。

问题:

  1. 如果用户登陆过后,再次访问时,访问的不是登陆接口,而是主页,那么我们还需要在主页的接口里判断用户是否登陆。代码冗余。
  2. 用户没有登录,直接调用主页接口,是可以调用的。

改善:

  1. 用户是否登陆的判断添加到过滤器里面,请求非登陆页面时,在此接口统一判断。
  2. 非登陆页面,不能直接进行接口访问。

[外链图片转存中…(img-emdxwaXW-1652970980110)]

登陆页面loginThird.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <link rel="stylesheet" href="/students_system/css/login.css">
</head>
<body>
<form action="/students_system/loginThird" method="post">
    用户名:<input type="text" name="username">
    密码:<input type="text" name="password">
    <input class="sub-but" type="submit" value="登陆">
    <br>
    ${message}
</form>
</body>
</html>

接口LoginThird

@WebServlet("/loginThird")
public class LoginThird extends HttpServlet {
    private static final IStudentService studentService = new StudentServiceImpl();

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, IOException {
        //1.设置编码
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        //2.获取session
        HttpSession session = request.getSession();
        Student user = (Student) session.getAttribute("user");
        if (user != null) {
            //有用户数据,重定向到主页
            response.sendRedirect("/students_system/all");
            return;
        }
        //3.获取请求参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        if ("".equals(username) || "".equals(password) || username == null || password == null) {
            //失败
            request.setAttribute("message", "失败:用户名和密码不能为null");
            request.getRequestDispatcher("/login/loginThird.jsp").forward(request, response);
            return;
        }
        //3.查询数据库
        Student student = studentService.login(username, password);
        if (student == null) {
            //失败
            request.setAttribute("message", "失败:用户不存在");
            request.getRequestDispatcher("/login/loginThird.jsp").forward(request, response);
        } else {
            //重定向到主页
            session.setAttribute("user",student);
            response.sendRedirect("/students_system/all");
        }
    }

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

}

过滤器FilterDemo

@WebFilter("/*")
public class FilterDemo extends HttpFilter {
    @Override
    protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
        req.setCharacterEncoding("utf-8");
        res.setContentType("text/html;charset=utf-8");
        //不拦截的资源
        String[] urls = {
              "/login/loginSec.jsp",
                "/login/loginThird.jsp",
                "/login/login.jsp",
                "/login/fail.jsp",
                "/css/*",
                "/loginThird"
        };
        String url = req.getRequestURL().toString();
        System.out.println(url);
        for (int i = 0; i < urls.length; i++) {
            if (url.contains(urls[i])){
                //放行不进行拦截的资源
                chain.doFilter(req,res);
                //如果不return,代码执行完后,还进入过滤器继续执行。
                return;
            }
        }
        //通过session判断用户是否已登录
        HttpSession session = req.getSession();
        Student user = (Student) session.getAttribute("user");
        if (user != null) {
            System.out.println("登陆未过期,免登陆");
            chain.doFilter(req,res);
        }else {
            System.out.println("登陆过期,请重新登陆");
            req.setAttribute("message", "请重新登陆");
            req.getRequestDispatcher("/login/loginThird.jsp").forward(req, res);
        }

    }
}

7. 问题汇总

1. WEB-INF资源问题

WEB-INF目录是不对外开放的,外部没办法直接访问到。所有只能通过映射来访问,比如映射为一个action或者servlet通过服务器端跳转来访问到具体的页面。这样可以限制访问,提高安全性。

1. 首页在WEB-INF下

通过xml设置欢迎页来解决。首页登陆页面。

正常登录页是首页,如果一定要放在webinf下面的话。xml中添加
<welcome-file-list>
    <welcome-file>/WEB-INF/view/Login.jsp</welcome-file>
</welcome-file-list>

2. 获取WEB-INF指定jsp文件

通过转发的形式获取

@WebServlet("/forward/demo7")
public class ForwardDemo7 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getRequestDispatcher("/WEB-INF/homepage.jsp").forward(req,resp);
    }
}

3. 静态资源在WEB-INF下

WEB-INF目录下文件访问资源文件时,可以忽略WEB-INF这一层目录.如index.jsp 要用css目录里的一个css文件.

 <link rel="StyleSheet" href="../css/index.css" type="text/css" />这样就行了。

4. Tomcat项目Jar包问题

在使用Tomcat服务器进行开发的时候,如果需要使用第三方jar包,需要在WEB-INF下创建lib文件夹,将jar包复制进去,并右键add as library,否则可能在使用的时候出现类可以正常创建对象,但是使用报错情况。

[外链图片转存中…(img-xcbv9T0T-1652970980111)]

2. 乱码问题

Tomcat日志乱码

[(102条消息) Java-IDEA2020-IDEA或者启动Tomcat控制台中文乱码解决_gaogzhen的博客-CSDN博客_idea启动tomcat控制台乱码](https://blog.csdn.net/gaogzhen/article/details/107307459#:~:text=每次用新的 tomcat 和idea都会遇到 tomcat控制台中文乱码 问题,故在此整理。 基本上都是需要修改 tomcat,的 启动 参数(如果本身idea编码都统一设置为utf-8了)打开 tomcat 下的bin目录,找到catalina.bat文件找到 JAVA_OPTS 参数,在其中加上 -Dfile.encoding%3DUTF-8(用的7.0.82版本的设置为UTF-8,8.0.20版本的需要设置为GBK,)

URL解码

URLDecoder包含将 String 转换为 application/x-www-form-urlencoded MIME 格式的静态方法。

MIME 格式:定义的一种文件数据类型。格式:大类型/小类型 如:text/html image/jpeg ;

使用URLDecoder和URLEncoder对中文进行处理 - asflex - ITeye博客

请求响应

  1. 获取请求参数中文乱码
//1. 使用requset.getParameter("")
request.setCharacterEncoding("utf-8");
 System.out.println(requset.getParameter("username"));
System.out.println(requset.getParameter("password"));

//2. 使用字符流乱码  需要使用URLDecoder解码
BufferedReader reader = req.getReader();
String line = null;
while ((line=reader.readLine())!=null){
    System.out.println(URLDecoder.decode(line,"utf-8"));
}
  1. 响应数据乱码

  2. //方式一:
    resp.setContentType("text/html;charset=utf-8");
    //方式二:
    //1.1获取流对象之前,把流的默认编码“ISO-8859-1”设置为:utf-8
    resp.setCharacterEncoding("utf-8");
    //1.2告诉浏览器,服务器发送的数据的编码,建议浏览器使用此编码
    resp.setHeader("content-type","text/html;charset=utf-8");
    

控制台打印乱码

  1. idea设置编码格式全为utf-8

3. 路径问题汇总

  1. /表示根路径

    1. 在前端页面,表示的是当前主机地址和端口号。即:localhost:8080

      1. //jsp页面中发送请求,需要添加/servlet虚拟路径 
        <a href="/servlet/blog/add">添加blog</a>
        
    2. 在servlet服务中,表示的是当前服务器的虚拟路径。即localhost: 8080/servlet/

      1. //servlet服务中进行转发,不需要添加/servlet虚拟路径 
        request.getRequestDispatcher("/hello.jsp").forward(request,response);
        
  2. 虚拟路径什么时候使用

    1. 浏览器使用:如:超链接表单提交发送请求时,需要添加虚拟路径。
    2. 服务器使用:如:转发,不需要添加虚拟路径。

4. 设置basepath路径

设置basepath:https://blog.csdn.net/weixin_33800593/article/details/92942073

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

See you !

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

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值