Request和Response基础知识入门
创始人
2024-03-18 23:47:42
0

文章目录

    • 1,Request和Response的概述
    • 2,Request对象
      • 2.1 Request继承体系
      • 2.2 Request获取请求数据
        • 2.2.1 获取请求行数据
        • 2.2.2 获取请求头数据
        • 2.2.3 获取请求体数据
        • 2.2.4 获取请求参数的通用方式
      • 2.3 IDEA快速创建Servlet
      • 2.4 请求参数中文乱码问题
        • 2.4.1 POST请求解决方案
        • 2.4.2 GET请求解决方案
      • 2.5 Request请求转发
    • 3,Response对象
      • 3.1 Response设置响应数据功能介绍
      • 3.2 Respones请求重定向
      • 3.3 路径问题
      • 3.4 Response响应字符数据
      • 3.3 Response响应字节数据
    • 4,用户注册登录案例
      • 4.1 用户登录
        • 4.1.1 需求分析
        • 4.1.2 环境准备
        • 4.1.3 代码实现
      • 4.2 用户注册
        • 4.2.1 需求分析
        • 4.2.2 代码编写
      • 4.3 SqlSessionFactory工具类抽取

1,Request和Response的概述

Request是请求对象,Response是响应对象。 这两个对象在我们使用Servlet的时候有看到:
在这里插入图片描述

此时,我们就需要思考一个问题request和response这两个参数的作用是什么?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nvrs4pfZ-1670137343178)(assets/1628735746602.png)]

  • request:获取请求数据
    • 浏览器会发送HTTP请求到后台服务器[Tomcat]
    • HTTP的请求中会包含很多请求数据[请求行+请求头+请求体]
    • 后台服务器[Tomcat]会对HTTP请求中的数据进行解析并把解析结果存入到一个对象中
    • 所存入的对象即为request对象,所以我们可以从request对象中获取请求的相关参数
    • 获取到数据后就可以继续后续的业务,比如获取用户名和密码就可以实现登录操作的相关业务
  • response:设置响应数据
    • 业务处理完后,后台就需要给前端返回业务处理的结果即响应数据
    • 把响应数据封装到response对象中
    • 后台服务器[Tomcat]会解析response对象,按照[响应行+响应头+响应体]格式拼接结果
    • 浏览器最终解析结果,把内容展示在浏览器给用户浏览

对于上述所讲的内容,我们通过一个案例来初步体验下request和response对象的使用。

@WebServlet("/demo3")
public class ServletDemo3 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//使用request对象 获取请求数据String name = request.getParameter("name");//url?name=zhangsan//使用response对象 设置响应数据response.setHeader("content-type","text/html;charset=utf-8");response.getWriter().write("

"+name+",欢迎您!

");}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("Post...");} }

启动成功后就可以通过浏览器来访问,并且根据传入参数的不同就可以在页面上展示不同的内容:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-meTOPZPf-1670137343179)(assets/1628738273049.png)]

小结

在这节中,我们主要认识了下request对象和reponse对象:

  • request对象是用来封装请求数据的对象
  • response对象是用来封装响应数据的对象

目前我们只知道这两个对象是用来干什么的,那么它们具体是如何实现的,就需要我们继续深入的学习。接下来,就先从Request对象来学习,主要学习下面这些内容:

  • request继承体系

  • request获取请求参数

  • request请求转发

2,Request对象

2.1 Request继承体系

在学习这节内容之前,我们先思考一个问题,前面在介绍Request和Reponse对象的时候,比较细心的同学可能已经发现:

  • 当我们的Servlet类实现的是Servlet接口的时候,service方法中的参数是ServletRequest和ServletResponse
  • 当我们的Servlet类继承的是HttpServlet类的时候,doGet和doPost方法中的参数就变成HttpServletRequest和HttpServletReponse

那么,

  • ServletRequest和HttpServletRequest的关系是什么?
  • request对象是有谁来创建的?
  • request提供了哪些API,这些API从哪里查?

首先,我们先来看下Request的继承体系:

在这里插入图片描述

从上图中可以看出,ServletRequest和HttpServletRequest都是Java提供的,所以我们可以打开JavaEE提供的API文档打开后可以看到:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PDj2r1bP-1670137343181)(assets/1628741839475.png)]

所以ServletRequest和HttpServletRequest是继承关系,并且两个都是接口,接口是无法创建对象,这个时候就引发了下面这个问题:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GpfS8eDd-1670137343182)(assets/1628742224589.png)]

这个时候,我们就需要用到Request继承体系中的RequestFacade:

  • 该类实现了HttpServletRequest接口,也间接实现了ServletRequest接口。
  • Servlet类中的service方法、doGet方法或者是doPost方法最终都是由Web服务器[Tomcat]来调用的,所以Tomcat提供了方法参数接口的具体实现类,并完成了对象的创建
  • 要想了解RequestFacade中都提供了哪些方法,我们可以直接查看JavaEE的API文档中关于ServletRequest和HttpServletRequest的接口文档,因为RequestFacade实现了其接口就需要重写接口中的方法

对于上述结论,要想验证,可以编写一个Servlet,在方法中把request对象打印下,就能看到最终的对象是不是RequestFacade,代码如下:

@WebServlet("/demo2")
public class ServletDemo2 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println(request);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}
}

启动服务器,运行访问http://localhost:8080/request-demo/demo2,得到运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MIVOLP6c-1670137343182)(assets/1628743040046.png)]

小结

  • Request的继承体系为ServletRequest–>HttpServletRequest–>RequestFacade
  • Tomcat需要解析请求数据,封装为request对象,并且创建request对象传递到service方法
  • 使用request对象,可以查阅JavaEE API文档的HttpServletRequest接口中方法说明

2.2 Request获取请求数据

HTTP请求数据总共分为三部分内容,分别是请求行、请求头、请求体,对于这三部分内容的数据,分别该如何获取,首先我们先来学习请求行数据如何获取?

2.2.1 获取请求行数据

请求行包含三块内容,分别是请求方式请求资源路径HTTP协议及版本

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yqwMPzgg-1670137343183)(assets/1628748240075.png)]

对于这三部分内容,request对象都提供了对应的API方法来获取,具体如下:

  • 获取请求方式: GET
String getMethod()
  • 获取虚拟目录(项目访问路径): /request-demo
String getContextPath()
  • 获取URL(统一资源定位符): http://localhost:8080/request-demo/req1
StringBuffer getRequestURL()
  • 获取URI(统一资源标识符): /request-demo/req1
String getRequestURI()
  • 获取请求参数(GET方式): username=zhangsan&password=123
String getQueryString()

介绍完上述方法后,咱们通过代码把上述方法都使用下:

