写在前面
继续学习Java Servlet,这一部分学习URL重写,虽然这些技术用的不多,而且代码也比较简单,但是我还是决定一节一节的记录。仔细看看示例代码还是有很多内容。
URL重写
URL重写是一种会话跟踪技术, 它将一个或多个token添加到URL的查询字符串中, 每个token通常为key=value形式, 如下:
url?key-1=value-1&key-2=value-2 ... &key-n=value-n
注意, URL和tokens间用问号(? ) 分割, token间用与号(&) 。
URL重写适合于tokens无须在太多URL间传递的情况下, 然而它有如下限制:
- URL在某些浏览器上最大长度为2000字符;
- 若要传递值到下一个资源, 需要将值插入到链接中, 换句话说, 静态页面很难传值;
- URL重写需要在服务端上完成, 所有的链接都必须带值, 因此当一个页面存在很多链接时, 处理过程会是一个不小的挑战;
- 某些字符, 例如空格、 与和问号等必须用base64编码;
- 所有的信息都是可见的, 某些情况下不合适。
因为存在如上限制, URL重写仅适合于信息仅在少量页面间传递, 且信息本身不敏感。
示例代码
下面将构造一个Top10Servlet类会显示最受游客青睐的10个伦敦和巴黎的景点。 信息分成两页展示, 第一页展示指定城市的5个景点, 第二页展示另外5个。 该Servlet使用URL重写来记录所选择的城市和页数。 该类扩展自HttpServlet, 并通过/top10访问。
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;
@WebServlet(name = "Top10Servlet", urlPatterns = { "/top10" })
public class Top10Servlet extends HttpServlet {
private static final long serialVersionUID = 987654321L;
private List<String> londonAttractions;
private List<String> parisAttractions;
@Override
public void init() throws ServletException {
londonAttractions = new ArrayList<String>(10);
londonAttractions.add("Buckingham Palace");
londonAttractions.add("London Eye");
londonAttractions.add("British Museum");
londonAttractions.add("National Gallery");
londonAttractions.add("Big Ben");
londonAttractions.add("Tower of London");
londonAttractions.add("Natural History Museum");
londonAttractions.add("Canary Wharf");
londonAttractions.add("2012 Olympic Park");
londonAttractions.add("St Paul's Cathedral");
parisAttractions = new ArrayList<String>(10);
parisAttractions.add("Eiffel Tower");
parisAttractions.add("Notre Dame");
parisAttractions.add("The Louvre");
parisAttractions.add("Champs Elysees");
parisAttractions.add("Arc de Triomphe");
parisAttractions.add("Sainte Chapelle Church");
parisAttractions.add("Les Invalides");
parisAttractions.add("Musee d'Orsay");
parisAttractions.add("Montmarte");
parisAttractions.add("Sacre Couer Basilica");
}
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String city = request.getParameter("city");
if (city != null && (city.equals("london") || city.equals("paris"))) {
// show attractions
showAttractions(request, response, city);
} else {
// show main page
showMainPage(request, response);
}
}
private void showMainPage(HttpServletRequest request, HttpServletResponse response)
throws ServletException,IOException {
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
writer.print("<html><head>" +
"<title>Top 10 Tourist Attractions</title>" +
"</head><body>" +
"Please select a city:" +
"<br/><a href='?city=london'>London</a>" +
"<br/><a href='?city=paris'>Paris</a>" +
"</body></html>");
}
private void showAttractions(HttpServletRequest request, HttpServletResponse response, String city)
throws ServletException, IOException {
int page = 1;
String pageParameter = request.getParameter("page");
if (pageParameter != null) {
try {
page = Integer.parseInt(pageParameter);
} catch (NumberFormatException e) {
// do nothing and retain default value for page
}
if (page > 2) {
page = 1;
}
}
List<String> attractions = null;
if (city.equals("london")) {
attractions = londonAttractions;
} else if (city.equals("paris")) {
attractions = parisAttractions;
}
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
writer.println("<html><head>" +
"<title>Top 10 Tourist Attractions</title>" +
"</head><body>");
writer.println("<a href='top10'>Select City</a> ");
writer.println("<hr/>Page " + page + "<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'/>" +
"<a href='?city=" + city +
"&page=1'>Page 1</a>");
writer.println(" <a href='?city=" + city +
"&page=2'>Page 2</a>");
writer.println("</body></html>");
}
}
函数功能说明
1.init()函数。仅当该servlet第一次被用户访问时调用,构造两个类级别的列表, londonAttractions和
parisAttractions, 每个列表有10个景点。
2.doGet()函数。该方法每次请求时被调用, 检查URL中是否包括请求参数city, 并且其值是否为“london”或“paris”, 方法据此决定是调用showAttractions方法还是showMainPage方法。
3.showMainPage()函数。向浏览器发送主页面,提供两个链接选择,一个链接显示城市London,另一个链接显示城市Paris。
4.showAttractions()函数。向浏览器发送展示城市景点的页面。
程序运行过程说明
通过URL调用Servlet之后,首先由于该servlet第一次被用户访问,会调用init()函数。
由于用户一开始访问该servlet时不带任何请求参数, 此时调用showMainPage, 该方法发送两个链接到浏览器, 每个链接都包含token: city=cityName:
"Please select a city:" +
"<br/><a href='?city=london'>London</a>" +
"<br/><a href='?city=paris'>Paris</a>" +
请注意a元素中的href属性, 该属性值包括一个问号加token city=london或city=paris. 注意, 此处为相对URL, 即URL中没有协议部分, 相对于当前页面。 因此, 若你点击了任一链接, 则会提交:
http://localhost:8080/app02a/top10?city=london
或
http://localhost:8080/app02a/top10?city=paris
到服务器上。
用户点击两个链接中的一个之后,就会向Servlet发送请求,会调用doGet()函数,而该函数会识别请求参数的city值并传递给showAttractions方法, 该方法会检查URL中是否包含page参数, 如果没有该参数或该参数值无法转换为数字, 则该方法设定page参数值为1, 并将头5个景点发给客户端。
showAttractions方法还发送了3个链接到客户端:Select City、 Page 1和Page 2。 Select City 是无参数访问servlet, Page 1和Page 2链接包括两个tokens, 即city和page:
http://localhost:8080/app02a/top10?city=cityName&page=pageNumber
若选择了伦敦, 并点击了Page 2, 则将以下URL发送给服务端:
http://localhost:8080/app02a/top10?city=london&page=2
写在后面
不同页面之间的跳转我一直很好奇,但通过这个程序发现这个程序就是通过发送href来实现不同页面的跳转的。先记录这么多。