Request和Response

流程概述

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

  • 要得到客户机提交过来的数据,只需要找request对象就行了。
  • 要向客户机输出数据,只需要找response对象就行了。

request

首先回顾一下请求报文的结构

request就是将请求报文封装而成的对象,所以通过request能获得请求文本中的所有内容,请求头、请求体、请求行 。

API

request的函数大概可以分为以下三类

  • 获取请求头
  • 获取请求体
  • 获取请求行

具体可以参看官网解释

HttpServletRequest API

其他应用

request域

request对象同时也是一个域对象,我们可以使用request来传输数据。request域对象不像Servlet域一样一直能使用,request只能在一次请求过程之中传递数据,请求结束即自动销毁。具体使用涉及到以下三个方法:

  • setAttribute(String name, Object o)
  • getAttribute(String name)
  • removeAttribute(String name)

乱码问题

在获取请求报文相关的内容的时候,有时候我们会发现获取到的内容是乱码的内容。通常情况是浏览器向服务器发送的请求参数中包含中文字符,服务器获取到的请求参数的值是乱码。

浏览器向服务器发送请求,因为浏览器与服务器之间的通信实质上是socket流,所以要先将请求参数(字符)转换成字节,也就是编码过程,服务器接收到请求参数后进行解码(字节转字符),然后封装到request对象中。如果客户端的编码与服务器端的解码不统一,就会导致通过request获取到的请求参数的值是乱码。

所以为了解决request乱码的问题,我们只需要在服务器端设置相应的解码格式即可。

具体设置代码如下:

request.setCharacterEncoding("utf-8");  //设置request对象的解码方式

请求转发

什么是请求转发呢?

一个形象的类比就是我去问辅导员借钱,但是辅导员因为工资还没有发下来,他也没钱,他就去找了另外一个老师借钱来给我。例子中我就可以理解为浏览器,借钱这个行为就相当于发送request,辅导员就可以理解为第一次请求的地址,辅导员直接帮我去借了钱,而不是让我自己再去找另外一个老师借钱,就可以理解为第一次请求的地址直接把我的请求转发到了同一个系统内的其他地址,这就是请求转发。

请求转发的图示如下:

可以看到,请求转发实际上是一次请求,所以地址栏不会发生变化。

具体实现方式:

//path为转发的地址(只限内部路径)
request.getRequestDispatcher(path).forward(request,response);

防盗链

在平时的使用当中,我们会发现下载文件的时候会有一个下载地址,有的人直接将这个地址复制到其他地方,然后贴上下载,就盗取了别人的资源,这种白嫖行为我们就称之为盗链。

我们可以利用request来防范这种白嫖行为,我们只需要在下载页面做下简单的来源判断。如果不是来自本站的连接,则直接拒绝访问。

这就是request的防盗链的运用,具体实现代码如下:

//获取请求来源 
String referer = request.getHeader("referer");
//判断这个来源是否是自己认可的
if(referer.euqals(...)){
...
}else{
response.getWriter.print("休想白嫖");
}

response

首先回顾一下响应报文的结构

Tomcat传给Servlet时,它还是空的对象。Servlet逻辑处理后得到结果,最终通过response.write()方法,将结果写入response内部的缓冲区。Tomcat会在servlet处理结束后,拿到response,遍历里面的信息,组装成HTTP响应发给客户端。

HttpServletResponse对象代表服务器的响应。这个对象中封装了向客户端发送数据、发送响应头,发送响应状态码的方法。

API

request的函数大概可以分为以下三类

  • 设置响应状态行
  • 设置响应头
  • 设置响应实体

具体可以参看官网解释:

HttpServletResponse API

具体应用

乱码问题

有时候服务器向浏览器发送的数据包含中文字符,在 浏览器中显示的是乱码。乱码的原因是 response 对象的字符输出流在编码时采用的字符码表是 ISO-8859-1,该码表不兼容中文,所以就会出现乱码的问题。处理乱码问题,有下面两种方案:

方法一:

response.setCharacterEncoding("utf-8");    //设置 HttpServletResponse使用utf-8编码
response.setHeader("Content-Type", "text/html;charset=utf-8"); //通知浏览器使用utf-8解码

方法二:

response.setContentType("text/html;charset=utf-8");    //包含方法一的两个功能

重定向

什么是请求转发呢?

同样的我们可以用借钱来类比一下。我去问辅导员借钱,但是辅导员因为工资还没有发下来,他也没钱,他就告诉我另外一个老师有钱,让我去找那个老师借。例子中我就可以理解为浏览器,借钱这个行为就相当于发送request,辅导员就可以理解为第一次请求的地址,辅导员告诉我去找另外一个老师借钱,就可以理解为第一次请求的响应中告诉了我去访问另外一个地址,然后我自己去找另外一个老师借,这就相当于浏览器发送第二次请求到第一个响应当中的地址,这就是重定向。

当用户请求servlet时,服务器返回一个类似上面的响应头,上面的信息告诉浏览器,应该重新进行一次请求,请求的地址为 Header中的Location地址。

重定向的图示如图所示:

可以看到,请求重定向实际上是两次请求,所以地址栏发生变化。

重定向有两种实现方式:

  • 方法一
//在响应头中添加302状态码,告诉浏览器需要进行重定向 
response.setStatus(302);
//在响应头中添加Location,指定重定向的位置
response.setHeader("Location", "http://www.baidu.com");
  • 方法二
response.setRedirect("http://www.baidu.com")

传送图片

客户端有时候会请求某张图片的内容,我们需要将图片文件返回给客户端,一般来说使用Response传送图片的做法如下:

示例代码如下:

//        获得图片的绝对路径
String path = getServletContext().getRealPath("man.jpg");
System.out.println(path);
// 获取本地字节输入流
FileInputStream fis = new FileInputStream(path);
// 获取本地字节输出流
ServletOutputStream sos = response.getOutputStream();
int len=0;
// 建立一个数组 一次读1024个字节
byte[] bytes = new byte[1024];
while ((len=fis.read(bytes))!=-1){
out.write(bytes,0,len);
}
fis.close();
sos.close();

传送文件

类似于传输图片,但是浏览器不能识别文件的名称和文件的类型,需要我们在响应头中设置文件的类型来告诉浏览器该文件的相关属性:

示例代码如下:

//通过路径得到一个输入流
String path = this.getServletContext().getRealPath("man.jpg");
FileInputStream fis = new FileInputStream(path);
//创建字节输出流
ServletOutputStream sos = response.getOutputStream();

//得到要下载的文件名
String filename = path.substring(path.lastIndexOf("\\")+1);

//设置文件名的编码
filename = URLEncoder.encode(filename, "UTF-8");//将不安全的文件名改为UTF-8格式

//告知客户端要下载文件,并且设置文件类型
response.setHeader("content-disposition", "attachment;filename="+filename);
response.setHeader("content-type", "image/jpeg");

//执行输出操作
int len = 1;
byte[] b = new byte[1024];
while((len=fis.read(b))!=-1){
sos.write(b,0,len);
}

sos.close();
fis.close();

禁止缓存

有时候我们想让用户端不缓存html,css,js等资源,我们可以通过对response头进行一定的设置来实现这样的功能,代码如下:

  response.setHeader("Cache-Control", "no-cache");
  response.setHeader("Pragma", "no-cache");
  response.setDateHeader("Expires", -1);
Author: Tunan
Link: http://yerias.github.io/2021/08/15/springboot/1/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.