/*** request 获取请求数据*/
@WebServlet("/req1")
public class RequestDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// String getMethod():获取请求方式: GETString method = req.getMethod();System.out.println(method);//GET// String getContextPath():获取虚拟目录(项目访问路径):/request-demoString contextPath = req.getContextPath();System.out.println(contextPath);// StringBuffer getRequestURL(): 获取URL(统一资源定位符):http://localhost:8080/request-demo/req1StringBuffer url = req.getRequestURL();System.out.println(url.toString());// String getRequestURI():获取URI(统一资源标识符): /request-demo/req1String uri = req.getRequestURI();System.out.println(uri);// String getQueryString():获取请求参数(GET方式): username=zhangsanString queryString = req.getQueryString();System.out.println(queryString);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {}
}

启动服务器,访问http://localhost:8080/request-demo/req1?username=zhangsan&passwrod=123,获取的结果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jdl9bXuA-1670137343184)(assets/1628762794935.png)]

2.2.2 获取请求头数据

对于请求头的数据,格式为key: value如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hfuCa93C-1670137343185)(assets/1628768652535.png)]

所以根据请求头名称获取对应值的方法为:

String getHeader(String name)

接下来,在代码中如果想要获取客户端浏览器的版本信息,则可以使用

/*** request 获取请求数据*/
@WebServlet("/req1")
public class RequestDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取请求头: user-agent: 浏览器的版本信息String agent = req.getHeader("user-agent");System.out.println(agent);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {}
}

重新启动服务器后,http://localhost:8080/request-demo/req1,获取的结果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HFCAylC9-1670137343194)(assets/1628768665185.png)]

2.2.3 获取请求体数据

浏览器在发送GET请求的时候是没有请求体的,所以需要把请求方式变更为POST,请求体中的数据格式如下:

在这里插入图片描述

对于请求体中的数据,Request对象提供了如下两种方式来获取其中的数据,分别是:

  • 获取字节输入流,如果前端发送的是字节数据,比如传递的是文件数据,则使用该方法
ServletInputStream getInputStream()
该方法可以获取字节
  • 获取字符输入流,如果前端发送的是纯文本数据,则使用该方法
BufferedReader getReader()

接下来,大家需要思考,要想获取到请求体的内容该如何实现?

具体实现的步骤如下:

1.准备一个页面,在页面中添加form表单,用来发送post请求

2.在Servlet的doPost方法中获取请求体数据

3.在doPost方法中使用request的getReader()或者getInputStream()来获取

4.访问测试

  1. 在项目的webapp目录下添加一个html页面,名称为:req.html


Title



  1. 在Servlet的doPost方法中获取数据
/*** request 获取请求数据*/
@WebServlet("/req1")
public class RequestDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//在此处获取请求体中的数据}
}
  1. 调用getReader()或者getInputStream()方法,因为目前前端传递的是纯文本数据,所以我们采用getReader()方法来获取
/*** request 获取请求数据*/
@WebServlet("/req1")
public class RequestDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取post 请求体:请求参数//1. 获取字符输入流BufferedReader br = req.getReader();//2. 读取数据String line = br.readLine();System.out.println(line);}
}

注意

BufferedReader流是通过request对象来获取的,当请求完成后request对象就会被销毁,request对象被销毁后,BufferedReader流就会自动关闭,所以此处就不需要手动关闭流了。

  1. 启动服务器,通过浏览器访问http://localhost:8080/request-demo/req.html

在这里插入图片描述

点击提交按钮后,就可以在控制台看到前端所发送的请求数据

在这里插入图片描述

小结

HTTP请求数据中包含了请求行请求头请求体,针对这三部分内容,Request对象都提供了对应的API方法来获取对应的值:

  • 请求行
    • getMethod()获取请求方式
    • getContextPath()获取项目访问路径
    • getRequestURL()获取请求URL
    • getRequestURI()获取请求URI
    • getQueryString()获取GET请求方式的请求参数
  • 请求头
    • getHeader(String name)根据请求头名称获取其对应的值
  • 请求体
    • 注意: 浏览器发送的POST请求才有请求体
    • 如果是纯文本数据:getReader()
    • 如果是字节数据如文件数据:getInputStream()

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

在学习下面内容之前,我们先提出两个问题:

  • 什么是请求参数?
  • 请求参数和请求数据的关系是什么?

1.什么是请求参数?

为了能更好的回答上述两个问题,我们拿用户登录的例子来说明

1.1 想要登录网址,需要进入登录页面

1.2 在登录页面输入用户名和密码

1.3 将用户名和密码提交到后台

1.4 后台校验用户名和密码是否正确

1.5 如果正确,则正常登录,如果不正确,则提示用户名或密码错误

上述例子中,用户名和密码其实就是我们所说的请求参数。

2.什么是请求数据?

请求数据则是包含请求行、请求头和请求体的所有数据

3.请求参数和请求数据的关系是什么?

3.1 请求参数是请求数据中的部分内容

3.2 如果是GET请求,请求参数在请求行中

3.3 如果是POST请求,请求参数一般在请求体中

对于请求参数的获取,常用的有以下两种:

  • GET方式:
String getQueryString()
  • POST方式:
BufferedReader getReader();

有了上述的知识储备,我们来实现一个案例需求:

(1)发送一个GET请求并携带用户名,后台接收后打印到控制台

(2)发送一个POST请求并携带用户名,后台接收后打印到控制台

此处大家需要注意的是GET请求和POST请求接收参数的方式不一样,具体实现的代码如下:

@WebServlet("/req1")
public class RequestDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String result = req.getQueryString();System.out.println(result);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {BufferedReader br = req.getReader();String result = br.readLine();System.out.println(result);}
}
  • 对于上述的代码,会存在什么问题呢?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nGw1VrKg-1670137343197)(assets/1628776252445.png)]

  • 如何解决上述重复代码的问题呢?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WUNiRaUs-1670137343197)(assets/1628776433318.png)]

当然,也可以在doGet中调用doPost,在doPost中完成参数的获取和打印,另外需要注意的是,doGet和doPost方法都必须存在,不能删除任意一个。

GET请求和POST请求获取请求参数的方式不一样,在获取请求参数这块该如何实现呢?

要想实现,我们就需要思考:

GET请求方式和POST请求方式区别主要在于获取请求参数的方式不一样,是否可以提供一种统一获取请求参数的方式,从而统一doGet和doPost方法内的代码?

解决方案一:

@WebServlet("/req1")
public class RequestDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取请求方式String method = req.getMethod();//获取请求参数String params = "";if("GET".equals(method)){params = req.getQueryString();}else if("POST".equals(method)){BufferedReader reader = req.getReader();params = reader.readLine();}//将请求参数进行打印控制台System.out.println(params);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req,resp);}
}

使用request的getMethod()来获取请求方式,根据请求方式的不同分别获取请求参数值,这样就可以解决上述问题,但是以后每个Servlet都需要这样写代码,实现起来比较麻烦,这种方案我们不采用

解决方案二:

request对象已经将上述获取请求参数的方法进行了封装,并且request提供的方法实现的功能更强大,以后只需要调用request提供的方法即可,在request的方法中都实现了哪些操作?

(1)根据不同的请求方式获取请求参数,获取的内容如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q7dthNQV-1670137343198)(assets/1628778931277.png)]

