一、获取ReqeustDispatcher的方式
1、ServletContext getRequestDispatcher(String path)
2、ServletRequest getRequestDispatcher(String path)
共同点:起到的作用是一样的
不同点:在于方法的参数path的写法上
1、path:必须是一个绝对路径。也就是说必须以“/”开头
2、path:既可以是绝对路径,又可以是相对路径。不以"/"开头就是相对路径(路径必须正确)
二、转发和重定向的细节
1、转发:只能转发到本应用的其他资源
2、请求重定向:可以转发到任何地址。
三、绝对路径的写法(都以/开头)
如果是给客户端用的,要加项目名称,否则(给服务器用的),不加项目名称
转发:getRequestDispatcher(String path) 不需要加项目名称 /servlet/Demo2
重定向:sendRedirect(String path) 需要加项目名称 /day06/servlet/Demo2
Refresh=2;URL=path 需要加项目名称 /day06/servlet/Demo2
form表单的action 需要加项目名称 /day06/servlet/Demo2
a的href: 需要加项目名称 /day06/servlet/Demo2
包含:include(String path) 不需要加项目名称 /servlet/Demo2
四、什么是会话,如同打电话
会话过程中要解决的问题:用户数据的保存问题。保存在ServletContext和ServletRequest的域对象中是不可取的。
解决方案:
1、Cookie
是客户端技术,程序把每个用户的数据以cookie的形式写给用户各自的浏览器
响应消息头Set-Cookie:key=value
请求消息头cookie:key=value
一个cookie必须有name和value,还有以下可选属性
comment:
path:默认路径就是访问写写domain:
名为abc的cookie的path:www.sina.com/mail
http://localhost:8080/day06 能不能得到abc这个cookie。不能
www.sina.com/mail 能不能得到abc这个cookie。能
www.sina.com/mail/abc/a.jsp 能不能得到abc这个cookie。能
如果把cookie的路径设置为:localhost/day06 说明day06下面的所有资源都可以访问这个cookie
age:设置cookie的缓存时间。默认的时间是一个负数(浏览器关闭时删除)。如果是0,则是删除该cookie。正整数才是存活的时间。
version:
如何向客户端写一个cookie:
HttpServletResponse.addCookie(javax.servlet.http.Cookie)
注:每个服务器只能存放20 cookie (稀有)
浏览器端最多能存放300cookie
每个cookie的大小不能超过4Kb
服务器如何获取客户端带来的cookie:
HttpServletRequest.getCookies()
注:不同网站向同一个客户端写的cookie的名称一致,可以通过cookie的path属性进行区分
cookie的默认存活时间是会话范围。
2、HttpSession
是服务器端技术,利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的session对象
如何得到HttpSession对象:
HttpServletRequest getSession()
其实session技术利用了cookie技术:
向客户端写了一个名字为JSESSIONID的cookie
value:session对象的id
path:/day06 request.getContextPath()
age:负数
特别注意:
关闭浏览器是结束一次会话。但是对于服务器来讲,并不会立刻销毁内存中的session对象。默认的session的存活时间是30分钟。
getSession():如果内存中有对应的session,它是获取方法。如果没有,则创建新的session。
getSession(true)功能同没有参数的
getSession(false):如果内存中有对应的session,它是获取方法。如果没有,返回null
Cookie案例:
eg1:记录用户最近一次的访问时间
每次访问页面的时候将系统时间记录下来
Cookie c = new Cookie("lastAccessTime",System.currentTimeMillis()+"");
//设置Cookie有限期
c.setMaxAge(Integer.MAX_VALUE);
c.setPath(request.getContextPath());
//写回Cookie
response.addCookie(c);
清除上一次访问的时间
//得到所有的Cookies //day06/servlet路径下
Cookie cookies[]= request.getCookies();
//进行遍历查找对应的 Cookie ,并对其进行设置
for(int i =0 ;cookies!=null&&i<cookies.length;i++ ){
if("lastAccessTime".equals(cookies[i].getName())){
Cookie c = cookies[i];
//对其进行设置
c.setMaxAge(0);//存活时间为零
c.setPath(request.getContextPath());//path必须相同,以Cookiename相同时,用path进行区别
//写回Cookie/也可理解为覆盖
response.addCookie(c);
}
}
eg2:记住用户名
常量接口设计模式
a)使用场景:在程序设计中,我们可以把所用要用到的常量设计为一个独立的类,使得对常量的管理有效清晰。
eg:
public interface MyConstant {
//接口常量,这样可以避免拼写错误
String USERNAME = "username";
}
CookieDemo1中:
//让用户名默认为空,记录用户名被checked时才根据Cookie显示
String username = "";
String checked = "";
//根据Cookie决定是否显示用户名
Cookie cookies[] = request.getCookies();
for(int i = 0;cookies!=null&&i<cookies.length;i++){
if(MyConstant.USERNAME.equals(cookies[i].getName())){
username = cookies[i].getValue();
checked = "checked='checked'";
break;
}
}
//生成登录页面
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.print("<html><head>用户登录</head><body>");
out.print("<form action='"+request.getContextPath()+"/servlet/CookiesDemo4' method='post'>");
out.print("用户名:<input type='text' name='username' value='"+username+"'><br/>");
out.print("密码:<input type='password' name='password'><br/>");
out.print("<input type='submit' value='登录'><br/>");
out.print("记住用户名<input type='checkbox' name='remeber' "+checked+">");
out.print("</form></body></html>");
CookieDemo2中;
// 获取表单中的信息,进行验证
String username = request.getParameter("username");
String remeber = request.getParameter("remeber");
if (remeber == null) {// 不需要记住 ,需要清空Cookie
Cookie cookies[] = request.getCookies();
for(int i =0;cookies!=null&&i<cookies.length;i++){
Cookie c = cookies[i];
if(MyConstant.USERNAME.equals(username)){
//清空Cookie
c.setMaxAge(0);
c.setPath(request.getServletPath());
break;
}
}
} else {
//需要记录该 Cookie
Cookie cookie = new Cookie(MyConstant.USERNAME, username);
cookie.setMaxAge(Integer.MAX_VALUE);
cookie.setPath(request.getContextPath());
//写回cookie
response.addCookie(cookie);
}
eg3:购物网站中的应用,记住用户最近浏览的商品记录
ShowAllBookServlet;
//列出所有的书籍
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
Map<String,Book> books = BookDb.getBooks();
for(Map.Entry<String, Book> me:books.entrySet()){
out.print("<a href='"+request.getContextPath()+"/servlet/ShowDetailsServlet?id="+me.getKey()+"' target='_blank'>"+me.getValue().getName()+"</a><br/>");
}
//显示最近浏览的商品列表,最多三件
out.print("<hr/>");
out.print("最近浏览过的商品:<br/>");
//根据Cookie中保存的信息进行显示
Cookie cookies[] = request.getCookies();
for(int i=0;cookies!=null&&i<cookies.length;i++){
Cookie cookie = cookies[i];
if(MyConstant.BOOK_HISTORY.equals(cookie.getName())){
String value = cookie.getValue();//1-2-3
String ids[] = value.split("\\-");
for(String id:ids){
//显示书的名称
out.print(BookDb.findBookById(id).getName()+"<br/>");
}
break;
}
}
ShowDetailsServlet:
doGet():
PrintWriter out = response.getWriter();
out.print("书的详细信息如下<br/>");;
String id = request.getParameter("id");
Book book = BookDb.findBookById(id);
out.print("名称:"+book.getName()+"<br/>");
out.print("作者:"+book.getAuthor()+"<br/>");
out.print("价格:"+book.getPrice()+"<br/>");
out.print("评价:"+book.getDescription()+"<br/>");
//将书的id保存到Cookie中
String value = makeId(id,request);
Cookie cookie = new Cookie(MyConstant.BOOK_HISTORY, value);
//设置Cookie属性
cookie.setMaxAge(Integer.MAX_VALUE);
cookie.setPath(request.getContextPath());
//写回Cookie
response.addCookie(cookie);
/*
**进行字符串拼接,根据用户当前浏览的书id 拼出浏览信息,如 1-2-3
*/
private String makeId(String id, HttpServletRequest request) {
Cookie cookies[] = request.getCookies();
//cookies为空,为第一次访问书籍
if(cookies==null)
return id;
Cookie cookie = null;//记录book_history
for(Cookie c : cookies){
//若存在name为BOOK_HISTORY的Cookie ,用cookie记录
if(MyConstant.BOOK_HISTORY.equals(c.getName())){
cookie = c;
break;
}
}
if(cookie==null)//有cookie但没有name为book_history的cookie
return id;
String value = cookie.getValue();
String ids[] = value.split("\\-");
LinkedList<String> list = new LinkedList<String>(Arrays.asList(ids));
if(list.size()<3){//浏览商品少于三件
if(list.contains(id)){
//浏览过,从原来记录中删除,然后将其添加到第一位
list.remove(id);
list.addFirst(id);
}else{
//没有浏览过,直接添加到第一位
list.addFirst(id);
}
}else{//浏览商品正好三件
if(list.contains(id)){
//浏览过,从原来记录中删除,然后将其添加到第一位
list.remove(id);
list.addFirst(id);
}else{
//没有浏览过,先移除最末一位.然后将当前浏览商品添加到第一位
list.removeLast();
list.addFirst(id);
}
}
StringBuffer sb = new StringBuffer();
for(int i=0;i<list.size();i++){
if(i>0){//从第一个id后添加-
sb.append("-");
}
sb.append(list.get(i));
}
return sb.toString();
}
Session 案例
eg1:简单的购物车
ShowAllProductsServlet 显示所有的商品,提供购买的超链接
//列出所有的书籍
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.print("本站有以下书籍<br/>");
Map<String,Book> books = BookDb.getBooks();
for(Map.Entry<String, Book> me:books.entrySet()){
out.print(me.getValue().getName()+"<a href='"+request.getContextPath()+"/servlet/BuyServlet?id="+me.getKey()+"'>购买</a><br/>");
}
BuyServlet 将当前购买的商品添加到购物车中
String id = request.getParameter("id");
Book book = BookDb.findBookById(id);
HttpSession session = request.getSession();
List<Book> carts = (List<Book>) session.getAttribute("carts");
if(carts==null){
carts = new ArrayList<Book>();
carts.add(book);
session.setAttribute("carts", carts);
}else{//若已经存在session则直接添加
carts.add(book);
}
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.print("<a href='/day06/servlet/ShowAllProductsServlet'>继续购物</a><br/><a href='/day06/servlet/ShowCartServlet'>去结算</a>");
ShowCartServlet 显示购物车中的商品
response.setContentType("text/html;charset=UTF-8");
PrintWriter out =response.getWriter();
HttpSession session = request.getSession();
List<Book> carts = (List<Book>) session.getAttribute("carts");
out.print("购物车中的商品:");
for(Book book:carts){
out.print(book.getName()+"<br/>");
}
out.print("去付款");
BookDb 模拟数据库信息
public class BookDb {
// key->id value->book
public static Map<String, Book> books = new HashMap<String, Book>();
/*
* 初始化时添加五本书
*/
static{
books.put("1", new Book("1","JAVA疯狂讲义","李刚","58.00","非常好的入门书"));
books.put("2", new Book("2","java入门详解","张孝祥","99.00","好得不行了"));
books.put("3", new Book("3","javascript","毕向东","55.00","经典初级入门"));
books.put("4", new Book("4","web深入开发","方立勋","89.00","不错哦"));
books.put("5", new Book("5","安卓深入浅出","老田","110.00","深入浅出经典之作"));
}
public static Map<String, Book> getBooks() {
return books;
}
//通过id找到对应的书
public static Book findBookById(String id){
return books.get(id);
}
}
eg2:简单的用户登录
LoginServlet:
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
String username = request.getParameter("username");
String password = request.getParameter("password");
PrintWriter out = response.getWriter();
User user = new User();
user.setUsername(username);
user.setPassword(password);
HttpSession session = request.getSession();
session.setAttribute("user", user);
out.print("登录成功,两秒后进行跳转");
//登录成功,两秒后进行跳转
response.setHeader("Refresh", "2;url=/day06/servlet/IndexServlet");
IndexServlet
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
HttpSession session = request.getSession();
User user = (User) session.getAttribute("user");
out.print("欢迎"+user.getUsername()+"登录<br/>");
out.print("这里是主页");
eg3:登录时进行验证码校验
ImageServlet生成验证码时中记录下来,先进行加密,然后放到session中
StringBuffer sb = new StringBuffer();
for(int i = 0;i<4;i++){
int num = r.nextInt(10);
g.drawString(num+"", x, 15);
x+=20;
sb.append(num);
}
//得到生成的验证码
String code = sb.toString();
//用md5算法对验证码进行加密
code = Md5Util.encode(code);
HttpSession session = request.getSession();
session.setAttribute("code", code);
在LoginServlet中将页面用户输入的验证码和生成的验证码进行对比
String code = request.getParameter("code");
PrintWriter out = response.getWriter();
HttpSession session = request.getSession();
//code2是正确的验证码,与用户填入的进行对比
String code2 = (String) session.getAttribute("code");
if(!code2.equals(Md5Util.encode(code))){
out.print("对不起,验证码输入失败");
//跳转回登录页面
response.setHeader("Refresh", "2;url=/day06/Login2.html");
return;
}
加密类Md5Util
public class Md5Util {
public static String encode(String code) {
try{
//得到md5算法的数据摘要
MessageDigest md = MessageDigest.getInstance("md5");
//根据MD5算法得到的数据指纹
byte[] b = md.digest(code.getBytes());
//用base64编码,将没有对应字符的二进制转为可见的字符
BASE64Encoder base64 = new BASE64Encoder();
return base64.encode(b);
}catch(Exception e){
throw new RuntimeException(e);
}
}
}
eg4:防止表单的重复提交
javascript在点击提交按钮后,将其设置为disabled
function toSubmit(btnObj){
var formObj = document.getElementById("f1");
btnObj.disabled=true;
formObj.submit();
}
但用户刷新后仍可以重复提交,不能根本解决问题
解决方案:
生成一个UUid (随即且唯一) ,保存在session中,然后同页面中hidden的输入框中的id进行比较
public class UUidUtil {
public static String getId() {
return UUID.randomUUID().toString();
}
}
ServletDemo0:
//生成登录页面
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
//随机生成一个唯一的码
String id = UUidUtil.getId();
out.print("<form id='f1' action='/day06/servlet/ServletDemo1' method='post'>"+
"姓名:<input type='text' name='username'/><br/>"+
"<input type='hidden' name='token' value='"+id+"'>"+
"<input type='submit' value='提交'>");
HttpSession session = request.getSession();
session.setAttribute("id", id);
ServletDemo1:
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
String username = request.getParameter("username");
String ctoken = request.getParameter("token");
HttpSession session =request.getSession();
String stoken = (String) session.getAttribute("id");
PrintWriter out = response.getWriter();
if(ctoken.equals(stoken)){//不一致 进行提交,然后移除session 里的id,让用户可以进行新的提交
System.out.println(username);
session.removeAttribute("id");
}else{//一致,说明是重复提交
out.print("表单不能重复提交");
return;
}