一、Cookie
/**
* @author changwen on 2016/11/13.
*/
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.ResourceBundle;
public class Cookie implements Cloneable, Serializable {
private static final long serialVersionUID = -6454587001725327448L;
private static final String TSPECIALS;
private static final String LSTRING_FILE = "javax.servlet.http.LocalStrings";
private static ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.http.LocalStrings");
private String name; //这个只有get方法
/*
下面几个属性都只有简单get,set方法
*/
private String value;
private String comment;
private String domain; //这个set方法有处理过
private int maxAge = -1;
private String path;
private boolean secure;
private int version = 0;
//需要了解,boolean类型的get方法是这样子public boolean isHttpOnly() {return isHttpOnly;}
private boolean isHttpOnly = false;
public Cookie(String name, String value) {
if(name != null && name.length() != 0) {
if(this.isToken(name) && !name.equalsIgnoreCase("Comment") && !name.equalsIgnoreCase("Discard")
&& !name.equalsIgnoreCase("Domain") && !name.equalsIgnoreCase("Expires")
&& !name.equalsIgnoreCase("Max-Age") && !name.equalsIgnoreCase("Path")
&& !name.equalsIgnoreCase("Secure") && !name.equalsIgnoreCase("Version")
&& !name.startsWith("$")) {
this.name = name;
this.value = value;
} else {
String errMsg = lStrings.getString("err.cookie_name_is_token");
Object[] errArgs = new Object[]{name};
errMsg = MessageFormat.format(errMsg, errArgs);
throw new IllegalArgumentException(errMsg);
}
} else {
throw new IllegalArgumentException(lStrings.getString("err.cookie_name_blank"));
}
}
private boolean isToken(String value) {
int len = value.length();
for(int i = 0; i < len; ++i) {
char c = value.charAt(i);
if(c < 32 || c >= 127 || TSPECIALS.indexOf(c) != -1) {
return false;
}
}
return true;
}
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException var2) {
throw new RuntimeException(var2.getMessage());
}
}
static {
if(Boolean.valueOf(System.getProperty("org.glassfish.web.rfc2109_cookie_names_enforced", "true")).booleanValue()) {
TSPECIALS = "/()<>@,;:\\\"[]?={} \t";
} else {
TSPECIALS = ",; ";
}
}
public void setDomain(String domain) {
this.domain = domain.toLowerCase(Locale.ENGLISH);
}
public String getDomain() {
return this.domain;
}
public String getName() {
return this.name;
}
}
二、Session
返回最大时效: getMaxInactiveInterval() 单位是秒
设置最大时效: setMaxInactiveInterval(int interval)
可以在 web.xml 文件中配置 Session 的最大时效, 单位是分钟.
<session-config>
<session-timeout>30</session-timeout>
</session-config>
HttpSession的生命周期:
<session-config>
<session-timeout>30</session-timeout>
</session-config>
public interface HttpSession {
String getId(); // 获取session id
boolean isNew(); //session是否是新的
void setMaxInactiveInterval(int var1); //设置session最大时效
int getMaxInactiveInterval(); // 获取session最大时效
long getCreationTime(); // 创建时间
long getLastAccessedTime(); //上次访问时间
/*最重要的两个方法*/
Object getAttribute(String var1); // 获取属性
void setAttribute(String var1, Object var2);
Enumeration<String> getAttributeNames();
void removeAttribute(String var1);
void invalidate(); //使session无效
ServletContext getServletContext();
/** @deprecated */
HttpSessionContext getSessionContext();
/** @deprecated */
Object getValue(String var1);
/** @deprecated */
String[] getValueNames();
/** @deprecated */
void putValue(String var1, Object var2);
/** @deprecated */
void removeValue(String var1);
}
8.应用
9. 相对路径和绝对路径:
开发时建议编写“绝对路径”:写绝对路径肯定没有问题,但写相对路径却可能有问题
1). 为什么要解决相对路径的问题: 在有一个 Servlet 转发页面的情况下, 会导致相对路径的混乱.
a.jsp: <a href="ToBServlet">To B Page2</a>
ToBServlet: request.getRequestDispatcher("/dir/b.jsp").forward(request, response);
注意, 此时点击 To B Page2 超链接后的浏览器的地址栏的值: http://localhost:8989/day_36/ToBServlet, 实际显示的是dir 路径下的 b.jsp,而 b.jsp 页面有一个超链接: <a href="c.jsp">TO C Page</a>. 默认情况下, c.jsp 应该和 b.jsp 在同一路径下. 此时点击超链接
将在浏览器地址栏显示: http://localhost:8989/day_36/c.jsp. 但在根目录下并没有 c.jsp, 所以会出现路径混乱的问题.
2). 使用绝对路径会解决以上的问题:
绝对路径: 相对于当前 WEB 站点根目录的路径.在当前 WEB 应用的所有的路径前都添加 contextPath 即可.
http://localhost:8989/httpsession/c.jsp:
- http://localhost:8989/ 是 WEB 站点的根目录,
- /httpsession_36 是 contextPath,
- /c.jsp 是相对于当前 WEB 应用的一个文件路径. 我们需要在当前 WEB 应用的任何的路径下都添加上 contextPath, 即可.
比如:
- <a href="ToBServlet">To B Page2</a> 需改为: <a href="<%= request.getContextPath() %>/ToBServlet">To B Page2</a>
- response.sendRedirect("a.jsp"); 需改为: response.sendRedirect(request.getContextPath() + "/a.jsp");
- <form action="AddServlet"></form> 需改为: <form action="<%= request.getContextPath() %>/AddServlet"></form>
3). 在 JavaWEB 应用中 / 代表的是什么:
①.当前WEB应用的根路径:http://localhost:8989/httpsession/ 若/需要交给Servlet容器来处理
- 请求转发时:request.getRequestDispatcher("/path/b.jsp").forward(request,response);
- web.xml文件中映射Servlet访问路径时
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/testServlet</url-pattern>
</servlet-mapping>
②.WEB站点的根路径:http://localhost:8989/ 若 / 直接交由浏览器解析
- 超链接:<a href="/testServlet">To B Page</a>
- 表达式中的action:<form action="/login.jsp"></form>
- 做请求重定向的时候:response.sendRedirect("/a.jsp");
总结: / 什么时候代表站点的根目录, 什么时候代表当前 WEB 应用的根目录
若 / 需要服务器进行内部解析, 则代表的就是 WEB 应用的根目录. 若是交给浏览器了, 则 / 代表的就是站点的根目录
若 / 代表的是 WEB 应用的根目录, 就不需要加上 contextPath 了.
4). 如何获取 contextPath:
ServletContext: getContextPath()
HttpServletRequest: getContextPath()
1). 重复提交的情况:
①. 在表单提交到一个 Servlet, 而 Servlet 又通过 请求转发的方式响应一个 JSP(HTML) 页面,
此时地址栏还保留着 Serlvet 的那个路径, 在响应页面点击 "刷新"
②. 在响应页面没有到达时重复点击 "提交按钮".
③. 点击 "返回", 再点击 "提交"
2). 不是重复提交的情况: 点击 "返回", "刷新" 原表单页面, 再 "提交"。
3). 如何避免表单的重复提交: 在表单中做一个标记, 提交到 Servlet 时, 检查标记是否存在且是否和预定义的标记一致, 若一致, 则受理请求,
并销毁标记, 若不一致或没有标记, 则直接响应提示信息: "重复提交"
①. 仅提供一个隐藏域: <input type="hidden" name="token" value="changwen"/>. 行不通: 没有方法清除固定的请求参数.
②. 把标记放在 request 中. 行不通, 因为表单页面刷新后, request 已经被销毁, 再提交表单是一个新的 request.
③. 把标记放在 session 中. 可以!
在原表单页面, 生成一个随机值 token
在原表单页面, 把 token 值放入 session 属性中
在原表单页面, 把 token 值放入到 隐藏域 中.
在目标的 Servlet 中: 获取 session 和 隐藏域 中的 token 值
比较两个值是否一致: 若一致, 受理请求, 且把 session 域中的 token 属性清除
若不一致, 则直接响应提示页面: "重复提交"
1). 基本原理: 和表单重复提交一致:
在原表单页面, 生成一个验证码的图片, 生成图片的同时, 需要把该图片中的字符串放入到 session 中.
在原表单页面, 定义一个文本域, 用于输入验证码.
在目标的 Servlet 中: 获取 session 和 表单域 中的 验证码的 值
比较两个值是否一致: 若一致, 受理请求, 且把 session 域中的 验证码 属性清除
若不一致, 则直接通过重定向的方式返回原表单页面, 并提示用户 "验证码错误"