(2)把获取到的内容进行分割,内容如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-STKRgVEp-1670137343199)(assets/1628779067793.png)]

(3)把分割后端数据,存入到一个Map集合中:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xUb2L91j-1670137343199)(assets/1628779368501.png)]

注意:因为参数的值可能是一个,也可能有多个,所以Map的值的类型为String数组。

基于上述理论,request对象为我们提供了如下方法:

  • 获取所有参数Map集合
Map getParameterMap()
  • 根据名称获取参数值(数组)
String[] getParameterValues(String name)
  • 根据名称获取参数值(单个值)
String getParameter(String name)

接下来,我们通过案例来把上述的三个方法进行实例演示:

1.修改req.html页面,添加爱好选项,爱好可以同时选多个



Title




游泳 爬山

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kFQDCuqr-1670137343200)(assets/1628780937599.png)]

2.在Servlet代码中获取页面传递GET请求的参数值

2.1获取GET方式的所有请求参数

/*** request 通用方式获取请求参数*/
@WebServlet("/req2")
public class RequestDemo2 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//GET请求逻辑System.out.println("get....");//1. 获取所有参数的Map集合Map map = req.getParameterMap();for (String key : map.keySet()) {// username:zhangsan lisiSystem.out.print(key+":");//获取值String[] values = map.get(key);for (String value : values) {System.out.print(value + " ");}System.out.println();}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {}
}

获取的结果为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dpcFUcqM-1670137343200)(assets/1628780965283.png)]

2.2获取GET请求参数中的爱好,结果是数组值

/*** request 通用方式获取请求参数*/
@WebServlet("/req2")
public class RequestDemo2 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//GET请求逻辑//...System.out.println("------------");String[] hobbies = req.getParameterValues("hobby");for (String hobby : hobbies) {System.out.println(hobby);}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {}
}

获取的结果为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ENtHTJjs-1670137343201)(assets/1628781031437.png)]

2.3获取GET请求参数中的用户名和密码,结果是单个值

/*** request 通用方式获取请求参数*/
@WebServlet("/req2")
public class RequestDemo2 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//GET请求逻辑//...String username = req.getParameter("username");String password = req.getParameter("password");System.out.println(username);System.out.println(password);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {}
}

获取的结果为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6iwmaeLJ-1670137343202)(assets/1628781176434.png)]

3.在Servlet代码中获取页面传递POST请求的参数值

3.1将req.html页面form表单的提交方式改成post

3.2将doGet方法中的内容复制到doPost方法中即可

小结

  • req.getParameter()方法使用的频率会比较高

  • 以后我们再写代码的时候,就只需要按照如下格式来编写:

public class RequestDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//采用request提供的获取请求参数的通用方式来获取请求参数//编写其他的业务代码...}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req,resp);}
}

2.3 IDEA快速创建Servlet

使用通用方式获取请求参数后,屏蔽了GET和POST的请求方式代码的不同,则代码可以定义如下格式:

在这里插入图片描述

由于格式固定,所以我们可以使用IDEA提供的模板来制作一个Servlet的模板,这样我们后期在创建Servlet的时候就会更高效,具体如何实现:

(1)按照自己的需求,修改Servlet创建的模板内容

在这里插入图片描述
例如修改为该模板:

#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
#parse("File Header.java")
@javax.servlet.annotation.WebServlet("/${Entity_Name}")
public class ${Class_Name} extends javax.servlet.http.HttpServlet {@Overrideprotected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {// 处理post请求乱码问题request.setCharacterEncoding("utf-8");// 处理响应乱码问题:字节流需getBytes("UTF-8")response.setContentType("text/html;charset=utf-8"); }@Overrideprotected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {this.doPost(request,response);}
}

(2)使用servlet模板创建Servlet类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c3t5WZlw-1670137343204)(assets/1628782117420.png)]
创建后的效果:

@WebServlet("/ServletDemo")
public class ServletDemo extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 处理post请求乱码问题request.setCharacterEncoding("utf-8");// 处理响应乱码问题:字节流需getBytes("UTF-8")response.setContentType("text/html;charset=utf-8");}@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}
}

2.4 请求参数中文乱码问题

问题展示:

(1)将req.html页面的请求方式修改为get



Title




游泳 爬山

(2)在Servlet方法中获取参数,并打印

/*** 中文乱码问题解决方案*/
@WebServlet("/req4")
public class RequestDemo4 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//1. 获取usernameString username = request.getParameter("username");System.out.println(username);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

(3)启动服务器,页面上输入中文参数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EodNozak-1670137343204)(assets/1628784323297.png)]

(4)查看控制台打印内容

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GOLM65zy-1670137343205)(assets/1628784356157.png)]

(5)把req.html页面的请求方式改成post,再次发送请求和中文参数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J5rnbHSq-1670137343206)(assets/1628784425182.png)]

(6)查看控制台打印内容,依然为乱码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YdjdeC5M-1670137343206)(assets/1628784356157.png)]

通过上面的案例,会发现,不管是GET还是POST请求,在发送的请求参数中如果有中文,在后台接收的时候,都会出现中文乱码的问题。具体该如何解决呢?

2.4.1 POST请求解决方案

  • 分析出现中文乱码的原因:
    • POST的请求参数是通过request的getReader()来获取流中的数据
    • TOMCAT在获取流的时候采用的编码是ISO-8859-1
    • ISO-8859-1编码是不支持中文的,所以会出现乱码
  • 解决方案:
    • 页面设置的编码格式为UTF-8
    • 把TOMCAT在获取流数据之前的编码设置为UTF-8
    • 通过request.setCharacterEncoding(“UTF-8”)设置编码,UTF-8也可以写成小写

修改后的代码为:

/*** 中文乱码问题解决方案*/
@WebServlet("/req4")
public class RequestDemo4 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//1. 解决乱码: POST getReader()//设置字符输入流的编码,设置的字符集要和页面保持一致request.setCharacterEncoding("UTF-8");//2. 获取usernameString username = request.getParameter("username");System.out.println(username);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

重新发送POST请求,就会在控制台看到正常展示的中文结果。

至此POST请求中文乱码的问题就已经解决,但是这种方案不适用于GET请求,这个原因是什么呢,咱们下面再分析。

2.4.2 GET请求解决方案

刚才提到一个问题是POST请求的中文乱码解决方案为什么不适用GET请求?

  • GET请求获取请求参数的方式是request.getQueryString()
  • POST请求获取请求参数的方式是request.getReader()
  • request.setCharacterEncoding(“utf-8”)是设置request处理流的编码
  • getQueryString方法并没有通过流的方式获取数据

所以GET请求不能用设置编码的方式来解决中文乱码问题,那问题又来了,如何解决GET请求的中文乱码呢?

  1. 首先我们需要先分析下GET请求出现乱码的原因:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z2zrMaJz-1670137343207)(assets/1628829610823.png)]

(1)浏览器通过HTTP协议发送请求和数据给后台服务器(Tomcat)

(2)浏览器在发送HTTP的过程中会对中文数据进行URL编码

