声明:请勿利用本公众号文章内的相关技术、工具从事非法测试,如因此造成一切不良后果与文章作者及本公众号无关! |
前一篇分享了JavaWeb的基础知识包括HTTP协议、Tomcat容器和Servlet组件,其中Servlet是JavaWeb三大核心组件之一,另外两个核心组件Filter和Listener后续会介绍。想看JavaWeb基础内容的请参考下面链接:
JavaWeb之HTTP、Tomcat、Servlet
0xNvyao,公众号:安全随笔JavaWeb之HTTP、Tomcat、Servlet
本篇来学习下JavaWeb的两个重要的对象-Request和Response。
0x01,Request对象
上一篇介绍了Javaweb的Servlet最后留一个思考?编写Servlet类有两种方式,一种是通过实现Servlet接口,另一种是通过继承HttpServlet抽象类,注意观察这两种方式接收的请求、响应的参数类型是有区别的,前者是ServletRequest类型,后者是HttpServletRequest类型。本节详细说说他们之间的继承关系和常规用法。下面是两者的官方描述:
ServletRequest:
定义一个对象来向 servlet 提供客户端请求信息(说白了就是提供一个对象来封装请求信息)。servlet 容器创建一个 ServletRequest 对象并将其作为参数传递给 servlet 的service()方法。ServletRequest 对象提供数据,包括参数名称和值、属性和输入流。通过继承ServletRequest 接口可以提供其他特定于协议的数据(如:javax.servlet.http.HttpServletRequest 提供 HTTP 数据)
HttpServletRequest:
继承自 ServletRequest 接口以提供 HTTP servlet 的请求信息。servlet 容器创建一个 HttpServletRequest 对象并将其作为参数传递给 servlet 的服务方法(doGet、doPost 等)
Request 继承体系
ServletRequest //Java提供的请求对象根接口
-->继承关系
HttpServletRequest //Java提供的对Http协议封装的请求对象接口
-->实现关系
RequestFacade //Tomcat定义的实现类
可以通过IDEA看一下:
上面两个接口都是java提供好理解,为啥是Tomcat提供实现类?
在前一篇我们分析过,知道了Servlet类对象是由Tomcat创建的,Servlet对象的service()是由Tomcat调用的,因此service(request,response)方法的的request和response对象也应该是Tomcat来实现(用于封装请求、响应数据)。
我们通过打印一下request、response对象证实一下:
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException
System.out.println(servletRequest);
System.out.println(servletResponse);
}
-
Request对象:org.apache.catalina.connector.RequestFacade
-
Response对象:org.apache.catalina.connector.ResponseFacade
Request 获取请求数据
Request请求数据其实就是HTTP请求的数据,可以通过F12抓包看到,学习Request对象就是学习如何获取HTTP请求数据,请求数据包含请求行、请求头和请求体:
1)获取请求行
//获取请求行常用方法
String method = req.getMethod();
System.out.println(method);
String contextPath = req.getContextPath();
System.out.println(contextPath);
StringBuffer requestURL = req.getRequestURL(); //类型是StringBuffer
System.out.println(requestURL);
String requestURI = req.getRequestURI();
System.out.println(requestURI);
2)获取请求头
//获取请求头
String header = req.getHeader("user-agent");
System.out.println(header);
Cookie[] cookies = req.getCookies();
System.out.println(cookies);
System.out.println(req.getContentLength());
System.out.println(req.getContentType());
System.out.println(req.getLocalPort());
3)获取请求体
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取post请求体
//1.获取字符输入流
BufferedReader bufferedReader = req.getReader();
//2.读取数据:按行
//String line = bufferedReader.readLine();
//System.out.println(line);
//3.读取数据:按字符
int read = 1;
while(read >0) {
read = bufferedReader.read();
System.out.print((char)read);
}
}
当然如果是非字符输入流数据,而是字节输入流,需要通过创建字节输入流来接收请求体数据,举个例子:
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取字节输入流
Part filePart = req.getPart("file"); // 获取上传的文件部分
String fileName = filePart.getSubmittedFileName(); // 获取文件名
// 获取当前日期和时间
LocalDateTime currentDateTime = LocalDateTime.now();
// 定义日期时间格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 格式化日期时间
String formattedDateTime = currentDateTime.format(formatter);
System.out.println(formattedDateTime);
InputStream fileContent = filePart.getInputStream();
// 保存文件到服务器
OutputStream outputStream = new FileOutputStream("/Users/liujianping/Downloads/" + fileName.substring(0, fileName.lastIndexOf('.')) + formattedDateTime + ".png");
byte[] buffer = new byte[4096];
int bytesRead = -1;
while ((bytesRead = fileContent.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.close();
fileContent.close();
resp.getWriter().println("File " + fileName + " uploaded successfully");
}
Request 通用方式获取请求参数(统一doGet和doPost)
根据前面介绍,请求方式(Get或者Post)的不同,导致获取请求参数的方式也不一样,如下。除此之外其他的数据获取都是一样的。
-
GET参数:req.getQueryString();
-
POST参数:req.getReader();
因此如果因为参数获取方式不同导致写两遍一样的代码,显然不合理的,因此HttpServletRequest接口正好提供了这样的一些方法可以用于统一GET和POST不同方式的请求参数获取方式,分为以下三种场景:
-
Map<String, String[]>getParameterMap(): 获取所有参数Map集合
-
String[]getParameterValues(String name): 根据名称获取参数值(数组)
-
StringgetParameter(String name): 根据名称获取参数值(单个值)
package com.nvyao.web;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
@WebServlet("/req2")
public class RequestDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// System.out.println("get...");
System.out.println(req.getMethod() + "...");
//1.获取所有的参数的Map集合
Map<String, String[]> map = req.getParameterMap();
for(String key : map.keySet()) {
//获取键
System.out.print(key + ":");
//获取值
String[] values = map.get(key);
for (String value : values) {
System.out.print(value + " ");
}
System.out.println();
}
System.out.println("-------------");
//2.根据key获取参数值:数组
String[] hobbies = req.getParameterValues("hobby");
for (String hobby : hobbies) {
System.out.println(hobby);
}
System.out.println("-------------");
//3.根据key 获取单个参数值
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println("username:" +username +",password:" + password);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
可以看到不管是GET还是POST都可以通用地获取请求参数
0x02,Response对象
Response 继承体系
和Request 继承体系一样,Response的继承体系如下:
ServletResponse //Java提供的响应对象根接口
-->继承关系
HttpServletResponse //Java提供的对Http协议封装的响应对象接口
-->实现关系
ReponseFacade //Tomcat定义的实现类
Response 响应数据
Response 响应数据分为响应字符数据和响应字节数据,具体是:
-
Response响应字符数据:PrintWriter writer = response.getWriter();
-
Response响应字节数据:ServletOutputStream os = resp.getOutputStream();
以下是响应字符数据的代码示例:
/**
* 响应字符数据:设置字符数据的响应体
*/
"/resp3") (
public class ResponseDemo3 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");//设置响应的数据格式和字符集
System.out.println("resp3...");
//1、获取字符输出流
PrintWriter responseWriter = response.getWriter();
// response.setHeader("content-type","text/html");
responseWriter.write("你好");
responseWriter.write("<h1>bbbb</h1>");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
以下是响应字节数据的代码示例:
package com.nvyao.web.response;
import org.apache.commons.io.IOUtils;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 响应字节数据:设置字节数据的响应体
*/
"/resp4") (
public class ResponseDemo4 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1、读取文件
FileInputStream fis = new FileInputStream("/Users/liujianping/Downloads/baidu.png");
//2、获取response字节输出流
ServletOutputStream os = response.getOutputStream();
//3、完成流的copy
/*byte[] buffer = new byte[1024];
int len = 0;
while((len = fis.read(buffer)) != -1){
os.write(buffer, 0, len);
}*/
int copy = IOUtils.copy(fis, os);//返回总字节大小
System.out.println(copy);
fis.close();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
0x03,请求转发和响应重定向的区别
再来介绍下请求转发和响应重定向的区别:
请求转发(forward):一种在服务器内部的资源跳转方式,特点是:
-
浏览器地址栏路径不发生变化
-
只能转发到当前服务器的内部资源
-
发生一次请求,可以在转发的资源间使用request共享数据
重定向(Redirect):一种资源跳转方式,特点是:
-
浏览器地址栏路径发生变化
-
可以重定向到任意资源,包括外部资源
-
发生两次请求,不能在多个资源间使用request共享数据
以下是请求转发的代码示例:
"/req5") (
public class RequestDemo5 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo5...");
//存储数据
request.setAttribute("msg", "hello~");
// 请求转发
request.getRequestDispatcher("/req6").forward(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
以下是响应重定向代码示例:
package com.nvyao.web.response;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
"/resp1") (
public class ResponseDemo1 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("resp1...");
//重定向
// //1.设置响应码302
// response.setStatus(302);
// //2.设置响应头Location
// response.setHeader("location","/request-demo/resp2");
//简化完成重定向
String contextPath = request.getContextPath();
// System.out.println(contextPath);
response.sendRedirect( contextPath + "/resp2");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
原文始发于微信公众号(安全随笔):JavaWeb之Request请求和Response响应
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论