1.HttpServletRequest类
a)HttpServletRequest类有什么作用?
每次只要请求进入了Tomcat服务器,Tomcat服务器就会把请求过来的HTTP协议信息解析好封装到Request对象中。然后传递到Service方法中(doget和dopost)给我们使用。然后我们可以通过HttpServletRequest对象,获取到所有请求的信息。
通过Request对象进行的常用操作:
- 获取请求行信息:请求方式,url和HTTP版本。
- 获取请求头信息:浏览器类型,ip地址等。
- 获取请求参数:url后面拼接的参数或者请求体中提交的参数;
b)HttpServletRequest类的常用方法
getRequestURI() | 获取请求的资源路径 |
---|---|
getRequestURL() | 获取请求的统一资源定位符(绝对路径) |
getRemoteHost() | 获取客户端的ip地址 |
getHeader() | 获取请求头 |
getParameter() | 获取请求的参数 |
getParameterValues() | 获取请求的参数(多个值的时候使用) |
getMethod() | 获取请求的方式GET或POST |
setAttribute(key,value) | 设置域数据 |
getAttribute(key) | 获取域数据 |
getRequestDispatcher() | 获取请求转发对象 |
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地址
System.out.println("客户端 ip地址 => "+req.getRemoteHost());
//获取请求头
System.out.println("请求头User-Agent ==>> "+req.getHeader("User-Agent"));
//请求的方式
System.out.println("请求的方式 ==>>"+req.getMethod());
}
}
当获取ip地址的时候,localhost改为我们自己主机的ip地址时,获取到的IP地址也会是自己的ip。
![在这里插入图片描述](https://img-blog.csdnimg.cn/705ec7dc66ed40a4b7fd40bcb94103e5.png)
c).那我们如何获取请求的参数呢?
我们先建立一个表单:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://localhost:8080/servlet_03_war/parameter" 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>
在建立一个ParameterServlet类:
public class ParameterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求的参数
String username=req.getParameter("username");
String password =req.getParameter("password");
// String hobby=req.getParameter("hobby");//只能返回一个值多选的时候只能返回一个C++
String[] hobby=req.getParameterValues("hobby");
System.out.println("用户名:"+username);
System.out.println("密码:"+password);
System.out.println("爱好:"+ Arrays.asList(hobby));
}
}
启动服务器
点击提交
我们会发现爱好这里我们只获取到一个,但是我们明明勾选了两个,所以我们不能用getParameter,我们要用数组存进去,也就是req.getParameterValues().
现在我们使用post请求看一下:
当我们使用的post请求,我们发现了一个问题,就是当我们输入中文用户名的时候,出现了乱码。
我们就要使用setCharacterEncoding()方法就行了,那post为啥会出现乱码呢?
乱码的原因:POST 提交的数据在请求体中,其所使用的编码格式时页面一致(即 utf-8)。request 对象接收到数据之后,会将数据放到 request 缓冲区,缓冲区的默认字符集是 ISO-8859-1(该字符集不支持中文),两者使用的字符集不一致导致乱码。
扩展:
:::warning
Get请求
乱码的原因:Get 请求将请求数据附加到 URL 后面作为参数,浏览器发送文字时采用的编码格式与页面编码保持一致(utf-8)。如果 Tomcat 没有设置字符集,接收 URL 时默认使用 ISO-8859-1 进行解码,ISO-8859-1 不兼容中文,无法正确解码,导致出现乱码。
:::
Servlet请求转发
请求转发属于服务器行为。容器接收请求后,Servlet 会先对请求做一些预处理,然后将请求传递给其他 Web 资源,来完成包括生成响应在内的后续工作。
请求转发的工作原理
请求转发的特点
请求转发具有以下特点:
- 请求转发不支持跨域访问,只能跳转到当前应用中的资源。
- 请求转发之后,浏览器地址栏中的 URL 不会发生变化,因此浏览器不知道在服务器内部发生了转发行为,更无法得知转发的次数。
- 参与请求转发的 Web 资源之间共享同一 request 对象和 response 对象。
- 由于 forward() 方法会先清空 response 缓冲区,因此只有转发到最后一个 Web 资源时,生成的响应才会被发送到客户端。
:::
我们来看一下实例把:
我们首先创建Servlet1类
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中查看
req.setAttribute("key1","柜台1的章");
//问路:Servlet2怎么走?
/**
* 请求转发必须以斜杆打头,/斜杆表示地址为http://ip:port/工程名/,映射到IDEA代码的web目录
*/
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servlet2");
//走向Servlet2
requestDispatcher.forward(req,resp);
}
}
我们首先获取到请求的参数,相当于我们把材料给柜台1人员看,然后柜台1(Servlet1)的人员进行盖章且传到柜台2(Servlet2)中去查看。
这里我们用到了RequestDispatcher 接口,利用RequestDispatcher对象可以把请求转发给其他的Web资源。
其中,请求转发必须以斜杆打头,/斜杆表示地址为http://ip:port/工程名/,映射到IDEA代码的web目录。
返回类型 | 方法 | 功能描述 |
---|---|---|
void | forward(ServletRequest request,ServletResponse response) | 用于将请求转发给另一个 Web 资源。该方法必须在响应提交给客户端之前被调用,否则将抛出 IllegalStateException 异常 |
void | include(ServletRequest request,ServletResponse response) | 用于将其他的资源作为当前响应内容包含进来 |
然后我们再创建Servlet2类:
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);
//查看是否有盖章
Object key1 = req.getAttribute("key1");
System.out.println("柜台1是否有章"+key1);
//处理自己的业务
System.out.println("Servlet2处理自己的业务");
}
}
柜台2(Servlet2)就是进行查看章且输出自己的业务。
web.xml配置
<servlet>
<servlet-name>Servlet1</servlet-name>
<servlet-class>com.example.servlet03.Servlet1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet1</servlet-name>
<url-pattern>/servlet1</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Servlet2</servlet-name>
<servlet-class>com.example.servlet03.Servlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet2</servlet-name>
<url-pattern>/servlet2</url-pattern>
</servlet-mapping>
启动服务器:
Servlet-base标签的作用
我们先来说一下实例吧。servlet-base标签能带来什么作用?
我们先建一个index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
这是Web下的index.html<br/>
<a href="a/b/c.html">跳到c.html<a/><br/>
</body>
</html>
然后我们再跳转到a包下面的b包下的c.html。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
这是c.html页面<br/>
<a href="../../index.html">跳回到首页</a>
</body>
</html>
这样我们就能再两个页面之间跳转。
但是我们现在现在需要进行请求转发,我们建立一个ForwardC类。
public class ForwardC extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("请求转发");
req.getRequestDispatcher("a/b/c.html").forward(req,resp);
}
}
在index.html上面加上跳转。
我们发现我们的页面跳转到c.html之后,然后再跳转回首页时,却失败了。这是为啥那?
所有的相对路径在工作时候都会参照当前浏览器地址栏中的地址来进行跳转。
:::
就好比http://localhost:8080/servlet_03_war/a/b/c.html和…/…/index.html
…/和b目录抵消,…/又和a目录抵消,最后转成http://localhost:8080/servlet_03_war/index.html
这个就是我们的首页的链接。这也是第一个案例可以跳转回去的原因。
当我们使用请求转发进行跳转的时候,跳转路径是:http://localhost:8080/servlet_03_war/forwardC,而不是http://localhost:8080/servlet_03_war/a/b/c.html,所以我们的工作路径变了。跳转回去还是…/…/index.html
最后就是http://localhost:8080/index.html,这个就是路径,错误的路径。是因为相对路径参照工作路径的地址发生了变化。这时候我们就需要base标签了。
我们设置跳回首页的参照工作路径为http://localhost:8080/servlet_03_war/a/b/c.html。那他每次跳转的时候都会参照这个工作路径去进行跳转,这样就完成了。那在哪里加base标签呢?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- base标签设置页面的相对路径工作时参照的地址
href 属性就是参数的地址值-->
<base href="http://localhost:8080/servlet_03_war/a/b/c.html">
</head>
<body>
这是c.html页面<br/>
<a href="../../index.html">跳回到首页</a>
</body>
</html>
这个我们用请求转发进行跳转的时候就没问题了。
web中/斜杆的不同意义
HttpServletResponse类
a)HttpServlletResponse类的作用
HttpServletResponse类和HttpServletRequest类一样。每次请求进来,Tomcat服务器都会创建一个Response对象传递给Servlet程序去使用。
HttpServletRequest表示请求过来的信息,HttpServletResponse表示所有响应的信息,我们如果需要设置返回给客户端的信息,都可以通过HttpServletResponse对象来进行设置。
b)两个输入流的说明
返回值类型 | 方法 | 描述 |
---|---|---|
ServletOutputStream | getOutputStream() | 用于获取字节输出流对象。 |
PrintWriter | getWriter() | 用于获取字符输出流对象。 |
注意:getOutputStream() 和 getWriter() 方法互相排斥,不可同时使用,否则会发生 IllegalStateException 异常。
c).如何给客户端回传数据
public class ResponseIOServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter writer=resp.getWriter();
writer.write("responses content!");
}
}
d)response 中文乱码问题
我们进行中文输入时,我们会发现出现了中文乱码的问题。
PrintWriter writer=resp.getWriter();
writer.write("中国人!");
:::warning
乱码原因:
通过字符流输出的内容是存放在 response 缓冲区的,response 缓冲区的默认字符集是 ISO-8859-1,该字符集不支持中文。
:::
解决方案:
//设置服务器字符集为UTF-8
resp.setCharacterEncoding("UTF-8");
//通过响应头,设置浏览器也使用UFT-8字符集
resp.setHeader("Content-Type","text/html;charset=UTF-8");
PrintWriter writer=resp.getWriter();
writer.write("中国人!");
第一个是在服务器里面设置为中文(UTF-8)编码,第二个则是在浏览器里面通过响应头设置中文字符集。
方法二:使用setContentType()方法,但是这个方法必须在获取流之前调用才有效。
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("编程帮 www.biancheng.net");
e)请求重定向
重定向属于客户端行为。服务器在收到客户端请求后,会通知客户端浏览器重新向另外一个 URL 发送请求,这称为请求重定向。它本质上是两次 HTTP 请求,对应两个 request 对象和两个 response 对象。
重定向的工作流程
:::warning
- 用户在浏览器中输入 URL,请求访问服务器端的 Web 资源。
- 服务器端的 Web 资源返回一个状态码为 302 的响应信息,该响应的含义为:通知浏览器再次发送请求,访问另一个 Web 资源(在响应信息中提供了另一个资源的 URL)。
- 当浏览器接收到响应后,立即自动访问另一个指定的 Web 资源。
- 另一 Web 资源将请求处理完成后,由容器把响应信息返回给浏览器进行展示。
:::
话不多说!实例:
我们建立两个类分别是Response1类和Response2类。
public class Response1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("到此一游 response1");
//设置响应状态码302
resp.setStatus(302);
resp.setHeader("Location","http://localhost:8080/servlet_03_war/response2");
}
}
public class Response2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("response2!!!");
}
}
当我们跳转到response1(Response1)的时候会自动跳到Resonse2的。
我们启动服务器看看。
我们来看看response1的状态码
response2的状态码。
:::warning
重定义特点:
- 浏览器地址栏会发生变化
- 两次请求
- 不共享Request域中数据
- 不能访问WEB-INF下的资源
- 可以访问工程外的资源
:::
我们还可以使用一种更简便的方法:sendRedirect()方法
public class Response1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("到此一游 response1");
resp.sendRedirect("http://localhost:8080/servlet_03_war/response2");
}
}
f)Servlet Cookie的使用
HTTP(超文本传输协议)是一个基于请求与响应模式的无状态协议。
无状态主要指 2 点:
- 协议对于事务处理没有记忆能力,服务器不能自动维护用户的上下文信息,无法保存用户状态;
- 每次请求都是独立的,不会受到前面请求的影响,也不会影响后面的请求。
当浏览器发送 HTTP 请求到服务器时,服务器会响应客户端的请求,但当同一个浏览器再次发送请求到该服务器时,服务器并不知道它就是刚才那个浏览器,即 HTTP 协议的请求无法保存用户状态。
通常情况下,用户通过浏览器访问 Web 应用时,服务器都需要保存和跟踪用户的状态。例如,用户在某购物网站结算商品时,Web 服务器必须根据请求用户的身份,找到该用户所购买的商品。由于 HTTP 协议是无协议的,无法保存和跟踪用户状态,所以需要其他的方案来解决问此题,它就是会话技术。
会话技术
从打开浏览器访问某个网站,到关闭浏览器的过程,称为一次会话。会话技术是指在会话中,帮助服务器记录用户状态和数据的技术。
常用的会话技术分为两种:
- Cookie :客户端会话技术
- Session :服务端会话技术
什么是Cookie呢?
Cookie是属于客户端会话技术,他是服务器发送给浏览器小文本信息,存储在浏览器的内存或硬盘上。当浏览器保存了Cookie后,每次访问服务器,都会在HTTP这个请求头将将这个Cookie回传给服务器上。
Cookie 的使用细节
使用 Cookie 开发时需要注意以下细节:
- 一个 Cookie 只能标识一种信息,它至少包含一个名称(NAME)和一个值(VALUE)。
- 如果创建了一个 Cookie,并发送到浏览器,默认情况下它是一个会话级别的 Cookie。用户退出浏览器就被删除。如果希望将 Cookie 存到磁盘上,则需要调用 setMaxAge(int maxAge) 方法设置最大有效时间,以秒为单位。
- 使用 setMaxAge(0) 手动删除 Cookie时,需要使用 setPath 方法指定 Cookie 的路径,且该路径必须与创建 Cookie 时的路径保持一致。
Cookie 的缺点
Cookie 虽然可以解决服务器跟踪用户状态的问题,但是它具有以下缺点:
- 在 HTTP 请求中,Cookie 是明文传递的,容易泄露用户信息,安全性不高。
- 浏览器可以禁用 Cookie,一旦被禁用,Cookie 将无法正常工作。
- Cookie 对象中只能设置文本(字符串)信息。
- 客户端浏览器保存 Cookie 的数量和长度是有限制的。
实例:
我们建立Cookie Demo1类
public class CookieDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//服务器告诉你来的时间,然后把自己时间封装成一个信件,然后带下来,我就知道你来了。
//解决中文乱码
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
resp.setHeader("Content-Type","text/html;charset=utf-8");
PrintWriter writer = resp.getWriter();
//cookie服务器端从客户端中获取
Cookie[] cookies = req.getCookies();//数组返回,说明存在多个
//判断Cookie是否存在
if(cookies!=null){
writer.write("第一次访问的时间是:");
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
//获取cookie的名字
if(cookie.getName().equals("LastLoginTime")){
//获取cookie的值
long parseLong = Long.parseLong(cookie.getValue());
Date date = new Date(parseLong);
writer.write(date.toLocaleString());
}
}
}else {
writer.write("这是你第一次访问本站");
}
//服务器给客户端响应一个cookie
Cookie cookie = new Cookie("LastLoginTime",System.currentTimeMillis()+"");
cookie.setMaxAge(24*60*60);
resp.addCookie(cookie);
}
启动服务器。
![在这里插入图片描述](https://img-blog.csdnimg.cn/722243bbbb284f0694e6f35402029032.png)
我们这里使用首先创建Cookie对象。利用有参构造,注入相应的key和value值。
这里的setMaxAge()用于设置 Cookie 的最大有效时间,以秒为单位。addCookie()用于在响应头中增加一个相应的 Set-Cookie 头字段。