(3)在进行URL编码的时候会采用页面标签指定的UTF-8的方式进行编码,张三编码后的结果为%E5%BC%A0%E4%B8%89

(4)后台服务器(Tomcat)接收到%E5%BC%A0%E4%B8%89后会默认按照ISO-8859-1进行URL解码

(5)由于前后编码与解码采用的格式不一样,就会导致后台获取到的数据为乱码。

思考: 如果把req.html页面的标签的charset属性改成ISO-8859-1,后台不做操作,能解决中文乱码问题么?

答案是否定的,因为ISO-8859-1本身是不支持中文展示的,所以改了标签的charset属性后,会导致页面上的中文内容都无法正常展示。

分析完上面的问题后,我们会发现,其中有两个我们不熟悉的内容就是URL编码URL解码,什么是URL编码,什么又是URL解码呢?

URL编码

这块知识我们只需要了解下即可,具体编码过程分两步,分别是:

(1)将字符串按照编码方式转为二进制

(2)每个字节转为2个16进制数并在前边加上%

张三按照UTF-8的方式转换成二进制的结果为:

1110 0101 1011 1100 1010 0000 1110 0100 1011 1000 1000 1001

这个结果是如何计算的?

使用http://www.mytju.com/classcode/tools/encode_utf8.asp,输入张三

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Li0KBweO-1670137343207)(assets/1628833310473.png)]

就可以获取张和三分别对应的10进制,然后在使用计算器,选择程序员模式,计算出对应的二进制数据结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gaWfIxzs-1670137343208)(assets/1628833496171.png)]

在计算的十六进制结果中,每两位前面加一个%,就可以获取到%E5%BC%A0%E4%B8%89

当然你从上面所提供的网站中就已经能看到编码16进制的结果了:

在这里插入图片描述

但是对于上面的计算过程,如果没有工具,纯手工计算的话,相对来说还是比较复杂的,我们也不需要进行手动计算,在Java中已经为我们提供了编码和解码的API工具类可以让我们更快速的进行编码和解码:

编码:

java.net.URLEncoder.encode("需要被编码的内容","字符集(UTF-8)")

解码:

java.net.URLDecoder.decode("需要被解码的内容","字符集(UTF-8)")

接下来咱们对张三来进行编码和解码

public class URLDemo {public static void main(String[] args) throws UnsupportedEncodingException {String username = "张三";//1. URL编码String encode = URLEncoder.encode(username, "utf-8");System.out.println(encode); //打印:%E5%BC%A0%E4%B8%89//2. URL解码//String decode = URLDecoder.decode(encode, "utf-8");//打印:张三String decode = URLDecoder.decode(encode, "ISO-8859-1");//打印:`å¼ ä¸ `System.out.println(decode);}
}

到这,我们就可以分析出GET请求中文参数出现乱码的原因了,

  • 浏览器把中文参数按照UTF-8进行URL编码
  • Tomcat对获取到的内容进行了ISO-8859-1的URL解码
  • 在控制台就会出现类上å¼ ä¸‰的乱码,最后一位是个空格
  1. 清楚了出现乱码的原因,接下来我们就需要想办法进行解决

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K2h8Q3QD-1670137343209)(assets/1628846824194.png)]

从上图可以看到,

  • 在进行编码和解码的时候,不管使用的是哪个字符集,他们对应的%E5%BC%A0%E4%B8%89是一致的

  • 那他们对应的二进制值也是一样的,为:

    •   1110 0101 1011 1100 1010 0000 1110 0100 1011 1000 1000 1001
      
  • 所以我们可以考虑把å¼ ä¸‰转换成字节,在把字节转换成张三,在转换的过程中是它们的编码一致,就可以解决中文乱码问题。

具体的实现步骤为:

1.按照ISO-8859-1编码获取乱码å¼ ä¸‰对应的字节数组

2.按照UTF-8编码获取字节数组对应的字符串

实现代码如下:

public class URLDemo {public static void main(String[] args) throws UnsupportedEncodingException {String username = "张三";//1. URL编码String encode = URLEncoder.encode(username, "utf-8");System.out.println(encode);//2. URL解码String decode = URLDecoder.decode(encode, "ISO-8859-1");System.out.println(decode); //此处打印的是对应的乱码数据//3. 转换为字节数据,编码byte[] bytes = decode.getBytes("ISO-8859-1");for (byte b : bytes) {System.out.print(b + " ");}//此处打印的是:-27 -68 -96 -28 -72 -119//4. 将字节数组转为字符串,解码String s = new String(bytes, "utf-8");System.out.println(s); //此处打印的是张三}
}

说明:在第18行中打印的数据是-27 -68 -96 -28 -72 -119张三转换成的二进制数据1110 0101 1011 1100 1010 0000 1110 0100 1011 1000 1000 1001为什么不一样呢?

其实打印出来的是十进制数据,我们只需要使用计算机换算下就能得到他们的对应关系,如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EsAtcv28-1670137343210)(assets/1628849231208.png)]

至此对于GET请求中文乱码的解决方案,我们就已经分析完了,最后在代码中去实现下:

/*** 中文乱码问题解决方案*/
@WebServlet("/req4")
public class RequestDemo4 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//1. 解决乱码:POST,getReader()//request.setCharacterEncoding("UTF-8");//设置字符输入流的编码//2. 获取usernameString username = request.getParameter("username");System.out.println("解决乱码前:"+username);//3. GET,获取参数的方式:getQueryString// 乱码原因:tomcat进行URL解码,默认的字符集ISO-8859-1/* //3.1 先对乱码数据进行编码:转为字节数组byte[] bytes = username.getBytes(StandardCharsets.ISO_8859_1);//3.2 字节数组解码username = new String(bytes, StandardCharsets.UTF_8);*/username  = new String(username.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8);System.out.println("解决乱码后:"+username);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

注意

  • request.setCharacterEncoding("UTF-8")代码注释掉后,会发现GET请求参数乱码解决方案同时也可也把POST请求参数乱码的问题也解决了
  • 只不过对于POST请求参数一般都会比较多,采用这种方式解决乱码起来比较麻烦,所以对于POST请求还是建议使用设置编码的方式进行。

另外需要说明一点的是Tomcat8.0之后,已将GET请求乱码问题解决,设置默认的解码方式为UTF-8

小结

  1. 中文乱码解决方案
  • POST请求和GET请求的参数中如果有中文,后台接收数据就会出现中文乱码问题

    GET请求在Tomcat8.0以后的版本就不会出现了

  • POST请求解决方案是:设置输入流的编码

    request.setCharacterEncoding("UTF-8");
    注意:设置的字符集要和页面保持一致
    
  • 通用方式(GET/POST):需要先解码,再编码

    new String(username.getBytes("ISO-8859-1"),"UTF-8");
    
  1. URL编码实现方式:
  • 编码:

    URLEncoder.encode(str,"UTF-8");
    
  • 解码:

    URLDecoder.decode(s,"ISO-8859-1");
    

