20210903上课
重定向和请求转发
请求转发(forward)
请求转发也称之为服务端跳转,即客户端只发送一次请求,后续的多次请求由服务端发起,因此客户端的请求地址栏只会显示第一次发送的地址(实际服务端已经在内部转发了多次),请求转发可以将存储在一次request范围(客户端发起的)之内的数据传递到下一个Servlet中
重定向也称之为客户端跳转,即客户端请求会发送多次,而且地址栏会显示最后一次请求的地址,由于重定向是客户端的跳转方式,因此每次请求之间没有直接关系,所以存储在request范围之内数据无法传递到下一个请求
面试题:请你聊聊重定向和请求转发的区别?
相同点
*页面都会实现跳转
不同点
*请求转发的时候,url不会产生变化 307
*重定向时候,url地址栏会发生变化 302
getRequestDispatcher分成两种,可以用request调用,也可以用getServletContext()调用
不同的是request.getRequestDispatcher(url)的url可以是相对路径也可以是绝对路径。
而this.getServletContext().getRequestDispatcher(url)的url只能是绝对路径。
1、sendRedirect
LoginServlet
@WebServlet("/user/login")
public class LoginServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("进入登录");
resp.sendRedirect("goods/list");
}
}
GoodListServlet
@WebServlet("/goods/list")
public class GoodListServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("进入商品列表");
}
}
当在地址栏请求的时候出现404
http://localhost:8080/20210902/user/goods/list
因为现在在user后面直接拼接了goods/list因为user下面没有这个模块所以请求不到.现在想要解决的办法有:
resp.sendRedirect("../goods/list");
2、getRequestDispatcher
//获得到请求的调度器
//RequestDispatcher rd = req.getRequestDispatcher("../goods/list");
//rd.forward(req,resp);
区别:
1、请求转发能够从服务端发起跳转,重定向是在客户端进行跳转
2、地址不发生改变
重定向的地址会发生改变
请求转发的地址始终发生不变
3、请求转发每次刷新都会刷新整个过程,比如:付款转账延时的时候刷新会将之前的内容再次执行
3、getAttribute和setAttribute
请求转发可以将存储在request范围之内的数据传输给下一个servlet中
@WebServlet("/user/login")
public class LoginServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("进入登录");
//resp.sendRedirect("../goods/list");
//获得到请求的调度器
//RequestDispatcher rd = req.getRequestDispatcher("../goods/list");
//rd.forward(req,resp);
req.setAttribute("username","ZhangSan");
req.getRequestDispatcher("../goods/list").forward(req,resp);
}
}
@WebServlet("/goods/list")
public class GoodListServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("进入service");
System.out.println("goodList获取request中的数据:" + obj);
Object obj = req.getAttribute("username");
req.getRequestDispatcher("../system/info").forward(req,resp);
}
}
@WebServlet("/system/info")
public class SystemServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("进入到系统信息");
Object username = req.getAttribute("username");
Object list = req.getAttribute("list");
System.out.println("username : " + username);
System.out.println("list : " + list);
}
}
request的作用域一次request请求的范围(客户端请求)
Servlet作用域对象的问题
request
request作用域作用于一次请求范围(客户端请求),当使用请求转发跳转时,可以向request范围之内存储数据,并且在下一个Servlet中可以将request的数据获取
使用方式:
//存储数据到request
request.setAttribute(name,value);
//获取request数据
request.getAttribute(name)
session
session也称之为会话,通常指的是一次项目的访问过程(只要浏览器不关闭,或者不到达session的有效期)存储在session范围之内的数据会一直存在。
session有效期默认是:30分钟(在tomcat的web.xml文件中有配置):
如果需要修改session的有效期,不要去直接修改服务器中的web.xml;而是应该修改web项目中WEB-INF下的web.xml
<!--配置session的有效期(单位:分钟)-->
<session-config>
<session-timeout>20</session-timeout>
</session-config>
客户端每一次服务端建立连接时都会创建一个session(具备一个独一无二的sessionid),在同一次访问中,无论页面(请求)如何跳转,只要sessionid是同一个,则session中的数据是一直有效且共享的。一旦浏览器关闭,下一次重新开启时,sessionid会发生变更。
使用方式
HttpSession session = request.getSession();
//存储数据到session
session.setAttribute(name,value);
//取出session中的数据
session.getAttribute(name)
作用:
session的存储范围为服务端,一般用于存储一些安全性要求较高的少量数据,比如:用户的登录信息(账号密码等)
在LoginServlet中一个代码
req.setAttribute("username","ZhangSan");
req.getRequestDispatcher("../goods/list").forward(req,resp);
在SystemServlet的service方法中添加一个代码,请求转发的时候可以将username的值带到SystemServlet,但是重定向是不可以的
Object username = req.getAttribute("username");
//设置响应编码
resp.setCharacterEncoding("utf-8");
resp.getWriter().write("<html>");
resp.getWriter().write("<head>");
resp.getWriter().write("<meta charset=\"utf-8\">");
resp.getWriter().write("<title>系统信息</title>");
resp.getWriter().write("</head>");
resp.getWriter().write("<body>");
resp.getWriter().write("<h1>系统信息页</h1>");
resp.getWriter().write("<hr>");
resp.getWriter().write("欢迎你,"+username);
resp.getWriter().write("</body>");
resp.getWriter().write("</html>");
在使用重定向的时候就使用session进行存储值
在LoginServlet中一个代码
//从Request范围获取session
HttpSession session = req.getSession();
System.out.println("session被创建,sessionID:"+session.getId());
//一旦客户端和服务端建立连接,服务端会自动产生一个session,如果session已存在,则不创建直接使用
//如果不存在,则创建一个;如果使用req.getSession(true),无论何时都会重新创建session对象
//req.getSession(false);
session.setAttribute("user","softeem");
在GoodsListServlet中
Object user = req.getSession().getAttribute("user");
System.out.println("goodsList取得Session数据:"+user);
在SystemServlet中修改
resp.getWriter().write("欢迎你,"+req.getSession().getAttribute("user"));
两个页面之间共享数据使用request
整个页面使用的时候是session
多个用户使用的时候是SevletContent
ServletContext
ServletContext也称之为上下文环境;一旦web容器启动,项目被装载后,服务端会自动创建一个上下文环境(SerlvetContext),该环境是唯一的**(单例)**;只要服务器不重启,存储在在该范围内的数据会一直有效,并且可以实现多个用户之间的数据共享。
//获取ServletContext(jsp:application)
//方式一:
ServletContext app1 = this.getServletContext();
//方式二:
ServletContext app2 = this.getServletConfig().getServletContext();
//方式三:
ServletContext app3 = req.getSession().getServletContext();
System.out.println(app1);
System.out.println(app2);
System.out.println(app3);
app1.setAttribute("viewCount",10);
使用方式:
ServletContext context = req.getSession().getServletContext();
//存储数据到ServletContext
context.setAttribute(name,value);
//获取ServletContext数据
context.getAttribute(name)
如果需要对指定作用域中的属性清除可以使用:
removeAttribute(name)移除
对于session来说还可以直接使用`invalidate()`方法,清除整个session对象
resp.getWriter().write("<html>");
resp.getWriter().write("<head>");
resp.getWriter().write("<meta charset=\"utf-8\">");
resp.getWriter().write("<title>系统信息</title>");
resp.getWriter().write("</head>");
resp.getWriter().write("<body>");
resp.getWriter().write("<h1>系统信息页</h1>");
resp.getWriter().write("<hr>");
resp.getWriter().write("欢迎你,"+username);
resp.getWriter().write("<p>网站总访问量:"+this.getServletContext().getAttribute("viewCount")+"</p>");
resp.getWriter().write("</body>");
resp.getWriter().write("</html>");
清空session
@WebServlet("/req/login")
public class LoginServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println(username+"/"+password);
if(Objects.equals("softeem",username)){
if(Objects.equals("123456",password)){
//向session中存储用户信息
req.getSession().setAttribute("user",username);
//登录成功(跳转主界面)
resp.sendRedirect("../req/index");
}else{
//密码错误
req.setAttribute("msg","密码错误");
req.getRequestDispatcher("../req/loginpage").forward(req,resp);
}
}else{
//账户不存在
req.setAttribute("msg","账户不存在");
req.getRequestDispatcher("../req/loginpage").forward(req,resp);
}
}
}
@WebServlet("/req/loginpage")
public class LoginPageServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Object msg = req.getAttribute("msg");
resp.setCharacterEncoding("utf-8");
resp.getWriter().write("<html>");
resp.getWriter().write("<head>");
resp.getWriter().write("<meta charset='UTF-8'>");
resp.getWriter().write("<title>用户登录</title>");
resp.getWriter().write("</head>");
resp.getWriter().write("<body>");
resp.getWriter().write("<h1>用户登录</h1>");
if(msg != null) {
resp.getWriter().write("<h2 style=\"color:red\">" + msg + "</h2>");
}
resp.getWriter().write("<form action=\"../req/login\">");
resp.getWriter().write("账号:<input type='text' name='username'><br>");
resp.getWriter().write("密码:<input type='password' name='password'><br>");
resp.getWriter().write("<button>登录</button>");
resp.getWriter().write("</form>");
resp.getWriter().write("</body>");
resp.getWriter().write("</html>");
}
}
@WebServlet("/req/index")
public class IndexServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Object user = req.getSession().getAttribute("user");
if(user == null){
req.setAttribute("msg","请登陆后再试!");
req.getRequestDispatcher("../req/loginpage").forward(req,resp);
return;
}
resp.setCharacterEncoding("utf-8");
resp.getWriter().write("<html>");
resp.getWriter().write("<head>");
resp.getWriter().write("<meta charset='UTF-8'>");
resp.getWriter().write("<title>主页</title>");
resp.getWriter().write("</head>");
resp.getWriter().write("<body>");
resp.getWriter().write("<h1>XXX管理系统</h1>");
resp.getWriter().write("<h2>欢迎你,"+user+"</h2>");
resp.getWriter().write("<a href='../req/logout'>安全退出</a>");
resp.getWriter().write("</body>");
resp.getWriter().write("</html>");
}
}
@WebServlet("/req/logout")
public class LogoutServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
//将session中的指定属性移除
// session.removeAttribute("user");
// req.removeAttribute("XXX");
// this.getServletContext().removeAttribute("XXX");
//将session失效
session.invalidate();
resp.sendRedirect("../req/loginpage");
}
}
Cookie
Http协议是无状态的(每次一次的http请求相互之间无关联),但是实际开发中经常需要在多个请求之间进行数据的传递,以及状态的转移;此时servlet中支持四种会话跟踪技术:
-
URL传值(get请求)
http://localhost/javaweb01/login?username=admin&password=123456&code=0.12312435345345
-
隐藏域传值(post/get请求)
<input type="hidden" name="method" value="login">
-
session
session.setAttribute(name,value);
-
Cookie
Cookie是客户端浏览器,实现数据缓存的一种技术,是通过在本地操作系统中以文件存储的形式,存储少量(4kb左右)的数据信息;cookie由服务端制造,通过response对象响应到客户端,然后再通过request发送到服务端,
cookie的使用如下:
-
制造并存储cookie
public class User { private int id; private String username; private String password; private int level; public class Model { int code; String msg; Object data;
@WebServlet("/cookie/add") public class CookieServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { User u = new User(1,"张三","张三123",5); Model m = new Model(0,"操作成功",u); //创建cookie对象 Cookie c = new Cookie("username","softeem"); //设置cookie的有效期(秒) 一周时间 默认有效期与session保持一致 c.setMaxAge(60*60*24*7); //设置cookie生效的域名(主机地址) // c.setDomain("com.softeem.top"); //设置cookie生效的位置(对于同一个主机中的哪一个项目) // c.setPath("/javaweb05"); c.setPath("/"); //将对象转换为json字符串 String s = JSON.toJSONString(m); //将数据转码(Cookie不支持中文和特殊符号) s = URLEncoder.encode(s, "utf-8"); System.out.println("json---->"+s); Cookie c2 = new Cookie("info", s); c2.setMaxAge(60*60*24); c2.setPath("/javaweb05"); //将cookie对象通过response对象发送到客户端 resp.addCookie(c); resp.addCookie(c2); } }
-
读取Cookie
@WebServlet("/cookie/read") public class ReadCookieServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获取从客户端传递过来的所有cookie信息 Cookie[] cookies = req.getCookies(); for (Cookie c : cookies) { String name = c.getName(); String value = c.getValue(); System.out.println(name+"---->"+value); if("info".equals(name)){ //解码 String json = URLDecoder.decode(value, "utf-8"); System.out.println("info解码后:"+json); //解析为javabean Model model = JSON.parseObject(json, Model.class); System.out.println("解析json为javabean:"+model); } } } }
@WebServlet("/cookie/login") public class LoginServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username"); String password = req.getParameter("password"); String save = req.getParameter("save"); if("softeem".equals(username) && "123456".equals(password)){ if("1".equals(save)){ //保存账号信息 User u = new User(); u.setUsername(username); u.setPassword(password); String data = URLEncoder.encode(JSON.toJSONString(u), "utf-8"); System.out.println("存储用户:"+data); //将用户对象转换为json字符串并转码之后存储到cookie中 Cookie c = new Cookie("user", data); c.setMaxAge(60*60*24*7); c.setPath("/javaweb05"); resp.addCookie(c); } resp.sendRedirect("main.html"); }else{ //登录失败 req.setAttribute("msg","请检查账号或密码"); req.getRequestDispatcher("../index.jsp").forward(req,resp); } } }
@WebServlet("/cookie/check") public class CheckCookieServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username"); Cookie[] cookies = req.getCookies(); for (Cookie c : cookies) { if(c.getName().equals("user")){ String user = URLDecoder.decode(c.getValue(), "utf-8"); User u = JSON.parseObject(user,User.class); if(u.getUsername().equals(username)){ req.setAttribute("user",u); } } } req.getRequestDispatcher("../index.jsp").forward(req,resp); } }
<html> <head> <base href="<%=basePath%>"> <meta charset="UTF-8"> <title>$Title$</title> </head> <body> <h2 style="color:red"><%=request.getAttribute("msg")%></h2> <%--<%=((User)request.getAttribute("user")).getUsername()%>--%> <form action="cookie/login"> <input type="text" name="username" id="username" value="${user.username}" placeholder="请输入用户名"><br> <input type="password" name="password" value="${user.password}" placeholder="请输入密码"><br> <input type="checkbox" name="save" value="1">保存一周 <br> <button>登录</button> </form> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <script> $(function(){ $("#username").blur(function(){ location.href="cookie/check?username="+$(this).val(); }) }) </script> </body> </html>
Cookie的应用场景:
- 大型网站的首页信息的缓存
- 购物车功能
- 保存账号功能
- 历史浏览记录
- …