2004 Marty Hall 客户请求的处理 : HTTP 请求报头 JSP, Servlet, & Struts Training Courses: http://courses.coreservlets.com Available in US, China, Taiwan, HK, and Worldwide 2 JSP and Servlet Books from Sun Press: http://www.coreservlets.com Available in English, Chinese (simplified and traditional script), and 12 other languages 议程 HTTP 请求报头的读取 制作所有请求报头的表格 了解各种请求报头 通过压缩页面减少下载时间 区分不同的浏览器类型 3 JSP/servlet/Struts/JSF training: http://www.coreservlets.com
一个典型的 HTTP 请求 GET /servlet/search?keywords=servlets+jsp HTTP/1.1 Accept: image/gif, image/jpg, */* Accept-Encoding: gzip Connection: Keep-Alive Cookie: userid=id456578 Host: www.somebookstore.com Referer: http://www.somebookstore.com/findbooks.html User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0) 理解 HTTP 无疑会有助于更有效地使用 servlet 和 JSP 4 JSP/servlet/Struts/JSF training: http://www.coreservlets.com 请求报头的读取 (HttpServletRequest 中的方法 ) 通用方法 getheader ( 报头名对大小写不敏感 ) getheaders getheadernames 专用方法 getcookies getauthtype 和 getremoteuser getcontentlength getcontenttype getdateheader getintheader 获取相关信息的方法 getmethod, getrequesturi, getquerystring, getprotocol 5 JSP/servlet/Struts/JSF training: http://www.coreservlets.com
缺失报头的检查 HTTP 1.0 所有请求报头都是可选的 HTTP 1.1 仅 Host 是必需的 结论 在试图使用由 request.getheader 返回的值之前一定要检查它是否为 null String val = request.getheader("some-name"); if (val!= null) { 6 JSP/servlet/Struts/JSF training: http://www.coreservlets.com 制作所有请求报头的表格 public class ShowRequestHeaders extends HttpServlet { public void doget(httpservletrequest request, HttpServletResponse response) throws ServletException, IOException { out.println (doctype + "<HTML>\n" + "<HEAD><TITLE>"+title+"</TITLE></HEAD>\n"+ "<BODY BGCOLOR=\"#FDF5E6\">\n" + "<H1 ALIGN=\"CENTER\">" + title + "</H1>\n" + "<B>Request Method: </B>" + request.getmethod() + "<BR>\n" + "<B>Request URI: </B>" + request.getrequesturi() + "<BR>\n" + "<B>Request Protocol: </B>" + request.getprotocol() + "<BR><BR>\n" + 7 JSP/servlet/Struts/JSF training: http://www.coreservlets.com
制作所有请求报头的表格 ( 续 ) "<TABLE BORDER=1 ALIGN=\"CENTER\">\n" + "<TR BGCOLOR=\"#FFAD00\">\n" + "<TH>Header Name<TH>Header Value"); Enumeration headernames = request.getheadernames(); while(headernames.hasmoreelements()) { String headername = (String)headerNames.nextElement(); out.println("<tr><td>" + headername); out.println(" <TD>"+request.getHeader(headerName)); out.println("</table>\n</body></html>"); /** Since this servlet is for debugging, have it * handle GET and POST identically. */ public void dopost(httpservletrequest request, HttpServletResponse response) throws ServletException, IOException { doget(request, response); 8 JSP/servlet/Struts/JSF training: http://www.coreservlets.com 制作所有请求报头的表格 ( 结果 1) 9 JSP/servlet/Struts/JSF training: http://www.coreservlets.com
制作所有请求报头的表格 ( 结果 2) 10 JSP/servlet/Struts/JSF training: http://www.coreservlets.com 常见 HTTP 1.1 请求报头 Accept 标示浏览器能够处理的 MIME 类型 可以向不同的客户程序发送不同的内容 例如,PNG 文件拥有较好的压缩特性但并不被浏览器广泛支持 servlet 可以检查浏览器是否支持 PNG 文件, 如果支持则发送 <IMG SRC="picture.png"...>, 否则发送 <IMG SRC="picture.gif"...> 警告 : 在点击 Refresh 按钮时,IE 会错误在设置这个报头 但在最初的请求中, 对这个报头的设置是正确的 Accept-Encoding 标示浏览器能够处理的编码 ( 如 gzip 或 compress) 参见随后的例子 11 JSP/servlet/Struts/JSF training: http://www.coreservlets.com
常见 HTTP 1.1 请求报头 ( 续 ) Authorization 用于发送受密码保护的页面的用户身份验证信息 参见随后的例子 应该尽可能使用 HTML 表单来发送用户名 / 密码, 在会话对象中存储信息 ; 而不是这个报头 因为这种方式会弹出一个小而简练的对话框, 许多用户不熟悉它 服务器拥有其他高级的方式来设置受密码保护的页面, 无须显式地在 servlet 中编写相关的代码 详细信息, 参见 More Servlets and JavaServer Pages 的第 7 章和第 8 章, 以及 www.moreservlets.com 12 JSP/servlet/Struts/JSF training: http://www.coreservlets.com 常见 HTTP 1.1 请求报头 ( 续 ) Connection 在 HTTP 1.0 中,keep-alive 表示浏览器能够处理持续性连接 在 HTTP 1.1 中, 持续性连接是默认的 持续性连接表示 : 对于来自同一客户的相隔时间很近的请求 ( 如与同一页面相关联的图像, 或具有框架的页面中的不同单元 ), 服务器可以重用已有的 socket servlet 并不能单方面地完成这项工作 ; 要使持续性连接的使用成为可能, 它们能够做的就是给予服务器足够的信息 因此, 它们应该用 setcontentlength 设置 Content- Length( 输出的长度使用 ByteArrayOutputStream 来确定 ) Cookie 给出之前发送到客户端的 cookie 使用 getcookies, 不要使用 getheader 参见书中的内容以及后面的课程 13 JSP/servlet/Struts/JSF training: http://www.coreservlets.com
常见 HTTP 1.1 请求报头 ( 续 ) Host 标示最初的 URL 中给出的主机 在 HTTP 1.1 中这是一个必需的报头 如果您在编写定制的 HTTP 客户程序 ( 比如本书使用的 WebClient), 或者使用 HTTP/1.1 版本 telnet 到服务器, 那么了解这一点十分重要 If-Modified-Since 标示仅当页面在指定的日期之后被改动过时客户才希望获得该页面 不要直接处理这种情况, 而应实现 getlastmodified 参见本书中的 lottery-number 示例 (Core Servlets & JSP (2nd Ed) 第三章 ) 14 JSP/servlet/Struts/JSF training: http://www.coreservlets.com 常见 HTTP 1.1 请求报头 ( 续 ) Referer 引用页面的 URL 对于流量跟踪有作 ; 许多服务器都记录这项信息 也可以用来让用户设置相应的参数, 然后再返回到跳转过来的页面 很容易被哄骗 ; 所以不要将它作为确定付费网站显示了多少次您的标题广告的唯一途径 某些浏览器 (Opera), 广告过滤器 (Web Washer), 和个人防火墙 (Norton) 会屏蔽这个报头 参见书中的例子 User-Agent 最好用来鉴定客户的类型 浏览器 I-mode 的移动电话等 对于 Web 应用, 请尽可能使用其他报头 同样, 也很容易被哄骗 参见后面的例子 15 JSP/servlet/Struts/JSF training: http://www.coreservlets.com
发送经过压缩的 Web 页面 Dilbert used with permission of United Syndicates Inc. 16 JSP/servlet/Struts/JSF training: http://www.coreservlets.com 发送经过压缩的 Web 页面 : GzipUtilities.java public class GzipUtilities { public static boolean isgzipsupported (HttpServletRequest request) { String encodings = request.getheader("accept-encoding"); return((encodings!= null) && (encodings.indexof("gzip")!= -1)); public static boolean isgzipdisabled (HttpServletRequest request) { String flag = request.getparameter("disablegzip"); return((flag!= null)&& (!flag.equalsignorecase("false"))); public static PrintWriter getgzipwriter (HttpServletResponse response) throws IOException { return(new PrintWriter (new GZIPOutputStream (response.getoutputstream()))); 17 JSP/servlet/Struts/JSF training: http://www.coreservlets.com
发送经过压缩的 Web 页面 : LongServlet.java public class LongServlet extends HttpServlet { public void doget(httpservletrequest request, HttpServletResponse response) throws ServletException, IOException { response.setcontenttype("text/html"); // Change the definition of "out" depending on // whether or not gzip is supported. PrintWriter out; if (GzipUtilities.isGzipSupported(request) &&!GzipUtilities.isGzipDisabled(request)) { out = GzipUtilities.getGzipWriter(response); response.setheader("content-encoding", "gzip"); else { out = response.getwriter(); 18 JSP/servlet/Struts/JSF training: http://www.coreservlets.com 发送经过压缩的 Web 页面 : LongServlet.java ( 续 ) out.println (doctype + "<HTML>\n" + "<HEAD><TITLE>" + title + "</TITLE></HEAD>\n" + "<BODY BGCOLOR=\"#FDF5E6\">\n" + "<H1 ALIGN=\"CENTER\">" + title + "</H1>\n"); String line = "Blah, blah, blah, blah, blah. " + "Yadda, yadda, yadda, yadda."; for(int i=0; i<10000; i++) { out.println(line); out.println("</body></html>"); out.close(); 19 JSP/servlet/Struts/JSF training: http://www.coreservlets.com
发送经过压缩的页面 : 结果 未经压缩 (28.8K modem), Netscape 和 Internet Explorer: > 50 seconds 经过压缩 (28.8K modem), Netscape 和 Internet Explorer: < 5 seconds 注意 : 在处理这些基准数据时要小心 20 JSP/servlet/Struts/JSF training: http://www.coreservlets.com 区分不同的浏览器类型 仅在必需时才使用 User-Agent 否则, 我们最终的代码不得不维护浏览器各个版本及其功能的表格, 导致代码难以维护 检查 null HTTP 1.1 规范并不要求这个报头, 某些浏览器允许用户禁止这个报头 ( 如 Opera), 一些定制客户程序甚至有可能根本就不使用这个报头 ( 如 Web spiders 或 link verifiers) 如果要区分 Netscape 和 Internet Explorer, 请检查 MSIE, 而非 Mozilla 在这个报头的开始,Netscape 和 Internet Explorer 都设置 Mozilla 这是为了 JavaScript 的兼容性 要注意, 这个报头可以伪造 如果某个客户伪造这个报头, 服务器根本无从察觉 21 JSP/servlet/Struts/JSF training: http://www.coreservlets.com
区分不同的浏览器类型 ( 代码 ) public class BrowserInsult extends HttpServlet { public void doget(httpservletrequest request, HttpServletResponse response) throws ServletException, IOException { response.setcontenttype("text/html"); PrintWriter out = response.getwriter(); String title, message; // Assume for simplicity that Netscape and IE are // the only two browsers. String useragent = request.getheader("user-agent"); if ((useragent!= null) && (useragent.indexof("msie")!= -1)) { title = "Microsoft Minion"; message = "Welcome, O spineless slave to the " + "mighty empire."; else { title = "Hopeless Netscape Rebel"; message = "Enjoy it while you can. " + "You <I>will</I> be assimilated!"; 22 JSP/servlet/Struts/JSF training: http://www.coreservlets.com 区分不同的浏览器类型 ( 结果 ) 23 JSP/servlet/Struts/JSF training: http://www.coreservlets.com
小结 servlet 的许多任务只能利用来自于浏览器的 HTTP 报头来完成 访问任意报头使用 request.getheader 要记住检查是否为 null cookie, 授权信息, 内容长度以及内容类型都拥有快捷的方法 直接读取的最重要报头 Accept Accept-Encoding Connection Referer User-Agent 24 JSP/servlet/Struts/JSF training: http://www.coreservlets.com 2004 Marty Hall 问题? JSP, Servlet, & Struts Training Courses: http://courses.coreservlets.com Available in US, China, Taiwan, HK, and Worldwide 25 JSP and Servlet Books from Sun Press: http://www.coreservlets.com Available in English, Chinese (simplified and traditional script), and 12 other languages