response对象
三个常用方法
请求转发和重定向的区别
重定向是response对象提供的
请求转发是request对象提供的
重定向会导致数据丢失
请求转发不会导致数据丢失
区别表格
JSESSIONID
当客户端第一次请求服务端的时候,这个时候服务器会发现这个客户端之前没有请求过,就会产生一个name="JSESSIONID"的Cookie,返回给客户端.
四大范围对象
四种对象
共有的方法
理解记忆
其实四种对象的方法在怎么变化,也都是围绕着增删改查来进行的,
增(setAttribute(String name)
),
删(removeAttribute(String name)
),
改(setAttribute(String name)
),
查(getAttribute(String name)
)
要知道,凡是涉及到对数据的操作,无非就是增删改查.
作用范围
page Context
作用域是当前页面,无论是重定向还是请求转发,只要是换到了另外一个页面,这个就失效了
request
session
无论你怎么跳转,无论你是重定向还是请求转发,只要你的浏览器不关闭,这个会话就是一直有效.
application
只要服务器不关,这个对象就是一直有效.
MVC
MVC模型的理解
控制器的作用
MVC各层对应的代码
Model层
Model层相当于Dao层,使用JavaBean实现,直接与数据库进行打交道
Controller层
虽然控制层,或者说分发层可以使用JSP实现,但是一般建议使用Servlet实现控制器.
web.xml的理解
Servlet的映射
Servlet映射的理解
构建路径(Build Path)
构建路径相当于所有代码文件的根路径,一般用/表示,一个项目可以有多个构建路径.
根目录的区别
jsp文件的根目录
jsp文件的根目录就是这个站点的地址localhost:8080
jsp的根目录(same)
web.xml的根目录
web.xml的根目录是这个项目的根目录,也就在站点后面还需要加上这个项目的文件夹.
Servlet
Servlet的生命周期
因为加载和卸载是Servlet容器自动完成的,所以有人说Servlet的生命周期是有三个部分.
init()和destory()都是只执行一次.
service()可以执行多次.
三个阶段的执行情况
Servlet自动执行
如何让Servlet在启动服务器的时候就是自动执行这个init()方法?
只需要在这个web.xml对应Servlet的里面加上下面的语句:
难道init()方法不是自动执行的码?
init()方法执行的原则,就是这个Servlet即使加载好也不执行,只有在使用的过程中,你用到了这个Servlet这个init()方法才会执行,如果你在过程中没有使用到这个Servlet,那么即使这个Servlet已经加载了,也是不init()的.
注解方式设置自动启动
load-on-startup中的序号的含义
多个Servlet的启动顺序
ServletContext中的常见方法
上下文路径就是相对路径,真正路径就是绝对路径.
空实现接口的好处
传统方法
定义接口
package org.lanqiao.servlet;
public interface Student {
void sleep();
void study();
}
编写实现类
package org.lanqiao.servlet;
public class StudentImpl implements Student {
@Override
public void sleep() {
System.out.println("业务代码。。");
}
@Override
public void study() {
}
}
总结
产生的问题:如果我只要实现接口中的某个或某几个方法,那么实现需要的方法,于此同时,必须实现其不需要的方法他的方法(空实现)。
给接口设计空实现类
定义接口
package org.lanqiao.servlet;
public interface Student {
void sleep();
void study();
}
给接口设计空实现类
package org.lanqiao.servlet;
public class StudentImpl implements Student {
@Override
public void sleep() {
}
@Override
public void study() {
}
}
继承这个空实现类
package org.lanqiao.servlet;
public class Text extends StudentImpl {
@Override
public void study() {
System.out.println("只要重写需要用到的方法。。。。。");
}
}
总结
完美的解决了,实现接口中的某个方法同时必须实现其不需要的方法他的方法(空实现)的问题。
简而言之,就是只需要重写需要实现的方法,用不到的不需要重写,而如果实现接口,即使用不着,也必须实现.
三层架构
三层架构和MVC的区别
三层架构的概念
三层架构的比喻
表示层
数据访问层
业务逻辑层
三层之间请求的流程
三层架构和MVC的对比
表示层的映射
映射
表示层的代码
代码位置
数据访问层
代码位置
强调
业务逻辑层
业务逻辑层和数据访问层的关系
业务逻辑层的功能
代码位置
三层架构例子
项目结构
项目代码
[th_threeLayerModel - 快捷方式.lnk](code\th_threeLayerModel - 快捷方式.lnk)
代码逻辑图
代码逻辑的理解
详细版
依赖关系
如何把Servlet的中信息传到前端页面中?
放到request域中,但是只能使用请求转发的方式跳转,不能使用重定向,否则数据会丢失.
为什么使用请求转发的方式数据不会丢失?
为什么使用request域传递数据而不是session?
session也能达到同样的效果,只不过他的作用范围更大,而秉持的节省内存的原则,在满足条件的情况下,作用域范围越小越好.
如何接受Servlet中传递过来的数据
web.xml中设置页面的执行顺序
超链接中如何传递参数
删除的逻辑
input标签的readonly属性
为什么在form标点中展示信息?
不能改的加上readonly属性,能改的就是可以改,因此form表单同时起到了展示信息和修改信息的作用.
为什么重定向request域会失效,而请求转发不会?
jQuery的隔行选中功能
下面是所有奇数行的tr标签:
form表单的表单校验
check()一句话总结:就是设置层层障碍,有错误,就是返回FALSE,然后终止表单提交.
写检查逻辑的一个小技巧
先写正确的逻辑,然后取反
HttpServletRequest 对象
请求的方式
要访问服务器首先需要由客户端主动发出请求,在实际的操作中,我们可以通过多种方式向服务器发起
请求。根据不同的场景需求,使用不同的请求方式可以达到不同的效果。
地址栏输入
在浏览器地址栏直接输入要访问的地址即可,此种方式可以看做是访问服务器的起始操作。
http://ip:port/path
超链接
使用超链接也可以向服务器发出请求
<a href="http://www.baidu.com">百度</a>
Form表单
当需要向服务器发送请求,并且传输一些用户输入的数据时,我们优先选择form表单的方式发起请求。
ajax
通过ajax发起的请求,属于异步请求,能实现局部刷新的效果,是一种比较常用的请求方式。
通过jQuery中的ajax(),get(),post(),getJSON()等方法都能发送请求
请求转发
服务端行为
通过服务器内部将请求进行一次转发,可以请求到其他资源
重定向
客户端行为
服务器通过给定一个新资源的地址,响应回客户端后,客户端自动再次发送一个请求到新资源的地址
处。
介绍
HttpServletRequest 对象:主要作用是用来接收客户端发送过来的请求信息,
例如:请求的参数,发送
的头信息等都属于客户端发来的信息,
service()方法中形参接收的是 HttpServletRequest 接口的实例化对象,表示该对象主要应用在 HTTP 协议上,该对象是由 Tomcat 封装好传递过来。
HttpServletRequest 是 ServletRequest 的子接口, ServletRequest 只有一个子接口,就是HttpServletRequest。既然只有一个子接口为什么不将两个合并为一个?
从长远上讲:现在主要用的协议是 HTTP 协议,但以后可能出现更多新的协议。若以后想要支持这种新协议,只需要直接继承 ServletRequest 接口就行了。
在 HttpServletRequest 接口中,定义的方法很多,但都是围绕接收客户端参数的。但是怎么拿到该对象呢?不需要,直接在 Service 方法中由容器传入过来,而我们需要做的就是取出对象中的数据,进行分析、处理。
常用形式
常用方法
常用方法 | 描述 |
---|---|
getRequestURL() |
获取客户端发出的请求时的完整 URL |
getRequestURI() |
获取请求行中的资源名称部分(项目名称开始) |
getQueryString() |
获取请求行中的参数部分 |
getMethod() |
获取客户端请求方式 |
getProtocol() |
获取 HTTP 版本号 |
getContextPath() |
获取 webapp 名字 |
获取请求头
方法 | 描述 |
---|---|
getHeader(String) |
获取单个请求头内容 |
Enumeration<String> getHeaderNames() |
获取所有的请求头名称集合 |
获取客户端请求参数(客户端提交的数据)
方法 | 描述 |
---|---|
getParameter(name) |
获取指定名称的参数 |
getParameterValues(String name) |
获取指定名称参数的所有值 |
getParameterNames() |
获取一个包含请求消息中的所有参数名的 Enumeration 对象 |
getParameterMap() |
返回一个保存了请求消息中的所有参数名称和值的 Map 对象 |
请求乱码解决
由于现在的 request 属于接收客户端的参数,所以必然有其默认的语言编码,主要是由于在解析过程中
默认使用的编码方式为 ISO-8859-1(此编码不支持中文),所以解析时一定会出现乱码。要想解决这种乱
码问题,需要设置 request 中的编码方式,告诉服务器以何种方式来解析数据。或者在接收到乱码数据
以后,再通过相应的编码格式还原。
方式一
这种方式只针对 POST 有效(必须在接收所有的数据之前设定)
req.setCharacterEncoding("UTF-8");
方式二
借助了String 对象的方法,该种方式对任何请求有效,是通用的。
new String(request.getParameter(name).getBytes("ISO-8859-1"),"UTF-8");
Tomcat8起,以后的GET方式请求是不会出现乱码的。
请求转发
请求转发,是一种服务器的行为,当客户端请求到达后,服务器进行转发,此时会将请求对象进行保
存,地址栏中的 URL 地址不会改变,得到响应后,服务器端再将响应发送给客户端, 从始至终只有一
个请求发出。实现方式如下,达到多个资源协同响应的效果
request.getRequestDispatcher("转发的路径").forward(req,resp);
例子-请求转发
请求转发的核心代码
//demo.java
package com.zhuo.demo;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* Servlet implementation class Demo
*/
@WebServlet("/Demo")
public class Demo extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#service(HttpServletRequest request, HttpServletResponse response)
*/
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("<h2>This is Demo.java</h2>");
//请求转发
request.getRequestDispatcher("/Demo2").forward(request,response);
}
}
//demo2.java
package com.zhuo.demo;
import java.io.IOException;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet("/Demo2")
public class Demo2 extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("<h2>This is Demo2.java</h2>");
}
}
输出:
request作为域对象
通过该对象可以在一个请求中传递数据,作用范围: 在一次请求中有效,即服务器跳转有效。
request.setAttribute(String name, Object o); // 设置域对象内容request.getAttribute(String name); // 获取域对象内容request.removeAttribute(String name); //删除域对象内容
request 域对象中的数据在一次请求中有效,经过请求转发, request 域中的数据依然存在,则在请求转发的过程中可以通过 request 来传输/共享数据。
例子-request域的范围
request对象的传递过程
demo.java
package com.zhuo.demo;import java.io.IOException;import jakarta.servlet.ServletException;import jakarta.servlet.annotation.WebServlet;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;/** * Servlet implementation class Demo */@WebServlet("/Demo")public class Demo extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#service(HttpServletRequest request, HttpServletResponse response) */ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write("<h2>This is Demo.java</h2>"); request.setAttribute("Demo","I'm request object from demo"); //请求转发 request.getRequestDispatcher("/Demo2").forward(request,response); }}
demo2.java
package com.zhuo.demo;import java.io.IOException;import jakarta.servlet.ServletException;import jakarta.servlet.annotation.WebServlet;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;@WebServlet("/Demo2")public class Demo2 extends HttpServlet { private static final long serialVersionUID = 1L; protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write("<h2>This is Demo2.java</h2>"); Object demoAttr = request.getAttribute("Demo"); response.getWriter().write(demoAttr.toString()); }}
输出:
HttpServletResponse 对象
介绍
Web 服务器收到客户端的 http 请求,会针对每一次请求,分别创建一个用于代表请求的 request
对象和代表响应的 response 对象。
request 和 response 对象代表请求和响应:获取客户端数据,需要通过 request 对象; 向客户
端输出数据,需要通过 response 对象。
HttpServletResponse 的主要功能用于服务器对客户端的请求进行响应,将 Web 服务器处理后的
结果返回给客户端。 service()方法中形参接收的是 HttpServletResponse 接口的实例化对象,这个对
象中封装了向客户端发送数据、发送响应头,发送响应状态码的方法。
常用方法
常用方法 | 描述 |
---|---|
addHeader(String name, String value) |
添加指定的键值到响应头信息中 |
containsHeader(String name) |
判断响应的头部是否被设置 |
encodeURL(String url) |
编码指定的 URL |
sendError(int sc) |
使用指定状态码发送一个错误到客户端 |
setHeader(String name, String value) |
设置指定响应头的值 |
setStatus(int sc) |
给当前响应设置状态 |
setContentType(String ContentType) |
设置响应的 MIME 类型 |
getWriter() |
获取输出字符流 |
getOutputStream() |
获取输出的字节流 |
刷新和页面自动跳转
所有头信息都是随着请求和回应自动发送到服务器端(客户端),在response中一个比较常用的头信息就是刷新的指令,可以完成定时刷新的功能。
response.setHeader("refresh","2");
对于刷新的头信息,除了定时的功能外,还具备了定时跳转的功能,可以让一个页面定时跳转到一个指定的页面。 (已经注册成功,两秒后跳转到登陆页面)
response.setHeader("refresh","3;URL=ok.html");
但是这种跳转不是万能的,有时候根本就无法进行跳转操作,返回后刷新不会跳转;对于这种定时跳转的头信息,也可以采用 HTML 的方式进行设置, HTML 本身也可以设置头信息。 (客户端跳转)
<meta http-equiv="refresh" content="3;http://www.baidu.com" />
例子-定时刷新
设置每2秒钟刷新一次
package com.zhuo.demo;import java.io.IOException;import java.io.PrintWriter;import java.text.SimpleDateFormat;import java.util.Date;import jakarta.servlet.ServletException;import jakarta.servlet.annotation.WebServlet;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;/** * Servlet implementation class Demo */@WebServlet("/Demo")public class Demo extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#service(HttpServletRequest request, HttpServletResponse response) */ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //设置自动刷新 response.setHeader("refresh","2"); //获取系统当前时间 Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat(); sdf.applyPattern("yyyy-MM-dd HH:mm:ss"); String time = sdf.format(date); //打印当前时间到页面 PrintWriter writer = response.getWriter(); writer.write(time); }}
输出:
例子-定时跳转
定时跳转的核心代码
package com.zhuo.demo;import java.io.IOException;import java.io.PrintWriter;import java.text.SimpleDateFormat;import java.util.Date;import jakarta.servlet.ServletException;import jakarta.servlet.annotation.WebServlet;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;/** * Servlet implementation class Demo */@WebServlet("/Demo")public class Demo extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#service(HttpServletRequest request, HttpServletResponse response) */ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write("<h2>This is Demo.java</h2>"); //设置定时跳转 response.setHeader("refresh","3;url=index.html"); }}
输出:
例子-客户端跳转
核心代码
package com.zhuo.demo;import java.io.IOException;import java.io.PrintWriter;import java.text.SimpleDateFormat;import java.util.Date;import jakarta.servlet.ServletException;import jakarta.servlet.annotation.WebServlet;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;/** * Servlet implementation class Demo */@WebServlet("/Demo")public class Demo extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#service(HttpServletRequest request, HttpServletResponse response) */ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write("<h2>This is Demo.java</h2>"); response.getWriter().write("<meta http-equiv=\"refresh\" content=\"3;http://www.baidu.com\" /> "); }}
输出:
数据响应
接收到客户端请求后,可以通过 HttpServletResponse
对象直接进行响应,响应时需要获取输出流,有两种形式
字符流和字节流的区别
getWriter()
获取字符流(只能响应回字符);
getOutputStream()
获取字节流(能响应一切数据)。
响应回的数据到客户端被浏览器解析。
注意:两者不能同时使用。
//字符流写入PrintWriter out = resp.getWriter();out.write("<h1>Hello World</h1>");//字节流写入ServletOutputStream out = resp.getOutputStream();out.write("<h1>Hello World</h1>").getBytes());
乱码解决
我们会发现在上述的响应中,如果我们响应的内容中含有中文,则有可能出现乱码。这是因为服务器响应的数据也会经过网络传输,服务器端有一种编码方式,在客户端也存在一种编码方式,当两端使用的编码方式不同时则出现乱码。
getWriter()的字符乱码
对于 getWriter() 获取到的字符流,响应中文必定出乱码,由于服务器端在进行编码时默认会使用ISO-8859-1 格式的编码,该编码方式并不支持中文。所以要解决该种乱码只能在服务器端告知服务器使 用 一 种 能 够 支 持 中 文 的 编 码 格 式 , 比 如 我 们 通 常 用 的 “UTF-8”
resp.setCharacterEncoding("UTF-8");
,此时还只完成了一半的工作,要保证数据正确显示,还需要指定客户端的解码方式
resp.setHeader("content-type", "text/html;charset=UTF-8");
,和服务器一致。两端指定编码后,乱码就解决了。
一句话:保证发送端和接收端的编码一致
response.setCharacterEncoding("UTF-8");response.setHeader("content-type", "text/html; charset=UTF-8");PrintWriter out = response.getWriter();out.write("<h1>你好啊!!!! </h1>");
设置响应编码的顺序和位置
以上两端编码的指定也可以使用一句替代,同时指定服务器和客户端:
response.setContentType("text/html;charset=utf-8");
getOutputStream()字节乱码
对于 getOutputStream() 方式获取到的字节流,响应中文时,由于本身就是传输的字节,所以此时可能出现乱码,也可能正确显示,这就看人品了_。当服务器端给的字节恰好和客户端使用的编码方式一致时则文本正确显示,否则出现乱码。无论如何我们都应该准确掌握服务器和客户端使用的是那种编码格式,以确保数据正确显示。指定客户端和服务器使用的编码方式一致即可。resp.setHeader("content-type", "text/html;charset=UTF-8");
。
resp.setHeader("content-type", "text/html; charset=UTF-8");ServletOutputStream out = resp.getOutputStream();out.write("<h1>你好啊!!! </h1>".getBytes("UTF-8"));
同样也可以使用一句替代:
resp.setContentType("text/html;charset=utf-8");
总结: 要想解决响应的乱码,只需要保证使用支持中文的编码格式。并且保证服务器端 和客户端使用相同的编码方式即可。
响应图片
在学习 HTML 的时候我们知道使用 <img src="img.jpg">
的方式可以显示图片。但有的时候我们并不知道(或不能确定)图片的路径,需要通过请求服务器资源动态地响应图片给客户端。这种方式和文件拷贝的理念是一致的,客户端请求服务器的资源,在服务端获取到真实的图片资源,通过输入流读取到内存,然后通过输出流写出到客户端即可。
值得注意的是,在客户端解析资源时默认是以文本(text/html)的形式,当响应图片时需要指定响应头信息,告知客户端响应内容为图片形式,使用一种叫做 MIME 类型的东西来指定。 MIME 类型见Tomcat 的 web.xml 文件。
<mime-mapping> <extension>jpeg</extension> <mime-type>image/jpeg</mime-type></mime-mapping><mime-mapping> <extension>jpg</extension> <mime-type>image/jpeg</mime-type></mime-mapping><mime-mapping> <extension>jpgm</extension> <mime-type>video/jpm</mime-type></mime-mapping><mime-mapping> <extension>jpgv</extension> <mime-type>video/jpeg</mime-type></mime-mapping><mime-mapping> <extension>jpm</extension> <mime-type>video/jpm</mime-type></mime-mapping>
定义某一个扩展名和某一个 MIME Type 做对应,包含两个子元素:
<extension></extension>
扩展名的名称
<mime-type></mime-type>
MIME 格式
例子
图片文件的路径
获取文件的绝对路径
设置上下文内容类型
response.setContentType(MIME)
的作用是使客户端浏览器,区分不同种类的数据,并根据不同的MIME调用浏览器内不同的程序嵌入模块来处理相应的数据。
例如web浏览器就是通过MIME类型来判断文件是GIF图片。通过MIME类型来处理json字符串。
Tomcat的安装目录\conf\web.xml 中就定义了大量MIME类型 ,可以参考。
response.setContentType("text/html; charset=utf-8");
html
response.setContentType("text/plain; charset=utf-8");
文本
response.setContentType("text/javascript; charset=utf-8");
json数据
response.setContentType("application/xml; charset=utf-8");
xml数据
package com.zhuo.cookie;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.PrintWriter;import jakarta.servlet.ServletException;import jakarta.servlet.ServletOutputStream;import jakarta.servlet.annotation.WebServlet;import jakarta.servlet.http.Cookie;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;/** * Servlet implementation class CookieTest */@WebServlet("/ImageShow")public class ImageShow extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#service(HttpServletRequest request, HttpServletResponse response) */ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //创建文件路径 String name = "dog.jpg"; String filePath = request.getServletContext().getRealPath("/WEB-INF/image/"+name); //创建文件对象 File file = new File(filePath); //如果文件存在且文件对象是文件 if(file.exists()&&file.isFile()) { //截取图片的文件的后缀 String pic = name.split("\\.")[0]; //如果文件的后缀为空或null,则返回 if(pic==null || "".equals(pic)) { return; } //如果文件的后缀为jpg,则设置内容类型 if("jpg".equals(pic)) { response.setContentType("image/jpg;chaset=utf-8"); } //建立文件读入流 FileInputStream in = new FileInputStream(file); //建立响应输出流 ServletOutputStream out = response.getOutputStream(); //创建一个字节数组 byte[] car = new byte[1024]; int len =0 ; //将文件以字节数组的方式读进来,同时写出 while((len=in.read(car))!=-1) { out.write(car,0,len); } //关闭文件读入流 in.close(); //关闭文件输出流 out.close(); }else { PrintWriter out = response.getWriter(); out.write("<h1>文件路径不正确!</h1>"); } }}
输出:
重定向跳转
重定向是一种服务器指导客户端的行为。客户端发出第一个请求,被服务器接收,经过处理服务器
进行响应,与此同时,服务器给客户端一个地址(下次请求的地址 resp.sendRedirect("url");
),当客户
端接收到响应后, 立刻、马上、自动根据服务器给的地址进行第二个请求的发送,服务器接收请求并作
出响应,重定向完成。从描述中可以看出重定向当中有两个请求存在,并且属于客户端行为。实现方式
如下:
resp.sendRedirect("index.html")
通过观察浏览器我们发现第一次请求获得的响应码为 302,并且含有一个 location 头信息。并且
地址栏最终看到的地址是和第一次请求地址不同的,地址栏已经发生了变化。
例子
核心代码
package com.zhuo.demo;import java.io.IOException;import jakarta.servlet.ServletException;import jakarta.servlet.annotation.WebServlet;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;/** * Servlet implementation class Demo */@WebServlet("/Demo")public class Demo extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#service(HttpServletRequest request, HttpServletResponse response) */ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.sendRedirect("index.html"); }}
输出:
请求转发和重定向区别
请求转发 (req.getRequestDispatcher().forward()) | 重定向(response.sendRedirect()) |
---|---|
一次请求,数据在 request 域中共享 | 两次请求, request 域中数据不共享 |
服务器端行为 | 客户端行为 |
地址栏不发生变化 | 地址栏发生变化 |
绝对地址定位到站点后 | 绝对地址可写到 http:// |
Cookie
Cookie 是浏览器提供的一种技术,通过服务器的程序能将一些只须保存在客户端,或者在客户端进行处理的数据,放在本地的计算机上,无论何时用户链接到服务器, Web 站点都可以访问 Cookie 信息不需要通过网络传输,因而提高网页处理的效率,并且能够减少服务器的负载,但是由于 Cookie 是服务器端保存在客户端的信息,所以其安全性也是很差的。例如常见的记住密码则可以通过 Cookie 来实现。
有一个专门操作Cookie的类 javax.servlet.http.Cookie
。随着服务器端的响应发送给客户端,保存在浏览器。当下次再访问服务器时把 Cookie 再带回服务器。
Cookie 的格式:键值对用 “ = ” 链接,多个键值对间通过 “ ; ” 隔开
Cookie的创建和发送
通过new Cookie()
来创建一个 Cookie 对象,要想将 Cookie 随响应发送到客户端,需要先添加到 response 对象中response.addCookie(cookie)
此时该 cookie 对象则随着响应发送至了客户端。在浏览器上可以看见。
// 创建Cookie对象Cookie cookie = new Cookie("A", "aa");// 响应给客户端resp.addCookie(cookie);
例子:
cookie的流程
服务端创建,然后发送给客户端
package com.zhuo.demo;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@WebServlet("/CookieTest")public class CookieTest extends HttpServlet { private static final long serialVersionUID = 1L; protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Cookie cookie = new Cookie("user", "zhuosong"); response.addCookie(cookie); }}
输出:
Cookie 的获取
在服务器端只提供了一个 getCookies()
的方法用来获取客户端回传的所有 cookie 组成的一个数组,如果需要获取单个 cookie 则需要通过遍历, getName()
获取 Cookie 的名称, getValue()
获取Cookie 的值
获取cookie数组,然后遍历这个数组
package com.zhuo.demo;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@WebServlet("/CookieTest")public class CookieTest extends HttpServlet { private static final long serialVersionUID = 1L; protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //添加两个cookie Cookie cookie = new Cookie("user", "zhuosong"); response.addCookie(cookie); Cookie cookie2 = new Cookie("age","18"); response.addCookie(cookie2); //取出cookie Cookie[] cookies = request.getCookies(); if(cookies!=null && cookies.length>0) { for(Cookie c:cookies) { System.out.println(c.getName()+"="+c.getValue()); } } }}
输出:
Cookie的发送流程
服务端使用response对象准备Cookie,客户端使用request对象获取Cookie.
Cookie 到期时间的设定
从图中除了看到 Cookie 的名称和内容外,我们还需要关心一个信息, 到期时间,到期时间用来指定该 cookie 何时失效。默认为当前浏览器关闭即失效。我们可以手动设定 cookie 的有效时间,通过setMaxAge(int expiry);
方法设定 cookie 的最大有效时间,以秒为单位。
大于 0 的整数,表示存储的秒数;若为负数,则表示不存储该 cookie;若为 0,则删除该cookie。
负整数: cookie 的 maxAge 属性的默认值就是 -1,表示只在浏览器内存中存活,一旦关闭浏览器窗口,那么 cookie 就会消失。
正整数:表示 cookie 对象可存活指定的秒数。 当生命大于 0 时,浏览器会把 Cookie 保存到硬盘上,就算关闭浏览器,就算重启客户端电脑, cookie 也会存活相应的时间。
零: cookie 生命等于 0 是一个特殊的值,它表示 cookie 被作废!也就是说,如果原来浏览器已经保存了这个 Cookie,那么可以通过 Cookie 的 setMaxAge(0) 来删除这个 Cookie。 无论是在浏览器内存中,还是在客户端硬盘上都会删除这个 Cookie。
设置cookie五秒后自动销毁
package com.zhuo.demo;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@WebServlet("/CookieTest")public class CookieTest extends HttpServlet { private static final long serialVersionUID = 1L; protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //添加无过期时间的cookie Cookie cookie = new Cookie("user", "zhuosong"); response.addCookie(cookie); //添加5秒后过期的cookie Cookie cookie2 = new Cookie("age","18"); cookie2.setMaxAge(5); response.addCookie(cookie2); }}
输出:
Cookie 的路径
?????????????????????????????????????????????????????????????????????????????????????????
浏览器在访问 BServlet
时,是否会带上 AServlet
保存的 Cookie,这取决于 Cookie 的 path。
例如有一个站点名为 cookie 的项目,在项目中的配置文件 web.xml
<servlet><servlet-name>cookie</servlet-name><servlet-class>com.xxx.servlet.Cookie</servlet-class></servlet><servlet-mapping><servlet-name>cookie</servlet-name><url-pattern>/coo</url-pattern><url-pattern>/test</url-pattern></servlet-mapping>
package com.zhuo.demo;import java.io.IOException;import java.util.TimeZone;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@WebServlet("/CookieTest")public class CookieTest extends HttpServlet { private static final long serialVersionUID = 1L; protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //默认路径 Cookie cookie = new Cookie("A", "默认路径"); response.addCookie(cookie); //"/"路径 Cookie cookie2 = new Cookie("B","/路径"); cookie2.setPath("/"); response.addCookie(cookie2); //"/test1路径" Cookie cookie3 = new Cookie("C","/test1路径"); cookie3.setPath("/test1"); response.addCookie(cookie3); //"/test2路径" Cookie cookie4 = new Cookie("D","/test2路径"); cookie4.setPath("/test2"); response.addCookie(cookie4); }}
?????????????????????????????????????????????????????????????????????????????????????????
Session
介绍
HttpSession
对象是 javax.servlet.http.HttpSession
的实例,该接口并不像 HttpServletRequest或 HttpServletResponse 还存在一个父接口,该接口只是一个纯粹的接口。这因为 session 本身就属于HTTP 协议的范畴。对于服务器而言,每一个连接到它的客户端都是一个 session, servlet 容器使用此接口创建 HTTP客户端和 HTTP 服务器之间的会话。会话将保留指定的时间段,跨多个连接或来自用户的页面请求。一个会话通常对应于一个用户,该用户可能多次访问一个站点。可以通过此接口查看和操作有关某个会话的信息,比如会话标识符、创建时间和最后一次访问时间。 在整个 session 中,最重要的就是属性的操作。 session 无论客户端还是服务器端都可以感知到,若重新打开一个新的浏览器,则无法取得之前设置的 session,因为每一个 session 只保存在当前的浏览器当中,并在相关的页面取得。
Session 的作用
Session 的作用就是为了标识一次会话,或者说确认一个用户;并且在一次会话(一个用户的多次请求)期间共享数据。 我们可以通过 request.getSession()
方法,来获取当前会话的 session 对象。
HttpSession session = request.getSession();
标识会话JSESSIONID
Session
既然是为了标识一次会话,那么此次会话就应该有一个唯一的标志,这个标志就是sessionId
。
每当一次请求到达服务器,如果开启了会话(访问了 session),服务器第一步会查看是否从客户端回传一个名为 JSESSION 的 cookie,
如果没有则认为这是一次新的会话,会创建一个新的session 对象,并用唯一的 sessionId
为此次会话做一个标志。
如果有 JESSIONID 这个 cookie 回传,服务器则会根据 JSESSIONID 这个值去查看是否含有 id 为 JSESSION 值的 session 对象,
如果没有则认为是一个新的会话,重新创建一个新的 session 对象,并标志此次会话;
如果找到了相应的session 对象,则认为是之前标志过的一次会话,返回该 session 对象,数据达到共享。
这里提到一个叫做 JSESSION 的 cookie,这是一个比较特殊的 cookie,当用户请求服务器时,如果访问了 session,则服务器会创建一个名JSESSION,值为获取到的 session(无论是获取到的还是新创建的)的 sessionId
的 cookie 对象,并添加到 response 对象中,响应给客户端,有效时间为关闭浏览器。所以 Session 的底层依赖 Cookie 来实现
Session对象域
Session 用来表示一次会话,在一次会话中数据是可以共享的,这时 session 作为域对象存在,可以通过 setAttribute(name,value)
方法向域对象中添加数据,通过 getAttribute(name)
从域对象中获取数据,通过 removeAttribute(name)
从域对象中移除数据。
HttpSession session = req.getSession();session.setAttribute("key", "hello");String key = (String) session.getAttribute("key");session.removeAttribute("key");
数据存储在 session 域对象中,当 session 对象不存在了,或者是两个不同的 session 对 象时,数据也就不能共享了。这就不得不谈到 session 的生命周期。
Session 的销毁
默认时间到期
当客户端第一次请求 servlet 并且操作 session 时, session 对象生成, Tomcat 中 session 默认的存活时间为 30min,即你不操作界面的时间,一旦有操作, session 会重新计时。那么 session 的默认事件可以改么?答案是肯定的。可以在 Tomcat 中的 web.xml 文件中进行修改。如下图:
自己设定到期时间
当然除了以上的修改方式外,我们也可以在程序中自己设定 session 的生命周期,通过session.setMaxInactiveInterval(int)
来设定 session 的最大不活动时间,单位为秒。
HttpSession session = request.getSession();session.setMaxInactiveInterval(5);
当然我们也可以通过 getMaxInactiveInterval()
方法来查看当前 Session 对象的最大不活动时间。
立刻失效
或者我们也可以通过 session.invalidate()
方法让 session 立刻失效
session.invalidate();
关闭浏览器
从前面的 JESSION 可知道, session 的底层依赖 cookie 实现,并且该 cookie 的有效时间为关闭浏览器,从而 session 在浏览器关闭时也相当于失效了(因为没有 JSESSION 再与之对应)。
关闭服务器
当非正常关闭服务器时, session 销毁;当正常关闭服务器时, Session 将被序列化到磁盘上,在工作空间work目录下的SESSION.ser文件中,如果对象被保存在了Session 中,服务器在关闭时要把对象序列化到硬盘,这个对象就必须实现 Serializable 接口,下次启动服务时,自动加载到内存。Session 失效则意味着此次会话结束,数据共享结束。
ServletContext 对象
每一个 web 应用都有且仅有一个 ServletContext 对象,又称 Application 对象,从名称中可知,该对象是与应用程序相关的。在 WEB 容器启动的时候,会为每一个 WEB 应用程序创建一个对应的ServletContext 对象。
该对象有两大作用,
第一、作为域对象用来共享数据,此时数据在整个应用程序中共享;
第二、该对象中保存了当前应用程序相关信息。
例如可以通过 getServerInfo(); 方法获取当前服务器信息,getResourceAsStream(String path); 方法以流的形式获取某个资源, getRealPath(String path); 获取资源的真实路径等。
Session和Cookie的区别
session是服务端的对象,而cookie是客户端的对象.
比较奇怪的是,虽然cookie是客户端的对象,但是是有服务端生成的,再发送给客户端保存的.
文件上传下载
文件上传
文件上传涉及到前台页面的编写和后台服务器端代码的编写,前台发送文件,后台接收并保存文
件,这才是一个完整的文件上传。
前台页面
在做文件上传的时候,会有一个上传文件的界面,首先我们需要一个表单,并且表单的请求方式为POST;其次我们的 form 表单的 enctype
必须设为”multipart/form-data”
即enctype="multipart/form-data"
意思是设置表单的 MIME 编码。默认情况下这个编码格是”application/x-www-form-urlencoded”
,不能用于文件上传;只有使用了 multipart/form-data
才能完整地传递文件数据。
Web的调试
一个比较好的调试找错误的例子:
程序如何排错
从上往下,第一行自己写的代码,往往都是出错的地方
MySQL的分页
页数和n的关系
分页的SQL语句
分页实现的五个变量
总页数
总页数并不需要我们自己计算,程序自动计算
数据总数和当前页数据
数据总数的查询
page类
干脆新建一个page类封装这五个字段.
面向接口开发
比如说我要实现某个功能,实现某个类,并不是直接去写这个实现类,而是先定义一个接口,在这个接口里面定义需要实现的方法,然后实现类去继承这个接口.这个接口就是有点像方法的管理目录或者说方法的管理菜单,具体的功能实现放到这个实现类里面了.
接口和实现类的命名规范
多态形式
当面向接口编程的时候,这个方法都是采用多态的形式(接口定义实体类对象)定义,方便接口对方法的管理,
多态的写法:
面向接口编程的好处
DBUtil的抽取
sql语句和参数的传递
又因为sql语句的下标是从1开始的,所以需要加一:
A类如何访问B类的变量
方法重构
比如说数据库的访问方法,有增删改查都会有共同的代码,将这些共同的代码提炼出来,单独写成一个方法
删除文件前的提醒
<a href="javascript: if(window.confirm('是否删除?'))
{
window.location.href='DeleteServlet?name=<%out.print(number);%>
&table=<%out.print(table);%>'
}" >删除</a>
跳转目的地址
跳转到一个DeleteServlet
需要传递过去的参数