2.5 Request请求转发

  1. 请求转发(forward):一种在服务器内部的资源跳转方式。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2jBEXv8j-1670137343211)(assets/1628851404283.png)]

(1)浏览器发送请求给服务器,服务器中对应的资源A接收到请求

(2)资源A处理完请求后将请求发给资源B

(3)资源B处理完后将结果响应给浏览器

(4)请求从资源A到资源B的过程就叫请求转发

  1. 请求转发的实现方式:
req.getRequestDispatcher("资源B路径").forward(req,resp);

具体如何来使用,我们先来看下需求:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-USnUlEPT-1670137343211)(assets/1628854783523.png)]

针对上述需求,具体的实现步骤为:

1.创建一个RequestDemo5类,接收/req5的请求,在doGet方法中打印demo5

2.创建一个RequestDemo6类,接收/req6的请求,在doGet方法中打印demo6

3.在RequestDemo5的方法中使用

​ req.getRequestDispatcher(“/req6”).forward(req,resp)进行请求转发

4.启动测试

(1)创建RequestDemo5类

/*** 请求转发*/
@WebServlet("/req5")
public class RequestDemo5 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("demo5...");}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

(2)创建RequestDemo6类

/*** 请求转发*/
@WebServlet("/req6")
public class RequestDemo6 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("demo6...");}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

(3)在RequestDemo5的doGet方法中进行请求转发

/*** 请求转发*/
@WebServlet("/req5")
public class RequestDemo5 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("demo5...");//请求转发request.getRequestDispatcher("/req6").forward(request,response);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

(4)启动测试

访问http://localhost:8080/request-demo/req5,就可以在控制台看到如下内容:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zQpe4M9n-1670137343212)(assets/1628855192876.png)]

说明请求已经转发到了/req6

  1. 请求转发资源间共享数据:使用Request对象

此处主要解决的问题是把请求从/req5转发到/req6的时候,如何传递数据给/req6

需要使用request对象提供的三个方法:

  • 存储数据到request域[范围,数据是存储在request对象]中
void setAttribute(String name,Object o);
  • 根据key获取值
Object getAttribute(String name);
  • 根据key删除该键值对
void removeAttribute(String name);

接着上个需求来:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yZr8VbrJ-1670137343213)(assets/1628856995417.png)]

1.在RequestDemo5的doGet方法中转发请求之前,将数据存入request域对象中

2.在RequestDemo6的doGet方法从request域对象中获取数据,并将数据打印到控制台

3.启动访问测试

(1)修改RequestDemo5中的方法

@WebServlet("/req5")
public class RequestDemo5 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("demo5...");//存储数据request.setAttribute("msg","hello");//请求转发request.getRequestDispatcher("/req6").forward(request,response);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

(2)修改RequestDemo6中的方法

/*** 请求转发*/
@WebServlet("/req6")
public class RequestDemo6 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("demo6...");//获取数据Object msg = request.getAttribute("msg");System.out.println(msg);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

(3)启动测试

访问http://localhost:8080/request-demo/req5,就可以在控制台看到如下内容:

在这里插入图片描述

此时就可以实现在转发多个资源之间共享数据。

  1. 请求转发的特点
  • 浏览器地址栏路径不发生变化

    虽然后台从/req5转发到/req6,但是浏览器的地址一直是/req5,未发生变化

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-igYJR5PV-1670137343215)(assets/1628857365153.png)]

  • 只能转发到当前服务器的内部资源

    不能从一个服务器通过转发访问另一台服务器

  • 一次请求,可以在转发资源间使用request共享数据

    虽然后台从/req5转发到/req6,但是这个只有一次请求

3,Response对象

前面讲解完Request对象,接下来我们回到刚开始的那张图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JAGnbX23-1670137343215)(assets/1628857632899.png)]

  • Request:使用request对象来获取请求数据
  • Response:使用response对象来设置响应数据

Reponse的继承体系和Request的继承体系也非常相似:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ohUzuucx-1670137343216)(assets/1628857761317.png)]

介绍完Response的相关体系结构后,接下来对于Response我们需要学习如下内容:

  • Response设置响应数据的功能介绍
  • Response完成重定向
  • Response响应字符数据
  • Response响应字节数据

3.1 Response设置响应数据功能介绍

HTTP响应数据总共分为三部分内容,分别是响应行、响应头、响应体,对于这三部分内容的数据,respone对象都提供了哪些方法来进行设置?

  1. 响应行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-50Pn3HRs-1670137343216)(assets/1628858926498.png)]

对于响应头,比较常用的就是设置响应状态码:

void setStatus(int sc);
  1. 响应头

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kcsIXFqg-1670137343217)(assets/1628859051368.png)]

设置响应头键值对:

void setHeader(String name,String value);
  1. 响应体

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fX4VAhRR-1670137343217)(assets/1628859268095.png)]

对于响应体,是通过字符、字节输出流的方式往浏览器写,

获取字符输出流:

PrintWriter getWriter();

获取字节输出流

ServletOutputStream getOutputStream();

介绍完这些方法后,后面我们会通过案例把这些方法都用一用,首先先来完成下重定向的功能开发。

3.2 Respones请求重定向

  1. Response重定向(redirect):一种资源跳转方式。

在这里插入图片描述

(1)浏览器发送请求给服务器,服务器中对应的资源A接收到请求

(2)资源A现在无法处理该请求,就会给浏览器响应一个302的状态码+location的一个访问资源B的路径

(3)浏览器接收到响应状态码为302就会重新发送请求到location对应的访问地址去访问资源B

(4)资源B接收到请求后进行处理并最终给浏览器响应结果,这整个过程就叫重定向

  1. 重定向的实现方式:
resp.setStatus(302);
resp.setHeader("location","资源B的访问路径");

具体如何来使用,我们先来看下需求:

在这里插入图片描述

针对上述需求,具体的实现步骤为:

1.创建一个ResponseDemo1类,接收/resp1的请求,在doGet方法中打印resp1....

2.创建一个ResponseDemo2类,接收/resp2的请求,在doGet方法中打印resp2....

3.在ResponseDemo1的方法中使用

​ response.setStatus(302);

​ response.setHeader(“Location”,“/request-demo/resp2”) 来给前端响应结果数据

4.启动测试

(1)创建ResponseDemo1类

@WebServlet("/resp1")
public class ResponseDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("resp1....");}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

(2)创建ResponseDemo2类

@WebServlet("/resp2")
public class ResponseDemo2 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("resp2....");}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

(3)在ResponseDemo1的doGet方法中给前端响应数据

@WebServlet("/resp1")
public class ResponseDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("resp1....");//重定向//1.设置响应状态码 302response.setStatus(302);//2. 设置响应头 Locationresponse.setHeader("Location","/request-demo/resp2");}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

(4)启动测试

访问http://localhost:8080/request-demo/resp1,就可以在控制台看到如下内容:

在这里插入图片描述

说明/resp1/resp2都被访问到了。到这重定向就已经完成了。

