Servlet会话管理一(URL重写和表单隐藏域)
会话可以简单的理解为客户端用户打开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器的整个过程称为一个会话。即一个客户端用户和服务器端进行通讯的过程,也是客户端和服务器端之间的数据传输过程。
HTTP协议的无状态性导致web服务器无法区分一个HTTP请求是否为第一次访问,因此需要程序必须能管理会话,并为每个用户保存其与服务器交互过程中产生的数据。
会话管理就是管理浏览器客户端和服务器端之间会话过程中产生的数据。
Servlet有4中不同的状态保持技术:
-- URL重写(token-based会话跟踪技术)
-- 表单隐藏域(仅当网页中有表单时使用) <input type="hidden" name="field_name" value="value">
-- cookies(客户端技术,数据保存在客户端)
-- HTTPSession(服务器端技术,数据保存在服务器端)
URL重写 (token-based会话跟踪技术)
将一个或多个token添加到URL的查询字符串中,每个token通常为 key=value 的形式。其中,URL和token之间用 ? 分隔,token之间用 & 分隔。
URL重写仅适合于信息仅在少量页面间传递,且信息本身不敏感的情况
数据通过URL后面的token传递到下一个页面,在下一个页面(即下一个http请求)中,通过获取request.getParameter(name)获取tonken的值,从而保证了数据在不同页面间的传递 。
请表单的提交方式为get时,表单中的参数也会显示的出现在URL后面
package app02a.urlrewriting; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Servlet implementation class Top10Servlet */ @WebServlet(name = "Top10Servlet", urlPatterns = { "/top10" }) public class Top10Servlet extends HttpServlet { private static final long serialVersionUID = 1L; private List<String> londonAttractions; private List<String> parisAttractions; public Top10Servlet() { super(); } @Override public void init() throws ServletException { // 用户第一次访问时,Tomcat初始化Servlet,init()方法被调用。 londonAttractions = new ArrayList<String>(10); // 赋值类的成员变量 londonAttractions.add("1.Buckingham Palace"); londonAttractions.add("2.Lodon Eye"); londonAttractions.add("3.British Museum"); londonAttractions.add("4.National Gallery"); londonAttractions.add("5.Big Ben"); londonAttractions.add("6.Tower of London"); londonAttractions.add("7.Natural History Museum"); londonAttractions.add("8.Canary Wharf"); londonAttractions.add("9.2012 Olympic Park"); londonAttractions.add("10.ST Paul's Cathedral"); parisAttractions = new ArrayList<String>(10); // 赋值类的成员变量 parisAttractions.add("1.Eiffel Tower"); parisAttractions.add("2.Notre Dame"); parisAttractions.add("3.The Louver"); parisAttractions.add("4.Champs Elysees"); parisAttractions.add("5.Arc de Triomphe"); parisAttractions.add("6.Sainte Chapelle Church"); parisAttractions.add("7.Les Invalides"); parisAttractions.add("8.Muess d'Orsay"); parisAttractions.add("9.Montmarte"); parisAttractions.add("10.Sacre Couer Basilica"); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String city = request.getParameter("city"); // 获取token的值,该token随URL传递过来 if (city != null && (city.equals("london") || city.equals("paris"))) { showAttractions(request, response, city); } else { showMainPage(request, response); } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub doGet(request, response); } private void showMainPage(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter writer = response.getWriter(); // 向客户端发送信息 writer.println("<html>"); writer.println("<head>"); writer.println("<title>Top10 Tourist Atrractions</title>"); writer.println("</head>"); writer.println("<body>"); writer.println("Please select a city:<br />"); writer.println("<a href='?city=london'>London</a><br />"); // 相对url,由 http://localhost:8080/top10/top10 writer.println("<a href='?city=paris'>Paris</a><br />"); // 变为 http://localhost:8080/top10/top10?city=london writer.println("</body>"); // 并将后者发送到服务器 writer.println("</html>"); } private void showAttractions(HttpServletRequest request, HttpServletResponse response, String city) throws ServletException, IOException { int page = 1; String pageParameter = request.getParameter("page"); // 获取token的值 if (pageParameter != null) { try { page = Integer.parseInt(pageParameter); }catch(NumberFormatException e) { e.printStackTrace(); } if (page > 2) { page = 1; } } List<String> attractions = null; if (city.equals("london")) { attractions = londonAttractions; } else { attractions = parisAttractions; } response.setContentType("text/html"); PrintWriter writer = response.getWriter(); writer.println("<html>"); writer.println("<head>"); writer.println("<title>Top 10 Tourist Attractions</title>"); writer.println("</head>"); writer.println("<body>"); writer.println("<a href='top10'>Select City</a>"); // 相对url 由 http://localhost:8080/top10/top10?city=london writer.println("<hr />"); // 变为 http://localhost:8080/top10/top10 writer.println("Page " + page); writer.println("<hr />"); int start = page * 5 -5; for (int i = start; i < start + 5; i++) { writer.println(attractions.get(i) + "<br />"); } writer.print("<hr style='color:blue' />"); writer.println("<a href='?city=" + city + "&page=1'>Page 1</a>"); // 由 localhost:8080/top10/top10?city=london 变为 localhost:8080/top10/top10?city=london&page=1 writer.println(" <a href='?city=" + city + "&page=2'>Page 2</a>"); // 由 localhost:8080/top10/top10?city=london 变为 localhost:8080/top10/top10?city=london&page=2 writer.println("</body>"); writer.println("</html>"); } }
表单隐藏域(仅当网页中有表单时有效)
使用隐藏域来保存会话过程中的数据是将值放到HTML表单的隐藏域中。当表单提交时,隐藏域的值也同时提交到服务器端。
<input type="hidden" name="field_name" value="value">
该技术较URL重写相比,没有字符的限制,同时无需额外编码,但也不适合跨越多个页面。
package app02a.hiddenfields; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet(name = "CustomerServlet", urlPatterns = { "/customer", "/editCustomer", "/updateCustomer" }) public class CustomerServlet extends HttpServlet { private static final long serialVersionUID = 1L; private List<Customer> customers = new ArrayList<Customer>(); // 类的成员变量 public CustomerServlet() { super(); } @Override public void init() throws ServletException { // 初始化两个数据,正常情况下,这些数据应该存储在数据库中 Customer customer1 = new Customer(); customer1.setId(1); customer1.setName("Donal D."); customer1.setCity("Miami"); customers.add(customer1); Customer customer2 = new Customer(); customer2.setId(2); customer2.setName("Micky M."); customer2.setCity("Orlando"); customers.add(customer2); } private void sendCustomerList(HttpServletResponse response) throws IOException { response.setContentType("text/html"); PrintWriter writer = response.getWriter(); writer.println("<html>"); writer.println("<head>"); writer.println("<title>Customers</title>"); writer.println("</head>"); writer.println("<body>"); writer.println("<h2>Customers</h2>"); writer.println("<ul>"); for (Customer customer : customers) { writer.println("<li>" + customer.getName() + " (" + customer.getCity() + ") (<a href='editCustomer?id=" + customer.getId() + "'>edit</a>)</li>"); // URL重写方式 } writer.println("</ul>"); writer.println("</body>"); writer.println("</html>"); } private Customer getCustomer(int customerId) { for (Customer customer : customers) { if (customer.getId() == customerId) { return customer; } } return null; } private void sendEditCustomerForm(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setContentType("text/html"); PrintWriter writer = response.getWriter(); int customerId = 0; try { customerId = Integer.parseInt(request.getParameter("id")); // 将String类型装换成int类型 } catch (NumberFormatException e) { e.printStackTrace(); } Customer customer = getCustomer(customerId); if (customer != null) { writer.println("<html>"); writer.println("<head>"); writer.println("<title>Edit Customer</title>"); writer.println("</head>"); writer.println("<body>"); writer.println("<h2>Edit Customer</h2>"); writer.println("<form method='post' action='updateCustomer'>"); writer.println("<input type='hidden' name='id' value='" + customerId + "' />"); // 隐藏域 用于传递数据 writer.println("<table>"); writer.println("<tr>"); writer.println("<td>Name:</td>"); writer.println("<td><input name='name' value='" + customer.getName().replaceAll("'", "'") + "' /></td>"); //'编码单引号 writer.println("</tr>"); writer.println("<tr>"); writer.println("<td>City:</td>"); writer.println("<td><input name='city' value='" +customer.getCity().replaceAll("'", "'") + "' /></td>"); writer.println("</tr>"); writer.println("<tr>"); writer.println("<td colspan='2' style='text-align:right'><input type='submit' value='Update' /></td>"); writer.println("</tr>"); writer.println("<tr>"); writer.println("<td colspan='2'><a href='customer'>Customer List</a></td>"); writer.println("</tr>"); writer.println("</table>"); writer.println("</form>"); writer.println("</body>"); writer.println("</html>"); } else { writer.println("No customer found!"); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String uri = request.getRequestURI(); if (uri.endsWith("/customer")) { sendCustomerList(response); } else if (uri.endsWith("/editCustomer")) { sendEditCustomerForm(request, response); } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int customerId = 0; try { customerId = Integer.parseInt(request.getParameter("id")); // 获取隐藏域中的值 } catch (NumberFormatException e) { e.printStackTrace(); } Customer customer = getCustomer(customerId); if (customer != null) { customer.setName(request.getParameter("name")); customer.setCity(request.getParameter("city")); } sendCustomerList(response); } }
cookies(客户端技术,数据保存在客户端)