JavaWeb学习总结(九)Servlet
一、HttpServletRequest 类
(一)HttpServletRequest 类的作用
每次只要有请求进入 Tomcat 服务器,Tomcat 服务器就会把请求过来的 HTTP 协议信息(包括用户提交的信息以及客户端的一些信息)解析好封装到 Request 对象中。 然后传递到 service 方法(doGet 和 doPost)中给我们使用。 客户端可通过HTML表单或在网页地址后面提供参数的方法提交数据,然后服务器通过Request对象的相关方法来获取所有请求的信息。
(二)HttpServletRequest 类的常用方法
getRequestURI()
获取请求的资源路径
getRequestURL()
获取请求的统一资源定位符(绝对路径)
getRemoteHost()
获取客户端的 ip 地址
getHeader()
获取请求头
getParameter()
获取请求的参数
getParameterValues()
获取请求的参数(多个值的时候使用)
getMethod()
获取请求的方式 GET 或 POST
setAttribute(key, value)
设置域数据
getAttribute(key)
获取域数据
getRequestDispatcher()
获取请求转发对象
案例
web.xml:
<servlet>
<servlet-name>RequestAPIServlet</servlet-name>
<servlet-class>com.fox.servlet.RequestAPIServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RequestAPIServlet</servlet-name>
<url-pattern>/requestAPI</url-pattern>
</servlet-mapping>
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class RequestAPIServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求的资源路径
System.out.println("URI => " + req.getRequestURI());
//获取请求的统一资源定位符(绝对路径)
System.out.println("URL => " + req.getRequestURL());
//获取客户端的ip地址
//在IDEA中,使用localhost访问时,得到的客户端 ip 地址是 ===>>> 127.0.0.1或者0:0:0:0:0:0:0:1(ipv6)
//在IDEA中,使用127.0.0.1访问时,得到的客户端 ip 地址是 ===>>> 127.0.0.1
//在IDEA中,使用 真实ip 访问时,得到的客户端 ip 地址是 ===>>> 真实的客户端 ip 地址
System.out.println("客户端 ip地址 => " + req.getRemoteHost());
//获取请求头
System.out.println("请求头User-Agent => " + req.getHeader("User-Agent"));
//获取请求的方式GET或POST
System.out.println( "请求的方式 => " + req.getMethod() );
}
}
(三)如何获取请求参数
web.xml:
<servlet>
<servlet-name>ParameterServlet</servlet-name>
<servlet-class>com.fox.servlet.ParameterServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ParameterServlet</servlet-name>
<url-pattern>/parameter</url-pattern>
</servlet-mapping>
html:
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://localhost:8080/WebStudy/parameterServlet" method="get">
用户名:<input type="text" name="username"><br/>
密码:<input type="password" name="password"><br/>
兴趣爱好:<input type="checkbox" name="hobby" value="cpp">C++
<input type="checkbox" name="hobby" value="java">Java
<input type="checkbox" name="hobby" value="js">JavaScript<br/>
<input type="submit">
</form>
</body>
</html>
Servlet:
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
public class ParameterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("-------------doGet------------");
// 获取请求参数
String username = req.getParameter("username");
String password = req.getParameter("password");
//参数为多个值时使用getParameterValues()
String[] hobby = req.getParameterValues("hobby");
System.out.println("用户名:" + username);
System.out.println("密码:" + password);
System.out.println("兴趣爱好:" + Arrays.asList(hobby));
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("-------------doPost------------");
// 获取请求参数
String username = req.getParameter("username");
String password = req.getParameter("password");
//参数为多个值时使用getParameterValues()
String[] hobby = req.getParameterValues("hobby");
System.out.println("用户名:" + username);
System.out.println("密码:" + password);
System.out.println("兴趣爱好:" + Arrays.asList(hobby));
}
}
1.GET请求的中文乱码问题
假如用户名输入的是中文,当客户端通过GET请求发送数据给服务器时,使用req.getParameter()获取的数据是被服务器误认为ISO-8859-1编码的,也就是说客户端发送过来的数据无论是UTF-8还是GBK,服务器都认为是ISO-8859-1,这就说明我们需要在使用req.getParameter()获取数据后,再转发成正确的编码。
例如客户端以UTF-8发送的数据,使用如下转码方式:
// 获取请求参数
String username = req.getParameter("username");
//1 先以 iso8859-1 进行编码
//2 再以 utf-8 进行解码
username = new String(username.getBytes("iso-8859-1"), "UTF-8");
2.POST请求的中文乱码问题
// 设置请求体的字符集为UTF-8,从而解决post请求的中文乱码问题
//要在获取请求参数之前调用才有效
req.setCharacterEncoding("UTF-8");
(四)请求转发
1.介绍
请求转发是指,服务器收到请求后,从一次资源跳转到另一个资源的操作叫请求转发。
案例1:
web.xml:
<servlet>
<servlet-name>Servlet1</servlet-name>
<servlet-class>com.fox.servlet.Servlet1</servlet-class>
</servlet>
<servlet>
<servlet-name>Servlet2</servlet-name>
<servlet-class>com.fox.servlet.Servlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet1</servlet-name>
<url-pattern>/servlet1</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Servlet2</servlet-name>
<url-pattern>/servlet2</url-pattern>
</servlet-mapping>
Servlet1:
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Servlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求的参数(办事的材料)查看
String username = req.getParameter("username");
System.out.println("在Servlet1(柜台1)中查看参数(材料):" + username);
// 给材料 盖一个章,并传递到Servlet2(柜台 2)去查看
req.setAttribute("key1","柜台1的章");
// 问路:Servlet2(柜台 2)怎么走
//请求转发必须要以斜杠打头,/ 斜杠表示地址为:http://ip:port/工程名/ , 映射到IDEA代码的web目录
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servlet2");
// 走向Sevlet2(柜台 2)
requestDispatcher.forward(req,resp);
//也可以写成:req.getRequestDispatcher("/servlet2").forward(req,resp);
}
}
Servlet2:
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Servlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求的参数(办事的材料)查看
String username = req.getParameter("username");
System.out.println("在Servlet2(柜台2)中查看参数(材料):" + username);
// 查看 柜台1 是否有盖章
Object key1 = req.getAttribute("key1");
System.out.println("柜台1是否有章:" + key1);
// 处理自己的业务
System.out.println("Servlet2 处理自己的业务 ");
}
}
地址栏请求到/servlet1让Servlet1处理请求,并输入参数username=mike:
2.请求转发的特点
- 浏览器地址栏没有变化
- 它们是一次请求(虽然有两个Servlet,但请求只有一次)
- 它们共享Request域中的数据(Servlet1设置的Request属性Servlet2可以获取到)
- 可以转发到WEB-INF目录下(WEB-INF是一个受服务器保护的目录,浏览器无法直接访问到此目录的内容)
- 不可以访问自身所属工程以外的资源
案例2:可以转发到WEB-INF目录下
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Servlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//请求转发必须要以斜杠打头,/ 斜杠表示地址为:http://ip:port/工程名/ , 映射到IDEA代码的web目录
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/WEB-INF/hello.html");
requestDispatcher.forward(req,resp);
}
}
案例3:不可以访问自身所属工程以外的资源
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Servlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//请求转发必须要以斜杠打头,/ 斜杠表示地址为:http://ip:port/工程名/ , 映射到IDEA代码的web目录
RequestDispatcher requestDispatcher = req.getRequestDispatcher("http://www.baidu.com");
requestDispatcher.forward(req,resp);
}
}
(五)base标签的作用
1.引入问题
假设目录如下:
c.html:
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
这是a下的b下的c.html页面<br/>
<a href="../../hello.html">跳回首页</a><br/>
</body>
</html>
hello.html:
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
这是Web下的hello.html <br/>
<a href="a/b/c.html">a/b/c.html</a><br/>
</body>
</html>
打开hello.html页面,点击链接,那么它们之间可以来回跳转:
如果用请求转发实现,效果如何呢:
web.xml:
<servlet>
<servlet-name>ForwardC</servlet-name>
<servlet-class>com.fox.servlet.ForwardC</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ForwardC</servlet-name>
<url-pattern>/forwardC</url-pattern>
</servlet-mapping>
Servlet:
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ForwardC extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getRequestDispatcher("/a/b/c.html").forward(req, resp);
}
}
hello.html:
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
这是Web下的hello.html <br/>
<!--<a href="a/b/c.html">a/b/c.html</a><br/>-->
<a href="http://localhost:8080/WebStudy/forwardC">请求转发:a/b/c.html</a><br/>
</body>
</html>
我们发现最后无法跳回首页,这是因为:
- 当我们按照第一种方法点击a标签进行跳转的时候,浏览器地址栏中的地址是
http://localhost:8080/WebStudy/a/b/c.html
,跳转回去的a标签路径是:../../hello.html
。因为所有相对路径在工作时候都会参照当前浏览器地址栏中的地址来进行跳转。那么参照后得到的地址是:http://localhost:8080/WebStudy/hello.html
,这是正确的跳转路径 - 而当我们使用请求转发来进行跳转的时候,浏览器地址栏中的地址是
http://localhost:8080/WebStudy/forwardC
,跳转回去的a标签路径是:../../hello.html
。因为所有相对路径在工作时候都会参照当前浏览器地址栏中的地址来进行跳转。那么参照后得到的地址是:http://localhost:8080/hello.html
,这是错误的路径
而base标签可以用来设置当前页面中所有相对路径工作时,参照哪个路径来进行跳转。
2.base标签使用
c.html:
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--base标签设置页面相对路径工作时参照的地址
href 属性就是参数的地址值
-->
<base href="http://localhost:8080/WebStudy/a/b/">
</head>
<body>
这是a下的b下的c.html页面<br/>
<a href="../../hello.html">跳回首页</a><br/>
</body>
</html>
这个时候再用请求转发的方式进行来回跳转就能成功了。
(六)Web 中的相对路径和绝对路径
在 javaWeb 中,路径分为相对路径和绝对路径两种:
-
相对路径
:
.
表示当前目录..
表示上一级目录资源名
表示当前目录/资源名
-
绝对路径
:
http://ip地址:端口号/工程路径/资源路径
在实际开发中,路径都使用绝对路径或base标签加上相对路径,而不简单的使用相对路径。
(七)web 中 / 斜杠的不同意义
在 web 中 / 斜杠 也是一种绝对路径。
- / 斜杠 如果被浏览器解析,得到的地址是:
http://ip地址:端口号/
,例如:<a href="/">斜杠</a>
- / 斜杠 如果被服务器解析,得到的地址是:
http://ip地址:端口号/工程路径
,例如:<url-pattern>/servlet1</url-pattern>
servletContext.getRealPath(“/”);
request.getRequestDispatcher(“/”);
- 特殊情况:
response.sendRediect(“/”);
服务器把斜杠发送给浏览器解析,得到http://ip地址:端口号/
二、HttpServletResponse 类
(一)HttpServletResponse 类的作用
HttpServletResponse 类和 HttpServletRequest 类一样。每次请求进来,Tomcat 服务器都会创建一个 Response 对象传递给 Servlet 程序去使用。HttpServletRequest 表示请求过来的信息,HttpServletResponse 表示所有响应的信息, 我们如果需要设置返回给客户端的信息,都可以通过 HttpServletResponse 对象来进行设置
(二)两个输出流的说明
- 字节流
getOutputStream();
常用于下载(传递二进制数据) - 字符流
getWriter();
常用于回传字符串(常用)
注意:两个流同时只能使用一个。 使用了字节流,就不能再使用字符流,反之亦然,否则就会报错
案例:
web.xml:
<servlet>
<servlet-name>ResponseIOServlet</servlet-name>
<servlet-class>com.fox.servlet.ResponseIOServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ResponseIOServlet</servlet-name>
<url-pattern>/responseIOServlet</url-pattern>
</servlet-mapping>
Servlet:
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class ResponseIOServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
writer.write("hello world!");
}
}
(三)响应的中文乱码解决方案
如果响应的数据有中文,那么显示时有可能出现乱码问题,因为服务器默认字符集为ISO-8859-1,我们需要将字符集修改为UTF-8:
方案一:(不推荐)
// 设置服务器字符集为 UTF-8
resp.setCharacterEncoding("UTF-8");
// 通过响应头,设置浏览器也使用 UTF-8 字符集
resp.setHeader("Content-Type", "text/html;charset=UTF-8");
方案二:(推荐)
// 它会同时设置服务器和客户端都使用 UTF-8 字符集,还设置了响应头
// 此方法一定要在获取流对象之前调用才有效
resp.setContentType("text/html; charset=UTF-8");
(四)请求重定向
1.介绍
请求重定向,是指客户端给服务器发请求,然后服务器告诉客户端说。我给你一些地址。你去新地址访问。叫请求 重定向(因为之前的地址可能已经被废弃)
案例:
web.xml:
<servlet>
<servlet-name>Response1</servlet-name>
<servlet-class>com.fox.servlet.Response1</servlet-class>
</servlet>
<servlet>
<servlet-name>Response2</servlet-name>
<servlet-class>com.fox.servlet.Response2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Response1</servlet-name>
<url-pattern>/response1</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Response2</servlet-name>
<url-pattern>/response2</url-pattern>
</servlet-mapping>
Servlet:
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Response1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("key", "value");
// 设置响应状态码302 ,表示重定向
resp.setStatus(302);
// 设置响应头,说明新的地址在哪里
resp.setHeader("Location", "http://localhost:8080/WebStudy/response2");
}
}
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Response2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(req.getAttribute("key"));
resp.getWriter().write("Response2's result!");
}
}
地址栏输入:
敲回车:
控制台显示:
证明两个Servlet实现请求重定向不共享Request域中的数据
2.请求重定向的两种方案
第一种方案:
// 设置响应状态码 302 ,表示重定向,(已搬迁)
resp.setStatus(302);
// 设置响应头,说明新的地址在哪里
resp.setHeader("Location", "http://localhost:8080/WebStudy/response2");
第二种方案(推荐使用):
resp.sendRedirect("http://localhost:8080/WebStudy/response2");
3.请求重定向的特点
- 浏览器地址栏会发生变化
- 它们是两次请求
- 不共享Request域中数据(Response1中设置的Request属性Response2不可以获取到)
- 不能访问WEB-INF下的资源
- 可以访问自身所属工程外的资源
例如:
resp.sendRedirect("http://www.baidu.com");
三、请求转发与请求重定向的区别
请求转发:在服务器端内部将请求转发给另外一个资源,浏览器只知道发出了请求并得到了响应结果,并不知道在服务器程序内部发生了转发行为,也根本不知道服务器发送的内容是从哪儿来的,所以它的地址栏中还是原来的地址。
服务器自己转发到服务器上的另一个请求(服务器行为)
- 地址栏不会变
- 可以携带参数
1.浏览器向Servlet1发出访问请求;
2.Servlet1调用forward()方法,在服务器端将请求转发给Servlet2;
3.最终由Servlet2做出响应。
请求重定向:对浏览器的请求直接作出响应,响应的结果就是告诉浏览器去重新发出对另外一个URL的访问请求,一般来说浏览器会用刚才请求的所有参数重新请求,所以session、request参数都可以获取。
服务器告诉浏览器,你去请求另外一个地址(客户端行为)
- 地址栏会变
- 不能携带参数
1.浏览器向Servlet1发出访问请求;
2.Servlet1调用sendRedirect()方法,将浏览器重定向到Servlet2;
3.浏览器向Servlet2发出请求;
4.最终由Servlet2做出响应。
举个通俗的例子:
请求转发就相当于:A找B借钱,B说没有,B去找C借,借到借不到都会把消息传递给A;
请求重定向就相当于:A找B借钱,B说没有,让A去找C借。
浏览器地址栏是否发生变化 | 几次请求 | 能否共享Request域中的数据 | 能否访问WEB-INF目录下的资源 | 能否访问自身所属工程外的资源 | |
---|---|---|---|---|---|
请求转发 | 否 | 一次 | 能 | 能 | 否 |
请求重定向 | 是 | 两次 | 否 | 否 | 能 |