Request/Response

Request

请求响应

Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象、和代表响应的response对象。


请求对象

请求:客户机希望从服务器端索取一些资源,向服务器发出询问

请求对象:在 JavaEE 工程中,用于发送请求的对象,常用的对象是 ServletRequest 和 HttpServletRequest ,它们的区是是否与 HTTP 协议有关

Request 作用:

  • 操作请求三部分(行,头,体)
  • 请求转发
  • 作为域对象存数据


请求路径

方法 作用
String getLocalAddr() 获取本机(服务器)地址
String getLocalName() 获取本机(服务器)名称
int getLocalPort() 获取本机(服务器)端口
String getRemoteAddr() 获取访问者IP
String getRemoteHost 获取访问者主机
int getRemotePort() 获取访问者端口
String getMethod(); 获得请求方式
String getRequestURI() 获取统一资源标识符(/request/servletDemo01)
String getRequestURL() 获取统一资源定位符(http://localhost:8080/request/servletDemo01)
String getQueryString() 获取请求消息的数据
(GET方式 URL中带参字符串:username=aaa&password=123)
String getContextPath() 获取虚拟目录名称(/request)
String getServletPath 获取Servlet映射路径
或@WebServlet值: /servletDemo01)
String getRealPath(String path) 根据虚拟目录获取应用部署的磁盘绝对路径

URL = HOST + URI

URI = ContextPath + ServletPath


获取请求头

方法 作用
String getHeader(String name) 获得指定请求头的值。
如果没有该请求头返回null,有多个值返回第一个
Enumeration getHeaders(String name) 获取指定请求头的多个值
Enumeration getHeaderNames() 获取所有请求头名称的枚举

请求参数

请求参数是正文部分标签内容,

标签属性action=“/request/servletDemo08”,服务器URI

法名 作用
String getParameter(String name) 获得指定参数名的值
如果没有该参数则返回null,如果有多个获得第一个
String[] getParameterValues(String name) 获得指定参数名所有的值。此方法为复选框提供的
Enumeration getParameterNames() 获得所有参数名
Map<String,String[]> getParameterMap() 获得所有的请求参数键值对(key=value)

请求域

request 域:可以在一次请求范围内进行共享数据

方法 作用
void setAttribute(String name, Object value) 向请求域对象中存储数据
Object getAttribute(String name) 通过名称获取请求域对象的数据
void removeAttribute(String name) 通过名称移除请求域对象的数据

请求转发

请求转发:客户端的一次请求到达后,需要借助其他 Servlet 来实现功能,进行请求转发。特点:

  • 浏览器地址栏不变
  • 域对象中的数据不丢失
  • 负责转发的 Servlet 转发前后响应正文会丢失
  • 由转发目的地来响应客户端

HttpServletRequest 类方法:

  • RequestDispatcher getRequestDispatcher(String path) : 获取任务调度对象

RequestDispatcher 类方法:

  • void forward(ServletRequest request, ServletResponse response) : 实现转发,将请求从 Servlet 转发到服务器上的另一个资源(Servlet,JSP 文件或 HTML 文件)

过程:浏览器访问 http://localhost:8080/request/servletDemo09,/servletDemo10也会执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@WebServlet("/servletDemo09")
public class ServletDemo09 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置共享数据
req.setAttribute("encoding","gbk");
//获取请求调度对象
RequestDispatcher rd = req.getRequestDispatcher("/servletDemo10");
//实现转发功能
rd.forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@WebServlet("/servletDemo10")
public class ServletDemo10 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取共享数据
Object encoding = req.getAttribute("encoding");
System.out.println(encoding);//gbk

System.out.println("servletDemo10执行了...");
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}


请求包含

请求包含:合并其他的 Servlet 中的功能一起响应给客户端。特点:

  • 浏览器地址栏不变
  • 域对象中的数据不丢失
  • 被包含的 Servlet 响应头会丢失

请求转发的注意事项:负责转发的 Servlet,转发前后的响应正文丢失,由转发目的地来响应浏览器

请求包含的注意事项:被包含者的响应消息头丢失,因为它被包含者包含起来了

HttpServletRequest 类方法:

  • RequestDispatcher getRequestDispatcher(String path) : 获取任务调度对象

RequestDispatcher 类方法:

  • void include(ServletRequest request, ServletResponse response) : 实现包含。包括响应中资源的内容(servlet,JSP页面,HTML文件)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@WebServlet("/servletDemo11")
public class ServletDemo11 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servletDemo11执行了...");//执行了
//获取请求调度对象
RequestDispatcher rd = req.getRequestDispatcher("/servletDemo12");
//实现包含功能
rd.include(req,resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
**********************************************************************************
@WebServlet("/servletDemo12")
public class ServletDemo12 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servletDemo12执行了...");//输出了
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}