虽然功能已经实现,但是从设置重定向的两行代码来看,会发现除了重定向的地址不一样,其他的内容都是一模一样,所以request对象给我们提供了简化的编写方式为:

resposne.sendRedirect("/request-demo/resp2")

所以第3步中的代码就可以简化为:

@WebServlet("/resp1")
public class ResponseDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("resp1....");//重定向resposne.sendRedirect("/request-demo/resp2");}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}
  1. 重定向的特点
  • 浏览器地址栏路径发送变化

    当进行重定向访问的时候,由于是由浏览器发送的两次请求,所以地址会发生变化

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gtV2VTHU-1670137343220)(assets/1628861893130.png)]

  • 可以重定向到任何位置的资源(服务内容、外部均可)

    因为第一次响应结果中包含了浏览器下次要跳转的路径,所以这个路径是可以任意位置资源。

  • 两次请求,不能在多个资源使用request共享数据

    因为浏览器发送了两次请求,是两个不同的request对象,就无法通过request对象进行共享数据

介绍完请求重定向请求转发以后,接下来需要把这两个放在一块对比下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ec9HvMcD-1670137343221)(assets/1628862170296.png)]

以后到底用哪个,还是需要根据具体的业务来决定。

3.3 路径问题

  1. 问题1:转发的时候路径上没有加/request-demo而重定向加了,那么到底什么时候需要加,什么时候不需要加呢?

在这里插入图片描述

其实判断的依据很简单,只需要记住下面的规则即可:

  • 浏览器使用:需要加虚拟目录(项目访问路径)
  • 服务端使用:不需要加虚拟目录

对于转发来说,因为是在服务端进行的,所以不需要加虚拟目录

对于重定向来说,路径最终是由浏览器来发送请求,就需要添加虚拟目录。

掌握了这个规则,接下来就通过一些练习来强化下知识的学习:

答案:

1.超链接,从浏览器发送,需要加
2.表单,从浏览器发送,需要加
3.转发,是从服务器内部跳转,不需要加
4.重定向,是由浏览器进行跳转,需要加。
  1. 问题2:在重定向的代码中,/request-demo是固定编码的,如果后期通过Tomcat插件配置了项目的访问路径,那么所有需要重定向的地方都需要重新修改,该如何优化?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-icduOILK-1670137343222)(assets/1628863270545.png)]

答案也比较简单,我们可以在代码中动态去获取项目访问的虚拟目录,具体如何获取,我们可以借助前面咱们所学习的request对象中的getContextPath()方法,修改后的代码如下:

@WebServlet("/resp1")
public class ResponseDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("resp1....");//简化方式完成重定向//动态获取虚拟目录String contextPath = request.getContextPath();response.sendRedirect(contextPath+"/resp2");}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

重新启动访问测试,功能依然能够实现,此时就可以动态获取项目访问的虚拟路径,从而降低代码的耦合度。
在这里插入图片描述

3.4 Response响应字符数据

要想将字符数据写回到浏览器,我们需要两个步骤:

  • 通过Response对象获取字符输出流: PrintWriter writer = resp.getWriter();

  • 通过字符输出流写数据: writer.write(“aaa”);

接下来,我们实现通过些案例把响应字符数据给实际应用下:

  1. 返回一个简单的字符串aaa
/*** 响应字符数据:设置字符数据的响应体*/
@WebServlet("/resp3")
public class ResponseDemo3 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.setContentType("text/html;charset=utf-8");//1. 获取字符输出流PrintWriter writer = response.getWriter();writer.write("aaa");}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

在这里插入图片描述

  1. 返回一串html字符串,并且能被浏览器解析
PrintWriter writer = response.getWriter();
//content-type,告诉浏览器返回的数据类型是HTML类型数据,这样浏览器才会解析HTML标签
response.setHeader("content-type","text/html");
writer.write("

aaa

");

在这里插入图片描述

==注意:==一次请求响应结束后,response对象就会被销毁掉,所以不要手动关闭流。

  1. 返回一个中文的字符串你好,需要注意设置响应数据的编码为utf-8
//设置响应的数据格式及数据的编码
response.setContentType("text/html;charset=utf-8");
writer.write("你好");

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DMxv0tDk-1670137343224)(assets/1628864390263.png)]

3.3 Response响应字节数据

要想将字节数据写回到浏览器,我们需要两个步骤:

  • 通过Response对象获取字节输出流:ServletOutputStream outputStream = resp.getOutputStream();

  • 通过字节输出流写数据: outputStream.write(字节数据);

接下来,我们实现通过些案例把响应字符数据给实际应用下:

  1. 返回一个图片文件到浏览器
/*** 响应字节数据:设置字节数据的响应体*/
@WebServlet("/resp4")
public class ResponseDemo4 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//1. 读取文件FileInputStream fis = new FileInputStream("d://a.jpg");//2. 获取response字节输出流ServletOutputStream os = response.getOutputStream();//3. 完成流的copybyte[] buff = new byte[1024];int len = 0;while ((len = fis.read(buff))!= -1){os.write(buff,0,len);}fis.close();}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jpe9jS2F-1670137343225)(assets/1628864883564.png)]

上述代码中,对于流的copy的代码还是比较复杂的,所以我们可以使用别人提供好的方法来简化代码的开发,具体的步骤是:

(1)pom.xml添加依赖

commons-iocommons-io2.6

(2)调用工具类方法

//fis:输入流
//os:输出流
IOUtils.copy(fis,os);

优化后的代码:

/*** 响应字节数据:设置字节数据的响应体*/
@WebServlet("/resp4")
public class ResponseDemo4 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//1. 读取文件FileInputStream fis = new FileInputStream("d://a.jpg");//2. 获取response字节输出流ServletOutputStream os = response.getOutputStream();//3. 完成流的copyIOUtils.copy(fis,os);fis.close();}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

4,用户注册登录案例

接下来我们通过两个比较常见的案例,一个是注册,一个是登录来对学习的内容进行一个实战演练,首先来实现用户登录。

4.1 用户登录

4.1.1 需求分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KzVokam8-1670137343225)(assets/1628865728305.png)]

  1. 用户在登录页面输入用户名和密码,提交请求给LoginServlet
  2. 在LoginServlet中接收请求和数据[用户名和密码]
  3. 在LoginServlt中通过Mybatis实现调用UserMapper来根据用户名和密码查询数据库表
  4. 将查询的结果封装到User对象中进行返回
  5. 在LoginServlet中判断返回的User对象是否为null
  6. 如果为nul,说明根据用户名和密码没有查询到用户,则登录失败,返回"登录失败"数据给前端
  7. 如果不为null,则说明用户存在并且密码正确,则登录成功,返回"登录成功"数据给前端

4.1.2 环境准备

  1. 复制资料中的静态页面到项目的webapp目录下

参考资料\1. 登陆注册案例\1. 静态页面,拷贝完效果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TgoHWLLJ-1670137343226)(assets/1628866248169.png)]
login.html


login



registet.html



欢迎注册

欢迎注册

已有帐号? 登录
用户名
用户名不太受欢迎
密码
密码格式有误

