简单介绍
首先我们先了解一下客户端与服务端之间信息如何传递:
从上图,我们可以看到:
- 客户端发送请求,而请求是以HTTP请求数据格式进行发送,因而Servlet就创建了一个Request类来封装这些接收数据
- 服务端给出响应,而响应是以HTTP响应数据格式进行发送,因而Servlet就创建了一个Response类来封装这些返回数据
简单来说:
- Request对象:获得请求数据
- Response对象:设置响应数据
Request介绍
我们在介绍Request之前,应当先了解一下Request的继承体系:
我们可以看到HttpServletRequest是基于ServletRequest接口创建的针对Http协议的请求对象接口
我们在使用Request对象时,也常常使用HttpServletRequest接口
Request获得请求数据
Request对象被创建的主要目的就是获得请求数据
我们将根据HTTP请求数据对象的三种格式分开介绍获得请求数据方法
- 请求行:
请求行格式:
GET/request-demo/req1?username=zhangsan HTTP/1.1
请求行获得代码:
函数 | 解释 |
---|---|
String getMethod() | 获得请求方式(GET/POST) |
String getContextPath() | 获得虚拟目录(项目访问路径):/request-demo |
StringBuffer getRequestURL() | 获得URL(统一资源定位符): http://localhost:8080/request-demo/req1 |
String getRequestURI() | 获得URI(统一资源标识符):/request-demo/req1 |
String getQueryString | 获得请求参数(GET方法):username=zhangsan HTTP/1.1 |
- 请求头:
请求头格式:
User-Agent: Mozilla/5.0 Chrome/91.0.4472.106
请求头获得代码:
函数 | 解释 |
---|---|
String getHeader(String name) | 根据请求头名称,获得值 |
- 请求体:
请求体格式:
username=superbaby&password=123456
请求体获得代码:
函数 | 解释 |
---|---|
ServletInputStream getInputStream() | 获得字节输入流 |
BufferedReader getReader() | 获得字符输入流 |
最后给出代码展示:
package com.itheima.web.request; 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.BufferedReader; import java.io.IOException; /** * request 获取请求数据 */ @WebServlet("/req1") public class RequestDemo1 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // String getMethod():获取请求方式: GET String method = req.getMethod(); System.out.println(method);//GET // String getContextPath():获取虚拟目录(项目访问路径):/request-demo String contextPath = req.getContextPath(); System.out.println(contextPath); // StringBuffer getRequestURL(): 获取URL(统一资源定位符):http://localhost:8080/request-demo/req1 StringBuffer url = req.getRequestURL(); System.out.println(url.toString()); // String getRequestURI():获取URI(统一资源标识符): /request-demo/req1 String uri = req.getRequestURI(); System.out.println(uri); // String getQueryString():获取请求参数(GET方式): username=zhangsan String queryString = req.getQueryString(); System.out.println(queryString); //------------ // 获取请求头:user-agent: 浏览器的版本信息 String agent = req.getHeader("user-agent"); System.out.println(agent); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获取post 请求体:请求参数 //1. 获取字符输入流 BufferedReader br = req.getReader(); //2. 读取数据 String line = br.readLine(); System.out.println(line); } }
Request通用方式获得请求参数
在请求参数的获取方法上GET与POST有所不同:
- GET:String getQueryString()
- POST: BufferedReader getReader()
那么如果我们能够采用一种方法同时使GET和POST获得参数,就可以实现两者的通用
Request对此提供了一下方法:
函数 | 解释 |
---|---|
Map<String,String[]> getParameterMap() | 获得所有参数Map的集合 |
String[] getParametervalues(String name) | 根据名称获得参数值(数组) |
String getParameter(String name) | 根据名称获得参数值 |
我们给出通用方法,并做出解释:
package com.itheima.web.request; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; import java.util.Map; /** * request 通用方式获取请求参数 */ @WebServlet("/req2") public class RequestDemo2 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //GET请求逻辑 //System.out.println("get...."); //1. 获取所有参数的Map集合 Map<String, String[]> map = req.getParameterMap(); for (String key : map.keySet()) { // username:zhangsan lisi 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获取参数值,数组(我们希望查询参数为hobby的值,在查询中hobby的值为1,2) String[] hobbies = req.getParameterValues("hobby"); for (String hobby : hobbies) { System.out.println(hobby); } //3. 根据key 获取单个参数值 String username = req.getParameter("username"); String password = req.getParameter("password"); System.out.println(username); System.out.println(password); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //POST请求逻辑 // 因为两者可以共用一个方法,所以doPost直接调用doGet即可 this.doGet(req,resp); /*System.out.println("post...."); //1. 获取所有参数的Map集合 Map<String, String[]> map = req.getParameterMap(); for (String key : map.keySet()) { // username:zhangsan lisi 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); } //3. 根据key 获取单个参数值 String username = req.getParameter("username"); String password = req.getParameter("password"); System.out.println(username); System.out.println(password);*/ } }
请求参数中文化导致乱码问题
当我们的请求参数中如果存在中文数据,可能会出现乱码(Tomcat8以下版本)
我们分别介绍POST和GET的中文乱码解决方案
- POST:
首先我们从根本上解释一下为什么会出现乱码:
- POST底层以getReader()的方式以ISO-8859-1的形式获得输入流
//1. 解决乱码:POST,getReader() // 默认情况下POST以ISO-8859-1的形式获取流 // POST中可以直接设置字符输入流的编码 // request.setCharacterEncoding() 改变字符输入流的获得格式 request.setCharacterEncoding("UTF-8");
- GET:
首先我们从根本上解释一下为什么会出现乱码:
- 当HTML识别到内容后,会以UTF-8的形式进行编码,并发送给服务端
- 但服务端在接收到数据后,会以ISO-8859-1的形式进行解码
- 编码解码方式不同,对中文的处理方式不同,导致中文数据出现乱码
// 我们以一个例子来模拟GET乱码的解决过程 package com.itheima.web.request; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; public class URLDemo { public static void main(String[] args) throws UnsupportedEncodingException { String username = "张三"; // 1.2步模拟了乱码过程;3,4步解决乱码过程 //1. URL编码(HTML传递) //URLEncoder.encode(username, "utf-8");表示对username进行utf-8形式的编码,形成code String encode = URLEncoder.encode(username, "utf-8"); System.out.println(encode); //2. URL解码 //URLDecoder.decode(encode, "ISO-8859-1");表示对encode进行ISO-8859-1形式的解码,形成decode String decode = URLDecoder.decode(encode, "ISO-8859-1"); System.out.println(decode); //3. 转换为字节数据,编码 byte[] bytes = decode.getBytes("ISO-8859-1"); //4. 将字节数组转为字符串,解码 String s = new String(bytes, "utf-8"); System.out.println(s); } }
因为GET的方法属于通用方法,所以在整个项目中可以直接通过第二种方法来进行GET和POST的中文乱码修改问题
package com.itheima.web.request; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; import java.nio.charset.StandardCharsets; /** * 中文乱码问题解决方案 */ @WebServlet("/req4") public class RequestDemo4 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1. 解决乱码:POST,getReader() //request.setCharacterEncoding("UTF-8");//设置字符输入流的编码 //2. 获取username String username = request.getParameter("username"); System.out.println("解决乱码前:"+username); //3. GET,获取参数的方式:getQueryString // 乱码原因:tomcat进行URL解码,默认的字符集ISO-8859-1 /* //3.1 先对乱码数据进行编码:转为字节数组 byte[] bytes = username.getBytes(StandardCharsets.ISO_8859_1); //3.2 字节数组解码 username = new String(bytes, StandardCharsets.UTF_8);*/ username = new String(username.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8); System.out.println("解决乱码后:"+username); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
Request请求转发
首先讲解一下请求转发的概念:
- 请求转发:一种在服务器内部的资源跳转方法
- 当客户端发送请求后,服务端可以选择把这个请求转发出去或者说是共享出去
实现方式:
req.getRequestDispatcher("资源B地址").forward(req,resp)
实现所需函数:
函数 | 解释 |
---|---|
void setAttribute(String name,Object o) | 存储数据到request域(一般在资源A地址存储) |
Object getAttribute(String name) | 根据key,获得value(一般在资源B地址存储) |
void removeAttribute(String name) | 根据key,删除该键值对 |
请求转发特点:
- 浏览器地址栏路径不发生变化
- 只能转发到当前服务器的内部资源
- 一次请求,可以在转发的资源间使用request共享数据
我们给出两个/demo服务器端进行模拟:
// 客户端资源A package com.itheima.web.request; 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.nio.charset.StandardCharsets; /** * 请求转发 */ @WebServlet("/req5") public class RequestDemo5 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("demo5..."); System.out.println(request); //存储数据 request.setAttribute("msg","hello"); //请求转发 request.getRequestDispatcher("/req6").forward(request,response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
// 客户端资源B package com.itheima.web.request; 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; /** * 请求转发 */ @WebServlet("/req6") public class RequestDemo6 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("demo6..."); System.out.println(request); //获取数据 Object msg = request.getAttribute("msg"); System.out.println(msg); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
Response介绍
同样,我们也来介绍一下Response的体系结构:
我们在使用Response时,也以HttpServletResponse为主
设置响应数据
和获取数据相同,我们把函数根据响应数据的三部分分别展示:
- 响应行:
响应行格式:
HTTP/1.1 200 OK
响应行设置函数:
函数 | 解释 |
---|---|
void setStatus(int sc) | 设置响应状态码 |
- 响应头:
响应头格式:
Content-Type:text/html
响应头设置函数:
函数 | 解释 |
---|---|
void setHeader(String name,String value) | 设置响应头键值对 |
- 响应体:
响应体格式:
<html><head></head><body></body></html>
响应体设置函数:
函数 | 解释 |
---|---|
PrintWriter getWriter() | 获得字符输出流 |
ServletOutputStream getOutputStream() | 获得字节输出流 |
下面给出代码示例:
package com.itheima.web.response; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; @WebServlet("/resp1") public class ResponseDemo1 extends HttpServlet { @Override 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"); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
重定位
首先我们介绍一下重定位:
- 重定位:一种资源跳转方式
- 客户端向服务器A发出请求,当该资源A无法满足客户端发出的请求,资源A返回响应(响应码302表示无法处理,并给出响应头location:xxx告诉客户端应该去哪个资源B解决问题),然后客户端再向资源B发送请求,由资源B来设置响应数据
实现方式:
resp.setStatus(302); resp.setHeader("location","资源B的路径")
resp.sendRedirect("资源B的路径")
重定位特点:
- 浏览器地址栏路径发生变化
- 可以重定位到任意位置的资源(服务器内部,外部均可以)
- 两次请求,不能在多个资源使用request共享数据
下面给出代码示例:
package com.itheima.web.response; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; @WebServlet("/resp1") public class ResponseDemo1 extends HttpServlet { @Override 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(); // 这里资源B的路径是虚拟目录+设置名 response.sendRedirect(contextPath+"/resp2"); //response.sendRedirect("https://www.itcast.cn"); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
资源路径问题:
浏览器使用:需要加虚拟目录(项目访问路径)
服务端使用:不需要加虚拟目录
目前已学习内容:
Response响应字符数据
我们可以使用Response对象向客户端发送数据
具体步骤:
- 通过Response对象获得字符输出流:
PrintWriter writer = resp.getWriter();
- 写数据
writer.write("aaa");
注意:
- 该流不需要关闭,随着响应结束,response对象销毁,由服务器关闭
- 中文数据乱码:原因通过Response获取的字符输出流默认编码:ISO-8859-1
resp.setContentType("text/html;charset=utf-8");
代码展示:
package com.itheima.web.response; 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.io.PrintWriter; /** * 响应字符数据:设置字符数据的响应体 */ @WebServlet("/resp3") public class ResponseDemo3 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); //1. 获取字符输出流 PrintWriter writer = response.getWriter(); //content-type //response.setHeader("content-type","text/html"); writer.write("你好"); writer.write("<h1>aaa</h1>"); //细节:流不需要关闭 } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
Response响应字节数据
我们可以使用Response对象向客户端发送数据
传统具体步骤:
- 通过Response对象获得字符输出流
ServletOutputStream outputStream = resp.getOutputStream();
- 写数据
outputStream.write(字节数据);
IOUtils工具类使用:
- 导入坐标:
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency>
- 使用即可:
IOUtils.copy(输入流,输出流);
代码展示:
package com.itheima.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; /** * 响应字节数据:设置字节数据的响应体 */ @WebServlet("/resp4") public class ResponseDemo4 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1. 读取文件 FileInputStream fis = new FileInputStream("d://a.jpg"); //2. 获取response字节输出流 ServletOutputStream os = response.getOutputStream(); //3. 完成流的copy /* byte[] buff = new byte[1024]; int len = 0; while ((len = fis.read(buff))!= -1){ os.write(buff,0,len); }*/ IOUtils.copy(fis,os); fis.close(); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
结束语
好的,关于Request和Respone的内容就到这了