乱码问题

请求体

  • POST:void setCharacterEncoding(String env):设置请求体的编码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @WebServlet("/servletDemo08")
    public class ServletDemo08 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //设置编码格式
    req.setCharacterEncoding("UTF-8");

    String username = req.getParameter("username");
    System.out.println(username);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    doGet(req,resp);
    }
    }

  • GET:Tomcat8.5 版本及以后,Tomcat 服务器已经帮我们解决

Response

响应对象

响应,服务器把请求的处理结果告知客户端

响应对象:在 JavaEE 工程中,用于发送响应的对象

  • 协议无关的对象标准是:ServletResponse 接口
  • 协议相关的对象标准是:HttpServletResponse 接口

Response 的作用:

  • 操作响应的三部分(行, 头, 体)
  • 请求重定向


操作响应行

方法 说明
int getStatus() Gets the current status code of this response
void setStatus(int sc) Sets the status code for this response

状态码:(HTTP–>相应部分)

状态码 说明
1xx 消息
2xx 成功
3xx 重定向
4xx 客户端错误
5xx 服务器错误

操作响应体

字节流响应

响应体对应乱码问题

项目中常用的编码格式是UTF-8,而浏览器默认使用的编码是gbk。导致乱码!

解决方式:
一:修改浏览器的编码格式(不推荐,不能让用户做修改的动作)
二:通过输出流写出一个标签:<meta http-equiv='content-type’content=‘text/html;charset=UTF-8’>
三:指定响应头信息:response.setHeader(“Content-Type”,“text/html;charset=UTF-8”)
四:response.setContentType(“text/html;charset=UTF-8”)

常用API:
ServletOutputStream getOutputStream() : 获取响应字节输出流对象
void setContenType("text/html;charset=UTF-8") : 设置响应内容类型,解决中文乱码问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@WebServlet("/servletDemo01")
public class ServletDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.设置响应内容类型
resp.setContentType("text/html;charset=UTF-8");
//2.通过响应对象获取字节输出流对象
ServletOutputStream sos = resp.getOutputStream();
//3.定义消息
String str = "你好";
//4.通过字节流输出对象
sos.write(str.getBytes("UTF-8"));
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}


字符流响应

response得到的字符流和字节流互斥,只能选其一,response获取的流不用关闭,由服务器关闭即可。

常用API:
PrintWriter getWriter() : 获取响应字节输出流对象,可以发送标签
void setContenType("text/html;charset=UTF-8") : 设置响应内容类型,解决中文乱码问题

1
2
3
4
5
6
7
8
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String str = "你好";
//解决中文乱码
resp.setContentType("text/html;charset=UTF-8");
//获取字符流对象
PrintWriter pw = resp.getWriter();
pw.write(str);
}

响应图片

响应图片到浏览器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@WebServlet("/servletDemo03")
public class ServletDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.通过文件的相对路径来获取文件的绝对路径
String realPath = getServletContext().getRealPath("/img/hm.png");
//E:\Project\JavaEE\out\artifacts\Response_war_exploded\img\hm.png
System.out.println(realPath);
//2.创建字节输入流对象,关联图片路径
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(realPath));

//3.通过响应对象获取字节输出流对象
ServletOutputStream sos = resp.getOutputStream();

//4.循环读写
byte[] arr = new byte[1024];
int len;
while((len = bis.read(arr)) != -1) {
sos.write(arr,0,len);
}
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}

操作响应头

常用方法

响应头: 是服务器指示浏览器去做什么

方法 说明
String getHeader(String name) 获取指定响应头的内容
Collection getHeaders(String name) 获取指定响应头的多个值
Collection getHeaderNames() 获取所有响应头名称的枚举
void setHeader(String name, String value) 设置响应头
void setDateHeader(String name, long date) 设置具有给定名称和日期值的响应消息头
void sendRedirect(String location) 设置重定向

setHeader常用响应头:

  • Expires:设置缓存时间
  • Refresh:定时跳转
  • Location:重定向地址
  • Content-Disposition: 告诉浏览器下载
  • Content-Type:设置响应内容的MIME类型(服务器告诉浏览器内容的类型)

控制缓存

缓存:对于不经常变化的数据,我们可以设置合理的缓存时间,防止浏览器频繁的请求服务器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@WebServlet("/servletDemo04")
public class ServletDemo04 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String news = "设置缓存时间";
//设置缓存时间,缓存一小时
resp.setDateHeader("Expires",System.currentTimeMillis()+1*60*60*1000L);
//设置编码格式
resp.setContentType("text/html;charset=UTF-8");
//写出数据
resp.getWriter().write(news);
System.out.println("aaa");//只输出一次,不能刷新,必须从网址直接进入
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}


