请求与响应

Request和Response

Posted by YD Blog on October 15, 2022

请求与响应

Request(请求)和Response(响应)

在Servlet对象中的service方法有两个参数,其类型分别为Request和Response,代表请求与响应。

浏览器在需要的时候会向服务器发送请求信息,本质上就是一段字符串,服务器会将这段请求信息封装成Request对象,并传给service方法。

而Response对象则是由service方法设置,服务器软件会根据Response对象中的信息拼凑出响应数据回传给浏览器。

  • Request:获取请求数据
  • Response:设置响应数据

Request对象

Request继承体系

  • ServletRequest:Java提供的请求对象根接口
  • HttpServletRequest:Java提供的对Http协议封装的请求对象接口
  • RequestFacade:Tomcat定义的实现类

以上均由下往上继承。

Tomcat需要解析请求数据,封装为request对象,并且创建request对象传递到service方法中。

获取请求数据

  1. 请求行

请求行由一行三部分的字符串组成,分别为请求方式,请求的资源路径及参数,请求的协议和版本号。

由如下方法: |方法|作用| |—|—| |String getMethod()|获取请求的方式| |String getContextPath()|获取虚拟目录(项目访问路径)| |StringBuffer getRequestURL()|获取URL(统一资源定位符)| |String getRequestURI()|获取URI(统一资源标识符)| |String getQueryString()|获取请求参数(GET方式)|

  1. 请求头

请求头的格式为键值对,也就是名称=值

方法 作用
String getHeader(String name) 根据请求头的名称获取值
  1. 请求体

请求体是以流的方式获取的:

方法: |方法|作用| |—|—| |ServletinputStream getInputStream()|获取字节输入流| |BufferedReader getReader()|获取字符输入流|

Request通用方式获取请求参数

  • 请求参数获取方式:
    • GET方式:String getQueryString()
    • POST方式:BufferedReader getReader()

GET请求方式和POST请求方式的区别主要在于获取请求参数的方式不一样,可以提供一种统一获取请求参数的方式从而统一doGet和doPost方法内的代码。

如下: 方法: |方法|作用| |Map<String,String[]> getParameterMap()|获取包含所有参数的Map集合| |String[] getParameterValue(String name)|根据名称获取参数值(数组)| |String getParameter(String name)|根据名称获取参数值(单个值)|

Request请求参数中文乱码处理

  • POST方式的中文乱码问题

这是由于服务器软件获取流的时候默认编码格式与页面格式不统一造成的,解决方案为在获取之前统一改变编码方式为UTF-8。

request.setCharacterEncoding("UTF-8");
  • GET方式的中文乱码问题

这是由于浏览器URL编码方式和服务器不同造成的,由于Tomcat无法自定义URL编码方式,故可通过将乱码的字符串先转为二进制流,然后按照UTF-8重新解码的方式解决。

byte[] bytes = stringName.getBytes(StandardCharsets.ISO_8859_1);
stringName = new String(bytes,StandardCharsets.UTF_8);

或者:

stringName = new String(stringName.getBytes("ISO-8859-1"),"UTF-8");

Request请求转发

浏览器请求一个资源时若资源只处理了这个请求的一部分内容,则需要跳转到另一个资源进行后续处理。

请求转发(forward):一种在服务器内部的资源跳转方式。

转发的实现方式:

request.getRequestDispatcher("跳转资源路径").forward(request,response);

资源转发需要进行两个资源间的资源共享,使用request对象:

void setAttribute(String name,Object o);//数据存储到request域中
Object getAttribute(String name);//根据key获取值
void removeAttribute(String name);//根据key删除对应值

请求转发的特点:

  • 浏览器地址栏路径不发生变化
  • 只能转发到当前服务器的内部资源
  • 一次请求可以在转发的资源间使用request共享数据

Response对象

Response继承体系

  • ServletResponse:Java提供的请求对象根接口
  • HttpServletResponse:Java提供的对Http协议封装的请求对象
  • ResponseFacade:Tomcat定义的实现类

从下往上继承。

响应数据的结构

  1. 响应行:协议版本,状态码,状态码描述

void setStatus(int sc)设置响应状态码

  1. 响应头:键值对

void setHeader(String name,String value)设置响应头键值对

  1. 响应体:文件

PrintWriter getWriter()获取字符输出流

ServletOutputStream getOutputStream()获取字节输出流

Response完成重定向

重定向(Redirect):一种资源跳转方式

浏览器向资源A发送请求,资源A返回重定向信息,浏览器向资源B发送请求

重定向状态码302,将响应数据的状态码设置为302即代表响应为重定向信息。

重定向目标记录在响应头中,key为location。

故一个完整的重定向设置应该为:

response.setStatus(302);
response.setHeader("location","资源路径");

或者:

response.sendRedirect("资源路径");//其实就是上面两行代码的封装

重定向的特点:

  • 浏览器的地址栏发生变化
  • 可以重定向到任意位置的资源(服务器内部外部的均可)
  • 两次请求,不能在多个资源使用request共享数据

路径问题

  • 是否需要加虚拟目录与路径的使用者有关:
    • 浏览器使用:需要加虚拟目录(项目访问路径)
    • 服务端使用:不用加虚拟目录
  • 动态获取虚拟目录:String contextPath = request.getContextPath();

Response设置响应体字符数据

使用:

  • 通过Response对象获取字符输出流:PrintWriter writer = response.getWriter();
  • 写入数据:writer.write("23333");

注意:

  • 该流不需要关闭,随着响应结束,response对象销毁,由服务器关闭。
  • 中文数据乱码:原因通过Response获取的字符输出流默认编码:ISO-8859-1,解决方案为:response.setContentType("text/html;charset=utf-8");

Reaponse设置响应字节数据

使用:

  • 读取文件:FileInputStream fis = new FileInputStream("文件路径");
  • 获取字节输出流:ServletOutputStream os = response.getOutputStream();
  • 完成流的对拷:见下文
  • 关闭文件:fis.close();

实际完整操作为:

FileInputStream fis = new FileInputStream("文件路径");
ServletOutputStream os = response.getOutputStream();

Byte arr = new Byte(1024);
int len = 0;
while((len = fis.read(arr)) != -1) {
  os.write(arr,0,len);
}

fis.close();

关于流的对拷,可以由工具类实现更简单的写法:

在pom文件中加入如下代码:

    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>1.4</version>
    </dependency>

然后直接在程序中使用IOUtils.copy()方法就可以完成流的拷贝。

FileInputStream fis = new FileInputStream("文件路径");
ServletOutputStream os = response.getOutputStream();

IOUtils.copy(fis,os);

fis.close();