login.css

* {margin: 0;padding: 0;
}html {height: 100%;width: 100%;overflow: hidden;margin: 0;padding: 0;background: url(../imgs/Desert.jpg) no-repeat 0px 0px;background-repeat: no-repeat;background-size: 100% 100%;-moz-background-size: 100% 100%;
}body {display: flex;align-items: center;justify-content: center;height: 100%;
}#loginDiv {width: 37%;display: flex;justify-content: center;align-items: center;height: 300px;background-color: rgba(75, 81, 95, 0.3);box-shadow: 7px 7px 17px rgba(52, 56, 66, 0.5);border-radius: 5px;
}#name_trip {margin-left: 50px;color: red;
}p {margin-top: 30px;margin-left: 20px;color: azure;
}input {margin-left: 15px;border-radius: 5px;border-style: hidden;height: 30px;width: 140px;background-color: rgba(216, 191, 216, 0.5);outline: none;color: #f0edf3;padding-left: 10px;
}
#username{width: 200px;
}
#password{width: 202px;
}
.button {border-color: cornsilk;background-color: rgba(100, 149, 237, .7);color: aliceblue;border-style: hidden;border-radius: 5px;width: 100px;height: 31px;font-size: 16px;
}#subDiv {text-align: center;margin-top: 30px;
}
#loginMsg{text-align: center;color: aliceblue;
}

registet.css

* {margin: 0;padding: 0;list-style-type: none;
}
.reg-content{padding: 30px;margin: 3px;
}
a, img {border: 0;
}body {background-image: url("../imgs/reg_bg_min.jpg") ;text-align: center;
}table {border-collapse: collapse;border-spacing: 0;
}td, th {padding: 0;height: 90px;}
.inputs{vertical-align: top;
}.clear {clear: both;
}.clear:before, .clear:after {content: "";display: table;
}.clear:after {clear: both;
}.form-div {background-color: rgba(255, 255, 255, 0.27);border-radius: 10px;border: 1px solid #aaa;width: 424px;margin-top: 150px;margin-left:1050px;padding: 30px 0 20px 0px;font-size: 16px;box-shadow: inset 0px 0px 10px rgba(255, 255, 255, 0.5), 0px 0px 15px rgba(75, 75, 75, 0.3);text-align: left;
}.form-div input[type="text"], .form-div input[type="password"], .form-div input[type="email"] {width: 268px;margin: 10px;line-height: 20px;font-size: 16px;
}.form-div input[type="checkbox"] {margin: 20px 0 20px 10px;
}.form-div input[type="button"], .form-div input[type="submit"] {margin: 10px 20px 0 0;
}.form-div table {margin: 0 auto;text-align: right;color: rgba(64, 64, 64, 1.00);
}.form-div table img {vertical-align: middle;margin: 0 0 5px 0;
}.footer {color: rgba(64, 64, 64, 1.00);font-size: 12px;margin-top: 30px;
}.form-div .buttons {float: right;
}input[type="text"], input[type="password"], input[type="email"] {border-radius: 8px;box-shadow: inset 0 2px 5px #eee;padding: 10px;border: 1px solid #D4D4D4;color: #333333;margin-top: 5px;
}input[type="text"]:focus, input[type="password"]:focus, input[type="email"]:focus {border: 1px solid #50afeb;outline: none;
}input[type="button"], input[type="submit"] {padding: 7px 15px;background-color: #3c6db0;text-align: center;border-radius: 5px;overflow: hidden;min-width: 80px;border: none;color: #FFF;box-shadow: 1px 1px 1px rgba(75, 75, 75, 0.3);
}input[type="button"]:hover, input[type="submit"]:hover {background-color: #5a88c8;
}input[type="button"]:active, input[type="submit"]:active {background-color: #5a88c8;
}
.err_msg{color: red;padding-right: 170px;
}
#password_err,#tel_err{padding-right: 195px;
}#reg_btn{margin-right:50px; width: 285px; height: 45px; margin-top:20px;
}
  1. 创建db1数据库,创建tb_user表,创建User实体类

2.1 将资料\1. 登陆注册案例\2. MyBatis环境\tb_user.sql中的sql语句执行下:

-- 创建用户表
CREATE TABLE tb_user(id int primary key auto_increment,username varchar(20) unique,password varchar(32)
);-- 添加数据
INSERT INTO tb_user(username,password) values('zhangsan','123'),('lisi','234');SELECT * FROM tb_user;

2.2 将资料\1. 登陆注册案例\2. MyBatis环境\User.java拷贝到com.itheima.pojo

package com.itheima.pojo;public class User {private Integer id;private String username;private String password;public Integer getId() {return id;}public void setId(Integer 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;}@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", password='" + password + '\'' +'}';}
}

在这里插入图片描述

  1. 在项目的pom.xml导入Mybatis和Mysql驱动坐标
org.mybatismybatis3.5.5
mysqlmysql-connector-java5.1.34

  1. 创建mybatis-config.xml核心配置文件,UserMapper.xml映射文件,UserMapper接口

4.1 将资料\1. 登陆注册案例\2. MyBatis环境\mybatis-config.xml拷贝到resources目录下





4.2 在com.itheima.mapper包下创建UserMapper接口

public interface UserMapper {}

4.3 将资料\1. 登陆注册案例\2. MyBatis环境\UserMapper.xml拷贝到resources目录下

注意:在resources下创建UserMapper.xml的目录时,要使用/分割

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-34y8VKs2-1670137343227)(assets/1628867237329.png)]
UserMapper.xml




至此我们所需要的环境就都已经准备好了,具体该如何实现?

4.1.3 代码实现

  1. 在UserMapper接口中提供一个根据用户名和密码查询用户对象的方法
/*** 根据用户名和密码查询用户对象* @param username* @param password* @return*/@Select("select * from tb_user where username = #{username} and password = #{password}")User select(@Param("username") String username,@Param("password")  String password);

说明

@Param注解的作用:用于传递参数,是方法的参数可以与SQL中的字段名相对应。

  1. 修改loign.html

login

LOGIN IN

Username:

Password:

  1. 编写LoginServlet
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//1. 接收用户名和密码String username = request.getParameter("username");String password = request.getParameter("password");//2. 调用MyBatis完成查询//2.1 获取SqlSessionFactory对象String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//2.2 获取SqlSession对象SqlSession sqlSession = sqlSessionFactory.openSession();//2.3 获取MapperUserMapper userMapper = sqlSession.getMapper(UserMapper.class);//2.4 调用方法User user = userMapper.select(username, password);//2.5 释放资源sqlSession.close();//获取字符输出流,并设置content typeresponse.setContentType("text/html;charset=utf-8");PrintWriter writer = response.getWriter();//3. 判断user释放为nullif(user != null){// 登陆成功writer.write("登陆成功");}else {// 登陆失败writer.write("登陆失败");}}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}
  1. 启动服务器测试

4.1 如果用户名和密码输入错误,则

在这里插入图片描述

