1. request和response的实现结构
ServletRequest
接口是所有Request类的父类,HttpServletRequest
是用于http请求的Request接口。这两者都是接口,不进行具体的实现。ServletRequest
和HttpServletRequest
都属于servelt-api.jar
,为J2EE的标准规范。所有厂商要想实现具体的Request和Response,无论怎么实现都没关系,但是必须遵守J2EE标准规范。
catalina.jar
就是tomcat apache的实现方式。打开tomcat的lib目录就可以看到
catalina.jar
:2. HttpServletRequest
HttpServletRequest 接口继承自 ServletRequest 接口,其主要作用是封装 HTTP 请求消息。由于 HTTP 请求消息分为请求行、请求消息头和请求消息体三部分。因此,在 HttpServletRequest 接口中定义了获取请求行
、请求头
和请求消息体
的相关方法。
2.1 请求行
请求消息的请求行中包含请求方法、请求资源名、请求路径等信息,为了获取这些信息,
HttpServletRequest 接口定义了一系列方法:
方法声明 | 功能描述 |
---|---|
String getMethod() | 该方法用于获取 HTTP 请求消息中的请求方式(如 GET、POST 等) |
String getRequestURI() | 该方法用于获取请求行中的资源名称部分即位于 URL 的主机和端门之后、参数部分之前的部分 |
String getQueryString() | 该方法用于获取请求行中的参数部分,也就是资源路径后问号(?)以后的所有内容 |
String getContextPath() | 该方法用于获取请求 URL 中属于 Web 应用程序的路径,这个路径以 / 开头,表示相对于整个 Web 站点的根目录,路径结尾不含 /。如果请求 URL 属于 Web 站点的根目录,那么返回结果为空字符串("") |
String getServletPath() | 该方法用于获取 Servlet 的名称或 Servlet 所映射的路径 |
String getRemoteAddr() | 该方法用于获取请求客户端的 IP 地址,其格式类似于 192.168.0.3 |
String getRemoteHost() | 该方法用于获取请求客户端的完整主机名,其格式类似于 pcl.mengma.com。需要注意的是,如果无法解析出客户机的完整主机名,那么该方法将会返回客户端的 IP 地址 |
int getRemotePort() | 该方法用于获取请求客户端网络连接的端口号 |
String getLocaIAddr() | 该方法用于获取 Web 服务器上接收当前请求网络连接的 IP 地址 |
String getLocalName() | 该方法用于获取 Web 服务器上接收当前网络连接 IP 所对应的主机名 |
int getLocalPort() | 该方法用于获取 Web 服务器上接收当前网络连接的端口号 |
String getServerName() | 该方法用于获取当前请求所指向的主机名,即 HTTP 请求消息中 Host 头字段所对应的主机名部分 |
int gctServcrPort() | 该方法用于获取当前请求所连接的服务器端口号,即 HTTP 请求消息中 Host 头字段所对应的端口号部分 |
StringBuffcr getRequestURL() | 该方法用于获取客户端发出请求时的完整 URL,包括协议、服务器名、端口号、 资源路径等信息,但不包括后面的査询参数部分。注意,getRequcstURL() 方法返冋的结果是 StringBuffer 类型,而不是 String 类型,这样更便于对结果进行修改 |
2.2 请求头
当浏览器发送 Servlet 请求时,需要通过请求消息头向服务器传递附加信息,例如,客户端可以接收的数据类型、压缩方式、语言等。为此,在 HttpServletRequest 接口中定义了一系列用于获取 HTTP 请求头字段的方法
方法声明 | 功能描述 |
---|---|
String getHeader(String name) | 该方法用于获取一个指定头字段的值,如果请求消息中没有包含指定的头字段,则 getHeader() 方法返回 null;如果请求消息中包含多个指定名称的头字段,则 getHeader() 方法返回其中第一个头字段的值 |
Enumeration getHeaders(String name) | 该方法返回一个 Enumeration 集合对象,该集合对象由请求消息中出现的某个指定名称的所有头字段值组成。在多数情况下,一个头字段名在请求消息中只出现一次,但有时可能会出现多次 |
Enumeration getHeaderNames() | 该方法用于获取一个包含所有请求头字段的 Enumeration 对象 |
int getIntHeader(String name) | 该方法用于获取指定名称的头字段,并且将其值转为 int 类型。需要注意的是,如果指定名称的头字段不存在,则返回值为 -1;如果获取到的头字段的值不能转为 int 类型,则将发生 NumberFormatException 异常 |
long getDateHeader(String name) | 该方法用于获取指定头字段的值,并将其按 GMT 时间格式转换为一个代表日期/时间的长整数,该长整数是自 1970 年 1 月 1 日 0 时 0 分 0 秒算起的以毫秒为单位的时间值 |
String getContentType() | 该方法用于获取 Content-Type 头字段的值,结果为 String 类型 |
int getContentLength() | 该方法用于获取 Content-Length 头字段的值,结果为 int 类型 |
String getCharacterEncoding() | 该方法用于返回请求消息的实体部分的字符集编码,通常是从 Content-Type 头字段中进行提取,结果为 String 类型 |
2.3 获取请求参数及对象
2.3.1 获取请求参数
为了方便获取表单中的请求参数,在 HttpServletRequest 接口的父类 ServletRequest
中定义了一系列获取请求参数的方法
方法声明 | 功能描述 |
---|---|
String getParameter(String name) | 该方法用于获取某个指定名称的参数值。如果请求消息中没有包含指定名称的参数,则 getParameter() 方法返回 null。如果指定名称的参数存在但没有设置值,则返回一个空串。 如果请求消息中包含多个该指定名称的参数,则 getParameter() 方法返回第一个出现的参数值。 |
String [] getParameterValues (String name) | HTTP 请求消息中可以有多个相同名称的参数(通常由一个包含多个同名的字段元素的 form 表单生成),如果要获得 HTTP 请求消息中的同一个参数名所对应的所有参数值,那么就应该使用 getParameterValues() 方法,该方法用于返回一个 String 类型的数组。 |
Enumeration getParameterNames() | 方法用于返回一个包含请求消息中所有参数名的 Enumeration 对象,在此基础上,可以对请求消息中的所有参数进行遍历处理。 |
Map getParameterMap() | getParameterMap() 方法用于将请求消息中的所有参数名和值装入一个 Map 对象中返回。 |
2.3.1 获取请求对象
Request 对象不仅可以获取一系列数据,还可以通过属性传递数据。ServletRequest 接口中定义了一系列操作属性的方法。
方法声明 | 功能描述 |
---|---|
void setAttribute(String name, Object o) | 方法用于将一个对象与一个名称关联后存储到 ServletRequest 对象中 |
Object getAttribute(String name) | 从 ServletRequest 对象中返回指定名称的属性对象 |
void removeAttribute(String name); | 该方法用于从 ServletRequest 对象中删除指定名称的属性 |
3. HttpServletResponse
HttpServletResponse 接口继承自 ServletResponse 接口,主要用于封装 HTTP 响应消息。由于 HTTP 响应消息分为状态行、响应消息头、消息体三部分。因此,在 HttpServletResponse 接口中定义了向客户端发送响应状态码
、响应消息头
、响应消息体
的方法
3.1 响应状态码
当 Servlet 向客户端回送响应消息时,需要在响应消息中设置状态码。因此,HttpServletResponse 接口定义了两个发送状态码的方法。
1)setStatus(int status)
方法
该方法用于设置 HTTP 响应消息的状态码,并生成响应状态行。由于响应状态行中的状态描述信息直接与状态码相关,而 HTTP 版本由服务器确定,因此,只要通过 setStatus(int status)方法设置了状态码,即可实现状态行的发送。需要注意的是,在正常情况下,Web 服务器会默认产生一个状态码为 200 的状态行。
2)sendError(int sc)
方法
该方法用于发送表示错误信息的状态码。例如,404 状态码表示找不到客户端请求的资源。response 对象提供了两个重载的 sendError(int sc)方法,具体如下:
public void sendError(int code) throws java.io.IOException
public void sendError(int code,String message)throws java.io.IOException
在上面重载的两个方法中,第一个方法只发送错误信息的状态码,而第二个方法除了发送状态码以外,还可以增加一条用于提示说明的文本信息,该文本信息将出现在发送给客户端的正文内容中。
3.2 响应消息体body
由于在 HTTP 响应消息中,大量的数据都是通过响应消息体传递的,因此,ServletResponse 遵循以 I/O 流传递大量数据的设计理念。在发送响应消息体时,定义了两个与输出流相关的方法。
1)getOutputStream()
方法
该方法所获取的字节输出流对象为 ServletOutputStream 类型。由于 ServletOutputStream是OutputStream 的子类,它可以直接输出字节数组中的二进制数据。因此,要想输出二进制格式的响应正文,就需要使用 getOutputStream() 方法。
2)getWriter()
方法
该方法所获取的字符输出流对象为 PrintWriter 类型。由于 PrintWriter 类型的对象可以直接输出字符文本内容,因此,要想输出内容全部为字符文本的网页文档,则需要使用 getWriter() 方法。
注意:虽然 response 对象的 getOutputStream() 和 getWriter() 方法都可以发送响应消息体,但是,它们之间互相排斥,不可同时使用,否则会发生 IllegalStateException 异常。
3.3 响应消息头header
Servlet 向客户端发送的响应消息中包含响应头字段,由于 HTTP 协议的响应头字段有很多种,因此,HttpServletResponse 接口定义了一系列设置 HTTP 响应头字段的方法,如表 2 所示。
方法声明 | 功能描述 |
---|---|
void addHeader/setHeader(String name,String value) | 这两个方法都是用于设置 HTTP 协议的响应头字段。其中,参数 name 用于指定响应头字段的名称,参数 value 用于指定响 应头字段的值。不同的是,addHeader() 方法可以增加同名的响应头字段,而 setHeader() 方法则会覆盖同名的头字段 |
void addIntHeader/setIntHeader(String name,int value) | 这两个方法专门用于设置包含整数值的响应头,避免了使用 addHeader() 与 setHeader() 方法时需要将 int 类型的设置值转换为 String 类型的麻烦 |
void setContentType(String type) | 该方法用于设置 Servlet 输出内容的 MIME 类型,对于 HTTP 协议来说,就是设置 Content-Type 响应头字段的值。例如,如果发送到客户端的内容是 jpeg 格式的图像数据,就需要将响应头字段的类型设置为 image/jpeg。需要注意的是,如果响应的内容为文本,setContentType() 方法还可以设置字符编码,如 text/html;charset = UTF-8 |
void setLocale (Locale loc) | 该方法用于设置响应消息的本地化信息。对 HTTP 来说,就是设置 Content-Language 响应头字段和 Content-Type 头字段中的字符集编码部分。需要注意的是,如果 HTTP 消息没有设置 Content-Type 头字段,则 setLocale() 方法设置的字符集编码不会出现在 HTTP 消息的响应头中,如果调用 setCharacterEncoding() 或 setContentType() 方法指定了响应内 容的字符集编码,则 setLocale() 方法将不再具有指定字符集编码的功能 |
void setCharacterEncoding(String charset) | 该方法用于设置输出内容使用的字符编码,对 HTTP 协议来说,就是设置 Content-Type 头字段中的字符集编码部分。如果没有设置 Content-Type 头字段,则 setCharacterEncoding 方法设 置的字符集编码不会出现在 HTTP 消息的响应头中。setCharacterEncoding() 方法比 setContentType() 和 setLocale() 方法的优先权高,它的设置结果将覆盖 setContentType() 和 setLocale() 方法所设置的字符码表 |
需要注意的是,在一系列方法中,addHeader()、setHeader()、addIntHeader()、setIntHeader() 方法都用于设置各种头字段,而 setContetType()、setLoacale() 和 setCharacterEncoding() 方法用于设置字符编码,这些设置字符编码的方法可以有效解决乱码问题。
4 请求转发与重定向
转发是服务器行为,重定向是客户端行为。
4.1 请求转发forward
是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,因为这个跳转过程实在服务器实现的,并不是在客户端实现的所以客户端并不知道这个跳转动作,所以它的地址栏还是原来的地址.
//Servlet里处理get请求的方法
public void doGet(HttpServletRequest request , HttpServletResponse response){
//获取请求转发器对象,该转发器的指向通过getRequestDisPatcher()的参数设置
RequestDispatcher requestDispatcher =request.getRequestDispatcher("资源的URL");
//调用forward()方法,转发请求
requestDispatcher.forward(request,response);
}
4.2 重定向redirect
是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL.
//Servlet中处理get请求的方法
public void doGet(HttpServletRequest request,HttpServletResponse response){
//请求重定向到另外的资源
response.sendRedirect("资源的URL");
}
4.3 区别
- 重定向是两次请求,转发是一次请求,因此转发的速度要快于重定向
- 重定向之后地址栏上的地址会发生变化,变化成第二次请求的地址,转发之后地址栏上的地址不会变化,还是第一次请求的地址
- 转发是服务器行为,重定向是客户端行为。重定向时浏览器上的网址改变 ,转发是浏览器上的网址不变
- 重定向是两次request,转发只有一次请求
- 重定向时的网址可以是任何网址,转发的网址必须是本站点的网址.
- 重定向时的根目录为webapps,请求转发的根目录为本项目的根目录