1.Web概述
1.1 Web和JavaWeb的概念
Web是全球广域网,也称为万维网(www),能够通过浏览器访问的网站。
JavaWeb就是用Java技术来解决相关web互联网领域的技术栈
1.2 JavaWeb技术栈
B/S 架构:Browser/Server,浏览器/服务器 架构模式,它的特点是,客户端只需要浏览器,应用程序的逻辑和数据都存储在服务器端。
浏览器只需要请求服务器,获取Web资源,服务器把Web资源发送给浏览器即可。
所以说B/S架构的好处:易于维护升级:服务器端升级后,客户端无需任何部署就可以使用到新的版本。
(HTML+CSS+JavaScript)但是由于做出来的这些内容都是静态的,这就会导致所有的人看到的内容将是一模一样。
1.张三
登录以后在网页的右上角看到的是 张三
,而李四
登录以后看到的则是李四
。所以不同的用户访问相同的资源看到的内容大多数是不一样的,要想实现这样的效果,光靠静态资源是无法实现的。所以靠动态资源处理
2.动态资源主要包含Servlet、JSP等,主要用来负责逻辑处理
动态资源处理完逻辑后会把得到的结果交给静态资源来进行展示,动态资源和静态资源要结合一起使用。动态资源处理完逻辑后会把得到的结果交给静态资源来进行展示,动态资源和静态资源要结合一起使用。
3.动态资源虽然可以处理逻辑,但是当用户来登录百度的时候,就需要输入用户名
和密码
,这个时候我们就又需要解决的一个问题是,用户在注册的时候填入的用户名和密码、以及我们经常会访问到一些数据列表的内容展示(如下图所示),这些数据都存储在哪里?我们需要的时候又是从哪里来取呢?数据库
4.数据库
主要负责存储数据。
5.HTTP协议
HTTP协议:主要定义通信规则
浏览器发送请求给服务器,服务器响应数据给浏览器,这整个过程都需要遵守一定的规则,之前大家学习过TCP、UDP,这些都属于规则,这里我们需要使用的是HTTP协议,这也是一种规则。
6.Web服务器
Web服务器:负责解析 HTTP 协议,解析请求数据,并发送响应数据
浏览器按照HTTP协议发送请求和数据,后台就需要一个Web服务器软件来根据HTTP协议解析请求和数据,然后把处理结果再按照HTTP协议发送给浏览器
Web服务器软件有很多,我们课程中将学习的是目前最为常用的Tomcat服务器
2.HTTP
2.1 简介
HyperText Transfer Protocol,超文本传输协议,规定了浏览器和服务器之间数据传输的规则。也就是客户端和浏览器交互数据的格式
也就是客户端向服务器端发送请求的数据和服务器收到请求数据的规则相同才能解析数据 同理服务器端发送响应数据和客户端的数据规则一样 客户端浏览器才能解析
HTTP协议特点
HTTP协议有它自己的一些特点,分别是:
基于TCP协议: 面向连接,安全
TCP是一种面向连接的(建立连接之前是需要经过三次握手)、可靠的、基于字节流的传输层通信协议,在数据传输方面更安全。
基于请求-响应模型的:一次请求对应一次响应
请求和响应是一一对应关系
HTTP协议是无状态协议:对于事物处理没有记忆能力。每次请求-响应都是独立的
无状态指的是客户端发送HTTP请求给服务端之后,服务端根据请求响应数据,响应完后,不会记录任何信息。这种特性有优点也有缺点 总的来说后一次请求不会记录前一次请求的数据
- 缺点:多次请求间不能共享数据
Java使用会话技术Cookie Session 来解决这个问题
- 优点:速度快
请求之间无法共享数据会引发的问题,如: - 京东购物,
加入购物车
和去购物车结算
是两次请求, - HTTP协议的无状态特性,加入购物车请求响应结束后,并未记录加入购物车是何商品
- 发起去购物车结算的请求后,因为无法获取哪些商品加入了购物车,会导致此次请求无法正确展示数据
2.2 请求数据格式
2.3 响应状态码
响应数据格式
响应数据总共分为三部分内容,分别是响应行、响应头、响应体
响应行:响应数据的第一行,响应行包含三块内容,分别是 HTTP/1.1[HTTP协议及版本] 200[响应状态码] ok[状态码的描述]
响应头:第二行开始,格式为key:value形式
响应头中会包含若干个属性,常见的HTTP响应头有:
Content-Type:表示该响应内容的类型,例如text/html,image/jpeg;
Content-Length:表示该响应内容的长度(字节数);
Content-Encoding:表示该响应压缩算法,例如gzip;
Cache-Control:指示客户端应如何缓存,例如max-age=300表示可以最多缓存300秒
响应体: 最后一部分。存放响应数据
上图中…这部分内容就是响应体,它和响应头之间有一个空行隔开。
3.Tomcat
3.1 简介
3.1.1 什么是Web服务器
Web服务器是一个应用程序(软件),对HTTP协议的操作进行封装,使得程序员不必直接对协议进行操作,让Web开发更加便捷。主要功能是"提供网上信息浏览服务"。
Web服务器是安装在服务器端的一款软件,将来我们把自己写的Web项目部署到Web Tomcat服务器软件中,当Web服务器软件启动后,部署在Web服务器软件中的页面就可以直接通过浏览器来访问了。
3.2安装
此处省略
3.3 Maven创建web项目
1.使用骨架
然后这里选中webapp
问题可能原因解决办法
webapp没有小蓝点
https://www.likecs.com/show-308010524.html
4.Servelet
4.1前言
个人理解就是后端web服务器的业务层逻辑代码处理 帮助我们给不同的人(参数)展示不同的动态资源
-
Servlet是JavaWeb最为核心的内容,它是Java提供的一门动态web资源开发技术。
-
使用Servlet就可以实现,根据不同的登录用户在页面上动态显示不同内容。
-
Servlet是JavaEE规范之一,其实就是一个接口,将来我们需要定义Servlet类实现Servlet接口,并由web服务器运行Servlet
Servelet执行很特殊 是由tomcat帮我们创建servelet对象的 并且调用Servelet方法的
什么时候创建 什么时候调用方法就是Servelet生命周期的意义
4.3 执行流程
Servlet程序已经能正常运行,但是我们需要思考个问题: 我们并没有创建ServletDemo1类的对象,也没有调用对象中的service方法,为什么在控制台就打印了servlet hello world~
这句话呢?
要想回答上述问题,我们就需要对Servlet的执行流程进行一个学习。
- 浏览器发出
http://localhost:8080/web-demo/demo1
请求,从请求中可以解析出三部分内容,分别是localhost:8080
、web-demo
、demo1
- 根据
localhost:8080
可以找到要访问的Tomcat Web服务器 - 根据
web-demo
可以找到部署在Tomcat服务器上的web-demo项目 - 根据
demo1
可以找到要访问的是项目中的哪个Servlet类,根据@WebServlet后面的值进行匹配
- 根据
- 找到ServletDemo1这个类后,Tomcat Web服务器就会为ServletDemo1这个类创建一个对象,然后调用对象中的service方法
- ServletDemo1实现了Servlet接口,所以类中必然会重写service方法供Tomcat Web服务器进行调用
- service方法中有ServletRequest和ServletResponse两个参数,ServletRequest封装的是请求数据,ServletResponse封装的是响应数据,后期我们可以通过这两个参数实现前后端的数据交互
问
:能更我说下javaweb的servlet处理程序流程嘛
当客户端向服务器发送HTTP请求时,Servlet容器会进行以下步骤来处理Servlet:
chatgpt
:客户端发出HTTP请求。该请求首先到达Servlet容器。
Servlet容器根据请求中的URL信息找到对应的Servlet,并加载Servlet的Class文件。
Servlet容器创建一个HttpServletRequest对象和一个HttpServletResponse对象,分别表示客户端的请求和服务器的响应。
Servlet容器调用Servlet的service()方法,并将HttpServletRequest和HttpServletResponse对象作为参数传递进去。
Servlet在service()方法中处理请求,并生成响应结果。在此过程中,Servlet可以调用HttpServletRequest对象的方法获取客户端请求信息,也可以调用HttpServletResponse对象的方法生成服务器响应。
Servlet容器将生成的响应发送回客户端,完成请求处理过程。
需要注意的是,Servlet容器会为每个请求都创建一个新的线程来处理,以确保并发请求的正确性和响应效率。此外,在Java Web开发中,Servlet通常作为Controller层来处理请求和响应,将业务逻辑封装在Service层和DAO层中。
问
:Servlet容器将生成的响应数据是放入response对象返回客户端的嘛?
chatgpt
: 是的,Servlet容器会将生成的响应数据存储在HttpServletResponse对象中,并将该对象作为参数传递给Servlet的service()方法。在service()方法中,Servlet会使用HttpServletResponse对象的方法来设置响应头、响应状态码、响应内容等信息。最后,Servlet容器会将HttpServletResponse对象中存储的响应数据发送回客户端。
具体来说,HttpServletResponse对象中有一些常用的方法可以用来设置响应数据,例如:
setContentType(String type):设置响应的内容类型。
setStatus(int sc):设置响应状态码。
getWriter():获取一个PrintWriter对象,用于向客户端输出响应数据。
addHeader(String name, String value):添加一个响应头。
sendRedirect(String location):将客户端重定向到另一个URL。
通过使用这些方法,Servlet可以根据具体的需求设置响应数据,最终将数据返回给客户端。
问:
那么也就是说服务器会把请求的数据封装进request对象中?
chatgpt:
是的,当Servlet容器接收到一个HTTP请求时,它会创建一个HttpServletRequest对象,将请求中的信息封装进该对象中,并将该对象作为参数传递给Servlet的service()方法。在service()方法中,Servlet可以通过HttpServletRequest对象来获取请求的信息,例如请求的方法、URL、请求头、请求参数等等。
HttpServletRequest对象中有一些常用的方法可以用来获取请求数据,例如:
getMethod():获取请求的方法(GET、POST、PUT等)。
getRequestURI():获取请求的URI。
getQueryString():获取请求的查询参数。
getParameter(String name):获取指定名称的请求参数的值。
getHeader(String name):获取指定名称的请求头的值。
通过使用这些方法,Servlet可以从HttpServletRequest对象中获取请求数据,并根据具体的业务逻辑进行处理。最终,Servlet会将处理结果存储在HttpServletResponse对象中,并将该对象返回给Servlet容器,以便将处理结果发送回客户端。
4.4 生命周期
生命周期: 对象的生命周期指一个对象从被创建到被销毁的整个过程。
介绍完Servlet的执行流程后,我们知道Servlet是由Tomcat Web服务器帮我们创建的。
接下来咱们再来思考一个问题:Tomcat什么时候创建的Servlet对象?
要想回答上述问题,我们就需要对Servlet的生命周期进行一个学习。
-
生命周期: 对象的生命周期指一个对象从被创建到被销毁的整个过程。
-
Servlet运行在Servlet容器(web服务器)中,其生命周期由容器来管理,分为4个阶段:
加载和实例化
:默认情况下,当Servlet第一次被访问时,由容器创建Servlet对象
默认情况,Servlet会在第一次访问被容器创建,但是如果创建Servlet比较耗时的话,那么第一个访问的人等待的时间就比较长,用户的体验就比较差,那么我们能不能把Servlet的创建放到服务器启动的时候来创建,具体如何来配置?@WebServlet(urlPatterns = "/demo1",loadOnStartup = 1) loadOnstartup的取值有两类情况(1)负整数:第一次访问时创建Servlet对象(2)0或正整数:服务器启动时创建Servlet对象,数字越小优先级越高
初始化
:在Servlet实例化之后,容器将调用Servlet的init()方法初始化这个对象,完成一些如加载配置文件、创建连接等初始化的工作。该方法只调用一次请求处理
:每次请求Servlet时,Servlet容器都会调用Servlet的==service()==方法对请求进行处理服务终止
:当需要释放内存或者容器关闭时,容器就会调用Servlet实例的==destroy()==方法完成资源的释放。在destroy()方法调用之后,容器会释放这个Servlet实例,该实例随后会被Java的垃圾收集器所回收
-
通过案例演示下上述的生命周期
package com.itheima.web;import javax.servlet.*; import javax.servlet.annotation.WebServlet; import java.io.IOException; /** * Servlet生命周期方法 */ @WebServlet(urlPatterns = "/demo2",loadOnStartup = 1) public class ServletDemo2 implements Servlet {/*** 初始化方法* 1.调用时机:默认情况下,Servlet被第一次访问时,调用* * loadOnStartup: 默认为-1,修改为0或者正整数,则会在服务器启动的时候,调用* 2.调用次数: 1次* @param config* @throws ServletException*/public void init(ServletConfig config) throws ServletException {System.out.println("init...");}/*** 提供服务* 1.调用时机:每一次Servlet被访问时,调用* 2.调用次数: 多次* @param req* @param res* @throws ServletException* @throws IOException*/public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {System.out.println("servlet hello world~");}/*** 销毁方法* 1.调用时机:内存释放或者服务器关闭的时候,Servlet对象会被销毁,调用* 2.调用次数: 1次*/public void destroy() {System.out.println("destroy...");}public ServletConfig getServletConfig() {return null;}public String getServletInfo() {return null;}}
注意:如何才能让Servlet中的destroy方法被执行?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-apLu2rRM-1677639150678)(assets/1627239292226.png)]
在Terminal命令行中,先使用mvn tomcat7:run
启动,然后再使用ctrl+c
关闭tomcat
小结
这节中需要掌握的内容是:
- Servlet对象在什么时候被创建的?
默认是第一次访问的时候被创建,可以使用@WebServlet(urlPatterns = “/demo2”,loadOnStartup = 1)的loadOnStartup 修改成在服务器启动的时候创建。
- Servlet生命周期中涉及到的三个方法,这三个方法是什么?什么时候被调用?调用几次?
涉及到三个方法,分别是 init()、service()、destroy()
init方法在Servlet对象被创建的时候执行,只执行1次
service方法在Servlet被访问的时候调用,每访问1次就调用1次
destroy方法在Servlet对象被销毁的时候调用,只执行1次
4.6 体系结构
通过上面的学习,我们知道要想编写一个Servlet就必须要实现Servlet接口,重写接口中的5个方法,虽然已经能完成要求,但是编写起来还是比较麻烦的,因为我们更关注的其实只有service方法,那有没有更简单方式来创建Servlet呢?
大致模拟HttpServlet运行原理
这是MyHttpServle.java文件中 模仿httpservlet运行原理
package com.itheima.web;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;public class MyHttpServlet implements Servlet {public void init(ServletConfig servletConfig) throws ServletException {}public ServletConfig getServletConfig() {return null;}/*** 将来我们要在service方法处理逻辑 逻辑的处理就是获取请求参数的信息* @param servletRequest* @param servletResponse* @throws ServletException* @throws IOException*/public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {//根据请求方式的不同 进行分别的处理//1.获取请求方式//我们得先servletrequest对象强转httpservlet对象HttpServletRequest request=(HttpServletRequest) servletRequest;String method = request.getMethod();//2.判断if ("GET".equals(method)){//get方式逻辑处理doget(servletRequest,servletResponse);} else if ("POST".equals(method)) {//post方式的逻辑处理dopost(servletRequest,servletResponse);}}protected void dopost(ServletRequest servletRequest, ServletResponse servletResponse) {}protected void doget(ServletRequest servletRequest, ServletResponse servletResponse) {}public String getServletInfo() {return null;}public void destroy() {}
}
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {//根据请求方式的不同 进行分别的处理//1.获取请求方式//我们得先servletrequest对象强转httpservlet对象HttpServletRequest request=(HttpServletRequest) servletRequest;String method = request.getMethod();//2.判断if ("GET".equals(method)){//get方式逻辑处理doget(servletRequest,servletResponse);} else if ("POST".equals(method)) {//post方式的逻辑处理dopost(servletRequest,servletResponse);}}
这里就可以看出 并得出结论
service方法:可以处理get/post方式的请求,如果servlet中有service方法,会优先调用service方法
- doGet方法: 处理get方法的请求
- doPost方法: 处理post方式的请求
这就相当于我们继承httpservlet类自己写的servlet
package com.itheima.web;import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;@WebServlet("/demo5")
public class ServletDemo5 extends MyHttpServlet {@Overrideprotected void doget(ServletRequest servletRequest, ServletResponse servletResponse) {System.out.println("get....");}@Overrideprotected void dopost(ServletRequest servletRequest, ServletResponse servletResponse) {System.out.println("post....");}
}
- HttpServlet中为什么要根据请求方式的不同,调用不同的方法?
- 如何调用?
针对问题一,我们需要回顾之前的知识点前端发送GET和POST请求的时候,参数的位置不一致,GET请求参数在请求行中,POST请求参数在请求体中,为了能处理不同的请求方式,我们得在service方法中进行判断
,然后写不同的业务处理,这样能实现
问题二根据不同的请求方式做不同的逻辑调用不同的方法而已
4.7 urlPattern配置
ervlet类编写好后,要想被访问到,就需要配置其访问路径(urlPattern)
- 一个Servlet,可以配置多个urlPattern
* @WebServlet(urlPatterns = {"/demo7","/demo8"})
* package com.itheima.web;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** urlPattern: 一个Servlet可以配置多个访问路径**/
@WebServlet(urlPatterns = {"/demo7","/demo8"})
public class ServletDemo7 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("demo7 get...");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {}
}
然后输入网址
http://localhost:8080/tomcat_demo1_war_exploded/demo7
http://localhost:8080/tomcat_demo1_war_exploded/demo8
访问的是一个Servlet
urlPattern配置规则
/*** UrlPattern:* * 精确匹配*/
@WebServlet(urlPatterns = "/user/select")
public class ServletDemo8 extends MyHttpServlet {@Overrideprotected void doGet(ServletRequest req, ServletResponse res) {System.out.println("demo8 get...");}@Overrideprotected void doPost(ServletRequest req, ServletResponse res) {}
}
- 访问路径
http://localhost:8080/web-demo/user
是否能访问到demo9的doGet方法? - 访问路径
http://localhost:8080/web-demo/user/a/b
是否能访问到demo9的doGet方法? - 访问路径
http://localhost:8080/web-demo/user/select
是否能访问到demo9还是demo8的doGet方法?
答案是: 能、能、demo8,进而我们可以得到的结论是/user/*
中的/*
代表的是零或多个层级访问目录同时精确匹配优先级要高于目录匹配
。
ServletDemo11
@WebServlet(urlPatterns = "/")
public class ServletDemo11 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("demo11 get");}
}
ServletDemo12
@WebServlet(urlPatterns = "/*")
public class ServletDemo12 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("demo12 get");}
}
-
当我们的项目中的Servlet配置了 “/”,会覆盖掉tomcat中的DefaultServlet,当其他的url-pattern都匹配不上时都会走这个Servlet
-
当我们的项目中配置了"/*",意味着匹配任意访问路径
-
DefaultServlet是用来处理静态资源,如果配置了"/"会把默认的覆盖掉,就会引发请求静态资源的时候没有走默认的而是走了自定义的Servlet类,最终导致静态资源不能被访问
5.resquest和response概述
之前我们写好一个Servlet 放在tomcat里面 通过浏览器请求到servlet 浏览器发送请求 也就是http请求数据 请求数据就是一些字符串 而这些字符串将来被tomcat解析 解析完成之后会把请求数据放入一个对象里面来保存 而这个对象就是request对象 所以request对象保持了一大堆请求数据 service方法有request对象获取这些请求数据 同理前端发送数据给后端也就是请求逻辑处理完了 后端处理后给用户响应的数据也就是tomcat给用户客户端浏览器发送响应字符串给到浏览器 浏览器解析响应的数据就展现出来效果了 字符串是由tomcat发送的 service方法里面通过response对象来控制那些响应的数据发送给前台
request:获取请求数据
浏览器会发送HTTP请求到后台服务器[Tomcat]
HTTP的请求中会包含很多请求数据[请求行+请求头+请求体]
后台服务器[Tomcat]会对HTTP请求中的数据进行解析并把解析结果存入到一个对象中
所存入的对象即为request对象,所以我们可以从request对象中获取请求的相关参数
获取到数据后就可以继续后续的业务,比如获取用户名和密码就可以实现登录操作的相关业务response:设置响应数据
业务处理完后,后台就需要给前端返回业务处理的结果即响应数据
把响应数据封装到response对象中
后台服务器[Tomcat]会解析response对象,按照[响应行+响应头+响应体]格式拼接结果
浏览器最终解析结果,把内容展示在浏览器给用户浏览
5.1 Request对象
5.1.1 Request继承体系
同时我们可以知道service request response对象 由tomcat对象创建
这个时候,我们就需要用到Request继承体系中的RequestFacade :
该类实现了HttpServletRequest接口,也间接实现了ServletRequest接口。
Servlet类中的service方法、doGet方法或者是doPost方法最终都是由Web服务器[Tomcat]来调用的,所以Tomcat提供了方法参数接口的具体实现类,并完成了对象的创建
要想了解RequestFacade中都提供了哪些方法,我们可以直接查看JavaEE的API文档中关于ServletRequest和HttpServletRequest的接口文档,因为RequestFacade实现了其接口就需要重写接口中的方法
请求消息的数据被tomcat解析 解析完成后封装成request对象放入service方法位置 谁来解析参数 就得谁来对方法做实现 同时提供实现类 因为这个类里面提供get方法获取请求数据 同时由tomcat提供request对象的实现类 service方法由tomcat调用同时service方法有两个参数 所以request response对象都由tomcat创建
5.2 Request获取请求数据
上文也有只是再拿出来罢了
HTTP请求数据总共分为三部分内容,分别是请求行、请求头、请求体
5.2.1 获取请求行数据
请求行包含三块内容,分别是请求方式
、请求资源路径
、HTTP协议及版本
package com.itheima.web;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;@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():获取虚拟目录(项目访问路径):String contextPath = req.getContextPath();System.out.println(contextPath);
// /tomcat_demo1_war_explodedStringBuffer requestURL = req.getRequestURL();System.out.println(requestURL.toString());// http://localhost:8080/tomcat_demo1_war_exploded/req1String requestURI = req.getRequestURI();System.out.println(requestURI.toString());// /tomcat_demo1_war_exploded/req1// 获取请求参数(GET方式): String queryString = req.getQueryString();System.out.println(queryString);
// username=123&password=1111//获取请求头: user-agent: 浏览器的版本信息String agent = req.getHeader("user-agent");}@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);}
}
5.2.2 获取请求头数据
获取请求头: user-agent: 浏览器的版本信息
String agent = req.getHeader("user-agent")
5.2.3 获取请求体数据
浏览器在发送GET请求的时候是没有请求体的,所以需要把请求方式变更为POST,请求体中的数据格式如下:
对于请求体中的数据,Request对象提供了如下两种方式来获取其中的数据,分别是:
- 获取字节输入流,如果前端发送的是字节数据,比如传递的是文件数据,则使用该方法
* ServletInputStream getInputStream()
该方法可以获取字节
获取字符输入流,如果前端发送的是纯文本数据,则使用该方法
BufferedReader getReader()
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取post请求体 请求参数//1.获取字符输入流BufferedReader br = req.getReader();//2.读取数据String line = br.readLine();System.out.println(line);
}
因为用户名和密码都是字符串所以采用字符输入流
前言get请求方式 请求参数在请求行位置
post请求参数在请求体中获取参数
5.2.3 获取请求参数的通用方式
对于请求参数的获取,常用的有以下两种:
GET方式:
POST方式:
有了上述的知识储备,我们来实现一个案例需求:
(1)发送一个GET请求并携带用户名,后台接收后打印到控制台
(2)发送一个POST请求并携带用户名,后台接收后打印到控制台
此处大家需要注意的是GET请求和POST请求接收参数的方式不一样,具体实现的代码如下:
1 String getQueryString()
1 BufferedReader getReader();
Request获取请求参数的通用方式
如果是get方式 底层调用getQueryString
如果是post方式 底层调用getReader
这个param就读取出来了
request就是这样做的同时request对象做的更可以 我们获取请求参数就是这么一长串字符串我们需要自己去解析 request对象不仅获取了请求参数并帮我们解析了
解析方式 以&切割 切割成三个字符串 再用=切割
然后保存用map集合保存
前言
import java.util.HashMap;class Main {public static void main(String[] args) {// 创建一个 HashMapHashMap<Integer, String> sites = new HashMap<>();// 往 HashMap 添加一些元素sites.put(1, "Google");sites.put(2, "Runoob");sites.put(3, "Taobao");System.out.println("sites HashMap: " + sites);// 返回所有 key 组成的 set 集合视图System.out.println("Keys: " + sites.keySet());}
}
sites HashMap: {1=Google, 2=Runoob, 3=Taobao}
Keys: [1, 2, 3]
ReuquestDemo2.java
@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<String, String[]> parameterMap = req.getParameterMap();for (String key : parameterMap.keySet()) {//System.out.print(key+":");//获取值String[] values = parameterMap.get(key);for (String value : values) {System.out.print(value+" ");}System.out.println();}//2.根据key获取参数值 数组String[] hobbies = req.getParameterValues("hobby");for (String hobby : hobbies) {System.out.println(hobby);//1,2}//3.根据key获取单个参数值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 {//POST请求参数this.doGet(req, resp);}
}
req.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<form action="/tomcat_demo1_war_exploded/req4" method="get"><input type="text" name="username"><br><input type="password" name="password"><br><input type="checkbox" name="hobby" value="1"> 游泳<input type="checkbox" name="hobby" value="2"> 爬山 <br><input type="submit">
</form>
</body>
</html>
这里改为post也行
5.2.4 请求参数中文乱码问题
1.post请求方式 中文乱码问题解决
/*** 中文乱码解决方案*/
@WebServlet(name = "RequestDemo4", value = "/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}
因为post请求方式走的是 getReader那个流 而get方式不是的
所以setCharacterenconding解决不了get中文乱码问题
2.get请求方式 中文乱码问题解决getQueryString
首先我们需要先分析下GET请求出现乱码的原因:
(1)浏览器通过HTTP协议发送请求和数据给后台服务器(Tomcat)(2)浏览器在发送HTTP的过程中会对中文数据进行URL==编码==(3)在进行URL编码的时候会采用页面`<meta>`标签指定的UTF-8的方式进行编码,`张三`编码后的结果为`%E5%BC%A0%E4%B8%89`(4)后台服务器(Tomcat)接收到`%E5%BC%A0%E4%B8%89`后会默认按照`ISO-8859-1`进行URL==解码==(5)由于前后编码与解码采用的格式不一样,就会导致后台获取到的数据为乱码。/*** 中文乱码问题解决方案*/
@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);}
}
因为tomcat源码默认URL解码ISO8859_1不可以修改所以我们将8859的转换为字节数据 再有字节数据转换为utf-8数据 因为他们对应的二进制数据是一样的
5.2.5 请求转发
1.简介
请求转发(forward):一种在服务器内部的资源跳转方式
比如说服务器有资源A和资源B 将来浏览器请求资源A的时候 资源A将请求处理了一部分然后跳转到资源B去让资源B接着处理 处理完成之后给浏览器做出了响应 资源A跳转到资源B我们叫forward 所以转发是服务器内部资源的跳转方式
(1)浏览器发送请求给服务器,服务器中对应的资源A接收到请求
(2)资源A处理完请求后将请求发给资源B
(3)资源B处理完后将结果响应给浏览器
(4)请求从资源A到资源B的过程就叫请求转发
2.请求转发的实现方式
req.getRequestDispatcher("资源B路径").forward(req,resp);
RequesetDemo5.java
@WebServlet(name = "RequestDemo5", value = "/req5")
public class RequestDemo5 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("demo5...");//请求转发// getRequestDispatcher("/req6") 查找路径 //forward(request, response);跳转过去request.getRequestDispatcher("/req6").forward(request, response);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}
}
RequesetDemo6.java
@WebServlet(name = "RequestDemo6", value = "/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 {}
}
demo5…
demo6…
也就是资源A处理了一部分数据 然后A把剩下的那部分数据给资源B处理 也就是资源A和资源B在转发过程中是可以共享了数据
转发过程中多个数据共享是利用了request对象 所以我们可以将数据存入request对象
3. 请求转发资源间共享数据:使用Request对象
此处主要解决的问题是把请求从/req5
转发到/req6
的时候,如何传递数据给/req6
。
需要使用request对象提供的三个方法:
- 存储数据到request域[范围,数据是存储在request对象]中
void setAttribute(String name,Object o);
- 根据key获取值
Object getAttribute(String name);
- 根据key删除该键值对
void removeAttribute(String name);
request对象是一个域对象 所以可以存储数据
接着上个需求来:
RequestDemo5.java
@WebServlet(name = "RequestDemo5", value = "/req5")
public class RequestDemo5 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("demo5...");//存储数据 request.setAttribute("msg", "hello");//请求转发// getRequestDispatcher("/req6") 查找路径//forward(request, response);跳转过去request.getRequestDispatcher("/req6").forward(request, response);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}
}
RequestDemo6.java
@WebServlet(name = "RequestDemo6", value = "/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 {}
}
demo5…
demo6…
hello
请求转发特点:
1.浏览器地址栏路径不发生变化
当前访问的是req5 真正执行的是req5和6这两个资源 但是路径不会变化 也就是用户并不知道服务器怎么运作的
2.只能转发到当前服务器的内部资源
也就是资源A和资源B在同一个服务器中 不能转发到其他服务器去了
3.一次请求,可以在转发的资源间使用request共享数据
也就是一次请求一次响应 所以可以在request对象共享数据
5.2 Responset对象
request获取请求数据 response对象获取响应数据的
- Request:使用request对象来获取请求数据
- Response:使用response对象来设置响应数据
1. Response设置响应数据功能介绍
HTTP响应数据总共分为三部分内容,分别是==响应行、响应头、响应体
响应行
响应头 setContextType方法更改格式
响应体
第一行为响应行
后续几行都是响应头键值对
这是响应体
对于响应体,是通过字符、字节输出流的方式往浏览器写,
2.response完成重定向
重定向(redirect):一种资源跳转方式
现在我们有两个资源 资源A和B 将来浏览器访问资源A 资源A发现浏览器的请求我处理不了 但资源A知道资源B可以处理 资源A就会告诉浏览器你的请求我处理不了 你去找别人处理把 然后还告诉别人的位置 那这时候浏览器就收到资源A的响应 他会拿着资源A告诉他的位置自动请求资源B 那么这个过程就是重定向
关键点在于资源A得告诉浏览器你请求的资源我处理不了你不能说汉字啊这时候得资源A告诉浏览器一个特殊的编号302 浏览器知道302之后就知道你让我去找别人 其实就是我们所说的响应状态码
(1)浏览器发送请求给服务器,服务器中对应的资源A接收到请求
(2)资源A现在无法处理该请求,就会给浏览器响应一个302的状态码+location的一个访问资源B的路径
(3)浏览器接收到响应状态码为302就会重新发送请求到location对应的访问地址去访问资源B
(4)资源B接收到请求后进行处理并最终给浏览器响应结果,这整个过程就叫重定向
3.重定向的特点
-
浏览器地址栏路径发送变化
这个地方地址栏发生了变化
当进行重定向访问的时候,由于是由浏览器发送的两次请求(都是从地址栏发出的),所以地址栏会发生变化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KFjvFdtm-1678082782612)(assets/1628861893130.png)]
-
可以重定向到任何位置的资源(服务内容、外部均可)
因为第一次响应结果中包含了浏览器下次要跳转的路径,所以这个路径是可以任意位置资源。
-
两次请求,不能在多个资源使用request共享数据
因为浏览器发送了两次请求,是两个不同的request对象,就无法通过request对象进行共享数据 两个request对象作用域不同 所以无法共享数据
ResponseDemo1.java
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;@WebServlet(name = "ResponseDemo1", value = "/resp1")
public class ResponseDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("resp1...");
// //重定向
// //1.设置响应状态码302
// response.setStatus(302);
// //2.设置响应头location
// response.setHeader("Location", "/tomcat_demo1_war_exploded/resp2");//简化方式完成重定向response.sendRedirect("/tomcat_demo1_war_exploded/resp2");}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}
}
ResponseDemo2.java
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;@WebServlet(name = "ResponseDemo2", value = "/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 {}
}
总结:
4.路径问题
问题1:转发的时候路径上没有加/request-demo
而重定向加了,那么到底什么时候需要加,什么时候不需要加呢?
- 浏览器使用:需要加虚拟目录(项目访问路径)
- 服务端使用:不需要加虚拟目录
对于转发来说,因为是在服务端进行的,所以不需要加虚拟目录
对于重定向来说,路径最终是由浏览器来发送请求,就需要添加虚拟目录。
练习 中 a herf 和from和sendredirect 是从浏览器发出请求需要虚拟目录 而getRequestDispatcher转发要加虚拟目录
5.Response响应字符数据
要想将字符数据写回到浏览器,我们需要两个步骤:
-
通过Response对象获取字符输出流: PrintWriter writer = resp.getWriter();
向响应体内写入内容
-
通过字符输出流写数据: writer.write(“aaa”);
接下来,我们实现通过些案例把响应字符数据给实际应用下:
/*** 响应字符数据:设置字符数据的响应体*/
@WebServlet(name = "ResponseDemo3", value = "/resp3")
public class ResponseDemo3 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.setContentType("text/html;charset=utf-8");PrintWriter writer = response.getWriter();//response.setHeader("content-type", "text/html");//这个方法其实是对contentType书写简化格式response.setContentType不仅在设置这个头setHeader同时也在设置流的编码writer.write("你好");writer.write("<h1>aaa<h1>");//细节:writer流不需要关闭 因为流是由response对象获取出来的 将来这一次请求响应完毕之后response对象会被销毁 销毁的时候流自动被关掉 我们不需要调用他创建的流对象的close关闭方法}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}
}