4.2 如果用户名和密码输入正确,则

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O5JV7BY2-1670137343229)(assets/1628867801708.png)]

至此用户的登录功能就已经完成了~

4.2 用户注册

4.2.1 需求分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-enNXNNdJ-1670137343229)(assets/1628867904783.png)]

  1. 用户在注册页面输入用户名和密码,提交请求给RegisterServlet
  2. 在RegisterServlet中接收请求和数据[用户名和密码]
  3. 在RegisterServlet中通过Mybatis实现调用UserMapper来根据用户名查询数据库表
  4. 将查询的结果封装到User对象中进行返回
  5. 在RegisterServlet中判断返回的User对象是否为null
  6. 如果为nul,说明根据用户名可用,则调用UserMapper来实现添加用户
  7. 如果不为null,则说明用户不可以,返回"用户名已存在"数据给前端

4.2.2 代码编写

  1. 编写UserMapper提供根据用户名查询用户数据方法和添加用户方法
/**
* 根据用户名查询用户对象
* @param username
* @return
*/
@Select("select * from tb_user where username = #{username}")
User selectByUsername(String username);/**
* 添加用户
* @param user
*/
@Insert("insert into tb_user values(null,#{username},#{password})")
void add(User user);
  1. 修改register.html


欢迎注册

欢迎注册

已有帐号? 登录
用户名
用户名不太受欢迎
密码
密码格式有误

  1. 创建RegisterServlet类
@WebServlet("/registerServlet")
public class RegisterServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//1. 接收用户数据String username = request.getParameter("username");String password = request.getParameter("password");//封装用户对象User user = new User();user.setUsername(username);user.setPassword(password);//2. 调用mapper 根据用户名查询用户对象//2.1 获取SqlSessionFactory对象String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//2.2 获取SqlSession对象SqlSession sqlSession = sqlSessionFactory.openSession();//2.3 获取MapperUserMapper userMapper = sqlSession.getMapper(UserMapper.class);//2.4 调用方法User u = userMapper.selectByUsername(username);//3. 判断用户对象释放为nullif( u == null){// 用户名不存在,添加用户userMapper.add(user);// 提交事务sqlSession.commit();// 释放资源sqlSession.close();}else {// 用户名存在,给出提示信息response.setContentType("text/html;charset=utf-8");response.getWriter().write("用户名已存在");}}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}
  1. 启动服务器进行测试

4.1 如果测试成功,则在数据库中就能查看到新注册的数据

4.2 如果用户已经存在,则在页面上展示 用户名已存在 的提示信息

4.3 SqlSessionFactory工具类抽取

上面两个功能已经实现,但是在写Servlet的时候,因为需要使用Mybatis来完成数据库的操作,所以对于Mybatis的基础操作就出现了些重复代码,如下

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

有了这些重复代码就会造成一些问题:

  • 重复代码不利于后期的维护
  • SqlSessionFactory工厂类进行重复创建
    • 就相当于每次买手机都需要重新创建一个手机生产工厂来给你制造一个手机一样,资源消耗非常大但性能却非常低。所以这么做是不允许的。

那如何来优化呢?

  • 代码重复可以抽取工具类
  • 对指定代码只需要执行一次可以使用静态代码块

有了这两个方向后,代码具体该如何编写?

public class SqlSessionFactoryUtils {private static SqlSessionFactory sqlSessionFactory;static {//静态代码块会随着类的加载而自动执行,且只执行一次try {String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);} catch (IOException e) {e.printStackTrace();}}public static SqlSessionFactory getSqlSessionFactory(){return sqlSessionFactory;}
}

工具类抽取以后,以后在对Mybatis的SqlSession进行操作的时候,就可以直接使用

SqlSessionFactory sqlSessionFactory =SqlSessionFactoryUtils.getSqlSessionFactory();

这样就可以很好的解决上面所说的代码重复和重复创建工厂导致性能低的问题了。

相关内容

热门资讯

那一次我真感动作文 那一次我真感动作文(精选48篇)  在日常学习、工作和生活中,大家一定都接触过作文吧,作文根据写作时...
海底漫游记作文 海底漫游记作文(精选43篇)  无论是在学校还是在社会中,大家总免不了要接触或使用作文吧,借助作文可...
五一游记作文 五一游记作文(精选49篇)  在生活、工作和学习中,大家总免不了要接触或使用作文吧,作文根据体裁的不...
我爱长沙作文 我爱长沙作文我生在长沙,长在长沙。听大人们说原来的长沙,听别人说现在的长沙,现在长沙变化非常大,我也...
新年的作文 关于新年的作文(15篇)  在日常的学习、工作、生活中,大家或多或少都会接触过作文吧,作文是由文字组...
美丽的秋天作文 最新美丽的秋天作文范文(精选18篇)  在平凡的学习、工作、生活中,大家总少不了接触作文吧,作文是一...
给吴老师的一封信 给吴老师的一封信  老师是您辛勤教导了我们,给予了我们无穷无尽的知识,下面是小编为大家搜集整理的写给...
最美的瞬间作文600字 最美的瞬间作文600字  在日常学习、工作抑或是生活中,大家或多或少都会接触过作文吧,作文是从内部言...
厨房变形记作文 厨房变形记作文········ 厨房变形记····这个纪录片主要讲的是一个城市公子哥易虎臣和一个在贫...
下象棋 下象棋下象棋1我的课余爱好有很多,有玩电脑、看课外书、跑步……其中最喜欢的是下中国象棋。     现...
我学会了炒菜作文400字 我学会了炒菜作文400字(精选70篇)  在平平淡淡的日常中,大家对作文都不陌生吧,作文是由文字组成...
炎热的夏天作文 炎热的夏天作文(精选40篇)  在平平淡淡的学习、工作、生活中,大家都经常看到作文的身影吧,根据写作...
画笔 画笔画笔1  在我的童年中,有许许多多的童年趣事,它们就像一支支五彩缤纷的画笔,在人生的图画上添上了...
关于信守承诺的作文 关于信守承诺的作文800字(精选15篇)  在日常学习、工作抑或是生活中,许多人都有过写作文的经历,...
我熟悉的人作文   我熟悉的人作文(一)  我现在是一名四年级的小学生,说到熟悉的人,我的脑海里自然浮现了妈妈的身影...
校园回忆录作文 校园回忆录作文  在日常学习、工作抑或是生活中,大家都不可避免地要接触到作文吧,作文是人们以书面形式...
快乐精灵作文600字 快乐精灵作文600字五篇  无论是身处学校还是步入社会,大家都接触过作文吧,根据写作命题的特点,作文...
秋天的水果作文 秋天的水果作文(13篇)  无论在学习、工作或是生活中,大家都不可避免地要接触到作文吧,作文是人们以...
试用期个人总结 试用期个人总结范文(精选14篇)  总结是在某一特定时间段对学习和工作生活或其完成情况,包括取得的成...
手的作文 关于手的作文(精选106篇)  在平平淡淡的日常中,许多人都有过写作文的经历,对作文都不陌生吧,根据...