第5章〓HTTP请求处理编程 HTTP请求内容 HTTP请求行 HTTP请求头 HTTP请求体 Jakarta EE请求对象类型 Jakarta EE请求对象功能和方法 Jakarta EE请求对象的生命周期 请求对象编程应用案例 Web应用工作在请求/响应模式下,要访问Web文档,需要使用浏览器通过URL地址对该文档进行HTTP请求。当Web服务器接收并处理该请求后,向请求的客户端发送HTTP响应,客户端接收到HTTP响应后进行显示,用户即可看到请求文档的内容,主要是HTML网页及其他类型文档。 在动态Web应用中,用户需要将信息输入Web系统中,通常使用HTML FORM表单和表单元素,如文本框、单选按钮、复选框、文本域等。将客户端信息提交到Web服务器端,服务器接收客户提交的数据,按具体业务进行处理,完成业务功能,如验证用户是否合法、增加新员工等。 Jakarta EE提供了HTTP请求对象,可以取得客户提交的数据。HTTP请求对象保存客户在发送HTTP请求时传递给Web服务器的所有信息,并提供相应的方法取得不同的提交信息。 确定HTTP请求中包含的数据类型和内容,并使用Jakarta EE规范中的请求对象取得HTTP请求中包含的这些数据是开发动态Web的关键。 5.1HTTP请求内容 当客户端对Web文档进行HTTP请求时,在请求中不但包含请求协议(如HTTP)、请求URL(如localhost:8080/web01/login.jsp),还包含其他客户端提交的数据。因此,在Web应用编程中,开发人员需要了解客户端发送请求中包含的数据和类型。 5.1.1HTTP请求中包含信息 当在浏览器地址中输入http://localhost:8080/web01/admin/login.jsp,对此Web JSP组件进行请求时,Web服务器会收到请求中包含的如下信息: GET /dumprequest HTTP/1.0 Host: djce.org.uk User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729) Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-cn,en-US;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7 Referer: http://www.google.cn/search?hl=zh-CN&source=hp&q=Http+request&btnG=Google+%E6%90%9C%E7%B4%A 2&aq=f&oq=Via: 1.1 cache3.dlut.edu.cn:3128 (squid/2.6.STABLE18) X-Forwarded-For: 210.30.108.201 Cache-Control: max-age=259200 Connection: keep-alive 以上请求信息按类别分为如下3部分。 (1) 请求行(Request Query)信息。请求行信息包括请求的协议HTTP、请求的方式、请求的地址URL、URI等。 (2) 请求头(Request Header)信息。请求头信息主要包含请求指示信息,用于通知Web容器请求中信息的类型、请求方式、信息的大小、客户的IP地址等。根据这些信息,Web组件可以采取不同的处理方式,实现对HTTP的请求处理。 (3) 请求体(Request body)信息。请求体信息中包含客户提交给服务器的数据,如表单提交中的数据、上传的文件等。 下面分别介绍每个组成部分的具体内容和意义。 5.1.2请求行 请求行(Request Query)信息位于请求信息的第一行,包括请求的协议、请求的地址URL、请求的方式等。请求行的案例代码如下: GET https://www.baidu.com/content-search.xml HTTP/1.1 其中,GET是请求方法,https://www.baidu com/是URL地址,HTTP/1.1指定了协议版本。 不同的HTTP版本能够使用的请求方法也不同,具体介绍如下。 (1) HTTP 0.9: 只有基本的文本GET功能。 (2) HTTP 1.0: 具有完善的请求/响应模型,并将协议补充完整,定义了GET、POST和HEAD 3种请求方法。 (3) HTTP 1.1: 在HTTP 1.0基础上进行更新,新增了5种请求方法: OPTIONS、PUT、DELETE、TRACE和CONNECT。 (4) HTTP 2.0: 请求/响应首部的定义基本没有改变,只是所有首部键必须全部小写,而且请求行要独立为:method、:scheme、:host、:path等键值对。 不同请求方式的意义如下。 (1) GET: 请求指定的页面信息,并返回实体主体。 (2) POST: 向指定资源提交数据请求处理(如提交表单或者上传文件),数据被包含在请求体中。POST 请求可能会导致新的资源的建立和已有资源的修改。 (3) HEAD: 类似于GET请求,只不过返回的响应中没有具体内容,用于获取报头。 (4) PUT: 这种请求方式下,从客户端向服务器传送的数据取代指定的文档内容。 (5) DELETE: 请求服务器删除指定的页面。 (6) OPTIONS: 允许客户端查看服务器的性能。 (7) TRACE; 回显服务器收到的请求,主要用于测试或诊断。 当Web客户向服务器发出HTTP请求时,请求头首先被发送到服务器端,服务器根据请求行的URL信息定位指定的文档,如果文档不存在,则服务器给客户端发送404错误信息,根据请求的方式,调用文档的指定的处理方法,如Servlet的doGet、doPost、doPut、doDelete等。 5.1.3请求头 当Web客户向服务器发出HTTP请求时,发送请求行信息后,请求头信息被发送到服务器端,告知服务器此请求中包含的指示信息,服务器以便根据这些指示信息采取不同的处理。请求头中主要是客户端的一些基础信息,其中关键信息如下。 (1) accept: 表示当前浏览器可以接受的文件类型。假设这里有 image/webp,表示当前浏览器可以支持 webp 格式的图片,那么当服务器给当前浏览器下发送 webp类型图片时,可以更省流量。 (2) acceptencoding: 表示当前浏览器可以接收的字符编码。如果服务器发送的响应数据不是浏览器可接收的字符编码,就会显示乱码。 (3) acceptlanguage: 表示当前客户端使用的语言,也包含客户所在的国家或地区。Web服务器端应用要实现国际化,可根据此请求头信息给客户端发送对应的语言文本,如给中国大陆客户发送中文简体、给美国客户发送英语。 (4) Cookie: 存储和用户相关的信息,每次用户在向服务器发送请求时会带上Cookie。例如,用户在一个网站上登录之后,下次访问时就不用再登录,就是因为登录成功的 token 放在了 Cookie 中; 另外,随着每次请求发送给服务器,服务器就会知道当前用户已登录。 (5) useragent: 表示浏览器的类型和版本信息。当服务器收到浏览器的请求后,通过该请求头知道浏览器的类型和版本,进而知道支持的语言版本(如JavaScript、HTML、CSS等),开发者可以针对此类型浏览器编写对应版本的代码。 请求头中也可以包含客户端自定义的信息,通常前端框架(如Vue、Angular等)都可以在请求头中加入自己定义的name和value值。表51列出了W3C规范中规定的常用HTTP请求头标记和说明。 表51W3C规范中规定的HTTP请求头标记和说明 头标记说明包含的值示例 UserAgent客户端的类型(包含浏览器名称)LIICello/1.0 libwww/2.5 Accept浏览器可接收的MIME类型各种标准的MIME类型 AcceptCharset浏览器支持的字符编码字符编码,如ISO88591 AcceptEncoding浏览器知道如何解码的数据编码类型xcompress; xzipAcceptLanguage 浏览器指定的语言如en:English Connection是否使用持续连接KeepAlive: 持续连接 ContentLength使用POST方法提交时,传递数据的字节数2352 Cookie保存的Cookie对象userid=9001 Host主机和端口192.168.100.3:8080 在Jakarta EE Web组件的Servlet和JSP中,可以使用请求对象的方法读取这些请求头的信息,进而进行相应的处理。5.2.4小节将讲述请求头信息的取得方法。 5.1.4请求体 每次HTTP请求时,在请求头后面会有一个空行,之后是请求中包含的提交数据,即请求体。请求体通常是表单元素中输入的数据,所有Web应用都需要客户输入数据。登录淘宝、京东的账号和密码信息,增加新产品的信息数据等,这些数据通常包含在请求体中。 不是所有的请求都有请求体,当为GET请求时,则没有请求体,因为请求数据直接附加在请求文档的URL地址中,请求体作为URL的一部分发送到Web服务器。例如,http://localhost:8080/web01/login.do?id=9001&pass=9001,这时请求体为空,因为提交数据直接在URL中,作为请求行的一部分传输到Web服务器,通过解析URL的QueryString部分就可以得到提交的参数数据。这种方式对提交的数据大小有限制,不同浏览器会有所不同,如IE为2083字节。GET请求时,数据会出现在URL中,保密性差,因此在实际项目编程中要尽量避免在URL地址栏中传递请求数据。 POST请求时,请求体数据单独打包为数据块,通过Socket直接发送到Web服务器端,数据不会在地址栏中出现,因此可以提交数据的类型和大小基本没有限制,可以包括二进制文件,可以实现文件上传功能。原则上POST请求对提交的数据没有大小限制,但为了应用需要,一般在编程时会对文件的大小加以限制。 Jakarta EE Web组件(如Serlvet、JSP、Filter、Listener等)规范中都定义了如何取得请求体数据的方法,在5.2节中会详细说明这些方法和编程应用。 5.2Jakarta EE请求对象 为取得客户HTTP请求中包含的信息,Jakarta EE的Web组件规范定义了请求对象接口规范,通过实现该接口的请求对象可以取得请求中包含的所有信息,包括请求行、请求头和请求体。 5.2.1请求对象接口类型与生命周期 1. 请求对象接口类型 Jakarta EE规范中,通用请求对象(不依赖请求协议的情况)要实现接口: jakarta.servlet.ServletRequest 而本书重点介绍的是HTTP下工作的请求对象,要实现接口: jakarta.servlet.http.HttpServletRequest 这两个接口的所有方法和属性可参阅Eclipse Jakarta EE API文档。 2. 请求对象生命周期 在Java Web组件开发中,不需要开发者自己创建Servlet或JSP使用的请求对象,它们由Web容器自动创建,并传递给Servlet和JSP的服务方法doGet、doPost、doPut、doDelete及doHead等。在这些HTTP请求处理方法中可以直接使用请求对象,调用其方法,取得客户端提交的数据。 (1) 创建请求对象。每次Web服务器接收到HTTP请求时,会自动创建实现HttpServletRequest接口的对象。具体的请求对象实现类由Jakarta EE服务器厂家实现,不同的服务器产品(如Tomcat、GlassFish、WebLogic等)实现请求对象接口的实现类不一定相同,但开发者不需要了解具体的请求对象的实现类型,只需掌握请求对象接口的方法即可。创建请求对象后,Web服务器将请求行、请求头和请求体信息存入请求对象,并自动把请求对象传递给请求的Web组件,如Servlet、JSP等。这些Web组件可以通过请求对象的方法取得这些请求信息,即客户端用户提交的数据。 (2) 销毁请求对象。当Web服务器处理HTTP请求,向客户端发送HTTP响应结束后,会自动销毁请求对象,保存在请求对象中的数据随即丢失。当下次请求时新的请求对象又会创建,重新开始请求对象新的生命周期。 5.2.2请求对象的功能与方法 Jakarta EE提供的HttpServletRequest请求对象用于取得HTTP请求中包含的请求行、请求头和请求体的数据信息。HttpServletRequest接口定义的方法分类如下。 (1) 取得请求行的数据。 (2) 取得请求头信息。 (3) 取得请求体中包含的提交参数数据,包含表单元素或地址栏URL的参数。 (4) 取得服务器端的相关信息,如服务器的IP等。 (5) 取得请求对象存储的属性信息。 请求对象除了可以取得客户端的各种信息外,还提供了作为传递数据的容器的方法,用于在Web组件间传递数据。该功能在Web开发中使用得非常多,在7.2.3小节中会详细讲解请求对象的此项功能和方法。 5.2.3取得请求行方法 请求对象接口HttpServletRequest提供了如下方法,用于取得请求行中包含的数据。 (1) String getProtocol(): 取得使用的请求协议。 (2) String getMethod(): 取得请求的方式,返回字符串类型的GET、POST、PUT、DELETE等。 (3) StringBuffer getRequestURL(): 取得请求的URL地址。需要注意的是,其返回类型不是String,而是StringBuffer,需要调用其toString()方法将其转换为String类型再显示。 (4) String getRequestURI(): 取得请求的URI地址。URI地址是Web站点内的地址,从Web应用的起始站点名开始,假如Web的站点起始路径为/jakartaweb05, Web文档JSP目录是/employee,文件名是list.jsp, 则URI地址是/jakartaweb05/employee/list.jsp。而URL地址是包含协议、IP地址和端口的全地址,上面JSP文件的URL地址如下: http://localhost:8080/jakartaweb05/employee/list.jsp 从URL地址可以看出,URL包含URI,URI是URL的一部分,即: URL=协议://IP:端口/URI 测试取得请求行的Servlet代码如程序51所示。 程序51RequestQueryGetting.java取得请求行的测试Servlet类代码。 package com.city.oa.servlet; import jakarta.servlet.ServletConfig; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebInitParam; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * 取得请求行的测试Servlet */ @WebServlet(urlPatterns = { "/requestquery/get.do" }) public class RequestQueryGetting extends HttpServlet { private static final long serialVersionUID = 1L; /** * GET请求处理 */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //取得请求协议 String protocol=request.getProtocol(); //取得请求方式 String method=request.getMethod(); //取得请求地址URL String url=request.getRequestURL().toString(); //取得请求地址URI String uri=request.getRequestURI(); //发送响应数据 response.setContentType("text/html"); response.setCharacterEncoding("UTF-8"); PrintWriter out=response.getWriter(); out.println("