定时刷新

定时刷新:过了指定时间后,页面进行自动跳转

格式:setHeader("Refresh", "3;URL=https://www.baidu.com"");
Refresh设置的时间单位是秒,如果刷新到其他地址,需要在时间后面拼接上地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@WebServlet("/servletDemo05")
public class ServletDemo05 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String news = "您的用户名或密码错误,3秒后自动跳转到登录页面...";
//设置编码格式
resp.setContentType("text/html;charset=UTF-8");
//写出数据
resp.getWriter().write(news);

//设置响应消息头定时刷新
resp.setHeader("Refresh","3;URL=/response/login.html");
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}

下载文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@WebServlet("/servletDemo06")
public class ServletDemo06 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.创建字节输入流对象,关联读取的文件
String realPath = getServletContext().getRealPath("/img/hm.png");//绝对路径
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(realPath));

//2.设置响应头支持的类型 应用支持的类型为字节流
/*
Content-Type 消息头名称 支持的类型
application/octet-stream 消息头参数 应用类型为字节流
*/
resp.setHeader("Content-Type","application/octet-stream");

//3.设置响应头以下载方式打开 以附件形式处理内容
/*
Content-Disposition 消息头名称 处理的形式
attachment;filename= 消息头参数 附件形式进行处理
*/
resp.setHeader("Content-Disposition","attachment;filename=" + System.currentTimeMillis() + ".png");

//4.获取字节输出流对象
ServletOutputStream sos = resp.getOutputStream();

//5.循环读写文件
byte[] arr = new byte[1024];
int len;
while((len = bis.read(arr)) != -1) {
sos.write(arr,0,len);
}

//6.释放资源
bis.close();
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}

重定向

实现重定向

请求重定向:客户端的一次请求到达后,需要借助其他 Servlet 来实现功能。特点:

  1. 重定向两次请求
  2. 重定向的地址栏路径改变
  3. 重定向的路径写绝对路径(带域名 /ip 地址,如果是同一个项目,可以省略域名 /ip 地址)
  4. 重定向的路径可以是项目内部的,也可以是项目以外的(百度)
  5. 重定向不能重定向到 WEB-INF 下的资源
  6. 把数据存到 request 域里面,重定向不可用

实现方式:

  • 方式一:

    1. 设置响应状态码:resp.setStatus(302)
    2. 设置重定向的路径(响应到哪里,通过响应头 location 来指定)
      • response.setHeader("Location","http://www.baidu.com");
      • response.setHeader("Location","/response/servletDemo08);
  • 方式二:

    • resp.sendRedirect("重定向的路径");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@WebServlet("/servletDemo07")
public class ServletDemo07 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置请求域数据
req.setAttribute("username","zhangsan");

//设置重定向
resp.sendRedirect(req.getContextPath() + "/servletDemo07");
// resp.sendRedirect("https://www.baidu.com");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}

1
2
3
4
5
6
7
8
9
@WebServlet("/servletDemo08")
public class ServletDemo08 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servletDemo08执行了...");
Object username = req.getAttribute("username");
System.out.println(username);
}
}

重定向和转发

请求重定向跳转的特点:

  1. 重定向是由浏览器发起的,在这个过程中浏览器会发起两次请求
  2. 重定向可以跳转到任意服务器的资源,但是无法跳转到WEB-INF中的资源
  3. 重定向不能和请求域对象共享数据,数据会丢失
  4. 重定向浏览器的地址栏中的地址会变成跳转到的路径

请求转发跳转的特点:

  1. 请求转发是由服务器发起的,在这个过程中浏览器只会发起一次请求
  2. 请求转发只能跳转到本项目的资源,但是可以跳转到WEB-INF中的资源
  3. 请求转发可以和请求域对象共享数据,数据不会丢失
  4. 请求转发浏览器地址栏不变


路径问题

完整URL地址:

  1. 协议:http://
  2. 服务器主机地址:127.0.0.1 or localhost
  3. 服务器端口号:8080
  4. 项目的虚拟路径(部署路径):/response
  5. 具体的项目上资源路径 /login.html or Demo 的Servlet映射路径

相对路径:

不以"/“开头的路径写法,它是以目标路径相对当前文件的路径,其中”…"表示上一级目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>hello world....</h1>
<!--
目标资源的url: http://localhost:8080/response/demo05
当前资源的url: http://localhost:8080/response/pages/demo.html
相对路径的优劣:
1. 优势: 无论部署的项目名怎么改变,我的路径都不需要改变
2. 劣势: 如果当前资源的位置发生改变,那么相对路径就必定要发生改变-->
<a href="../demo05">访问ServletDemo05</a>
</body>
</html>

绝对路径:

绝对路径就是以"/"开头的路径写法,项目部署的路径