请求
请求对象
关于请求
请求,就是使用者希望从服务器端索取一些资源,向服务器发出询问。在B/S架构中,就是客户浏览器向服务器发出询问。在JavaEE工程中,客户浏览器发出询问,要遵循HTTP协议规定。
请求对象,就是在JavaEE工程中,用于发送请求的对象。我们常用的对象就是ServletRequest和HttpServletRequest,它们的区别就是是否和HTTP协议有关。
有需要大厂面经和面试技巧思维导图的朋友可以点进去了解一下,点击——【传送门】——即可!
常用请求对象
常用请求方法
请求对象的使用示例
常用方法一:请求各种路径
/* 获取路径的相关方法 */ @WebServlet("/servletDemo01") public class ServletDemo01 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.获取虚拟目录名称 getContextPath() String contextPath = req.getContextPath(); System.out.println(contextPath); //2.获取Servlet映射路径 getServletPath() String servletPath = req.getServletPath(); System.out.println(servletPath); //3.获取访问者ip getRemoteAddr() String ip = req.getRemoteAddr(); System.out.println(ip); //4.获取请求消息的数据 getQueryString() String queryString = req.getQueryString(); System.out.println(queryString); //5.获取统一资源标识符 getRequestURI() /request/servletDemo01 String requestURI = req.getRequestURI(); System.out.println(requestURI); //6.获取统一资源定位符 getRequestURL() http://localhost:8080/request/servletDemo01 StringBuffer requestURL = req.getRequestURL(); System.out.println(requestURL); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } } 复制代码
常用方法二:获取请求参数以及封装(非常重要)
我们常常会使用HttpServletRequest对象获取请求参数,然后将其封装到实体类中
/* 获取请求参数信息的相关方法 */ @WebServlet("/servletDemo03") public class ServletDemo03 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.根据名称获取数据 getParameter() String username = req.getParameter("username"); System.out.println(username); String password = req.getParameter("password"); System.out.println(password); System.out.println("--------------------"); //2.根据名称获取所有数据 getParameterValues() String[] hobbies = req.getParameterValues("hobby"); for(String hobby : hobbies) { System.out.println(hobby); } System.out.println("--------------------"); //3.获取所有名称 getParameterNames() Enumeration<String> names = req.getParameterNames(); while(names.hasMoreElements()) { String name = names.nextElement(); System.out.println(name); } System.out.println("--------------------"); //4.获取所有参数的键值对 getParameterMap() Map<String, String[]> map = req.getParameterMap(); for(String key : map.keySet()) { String[] values = map.get(key); System.out.print(key + ":"); for(String value : values) { System.out.print(value + " "); } System.out.println(); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } } 复制代码
下面通过实例说明几种封装方式
需求:我们要实现从网页填写学生注册信息,然后把获取请求参数并把相应的信息封装到每一个Student类中。
第一步:编写一个页面html程序
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>注册页面</title> </head> <body> <form action="/request/servletDemo08" method="post" autocomplete="off"> 姓名:<input type="text" name="username"> <br> 密码:<input type="password" name="password"> <br> 爱好:<input type="checkbox" name="hobby" value="study">学习 <input type="checkbox" name="hobby" value="game">游戏 <br> <button type="submit">注册</button> </form> </body> </html> 复制代码
第二步:编写Student的javabean类,注意其数据成员最好(必须)与html文件表单的name属性一致
public class Student { private String username; private String password; private String[] hobby; public Student() { } public Student(String username, String password, String[] hobby) { this.username = username; this.password = password; this.hobby = hobby; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String[] getHobby() { return hobby; } public void setHobby(String[] hobby) { this.hobby = hobby; } @Override public String toString() { return "Student{" + "username='" + username + ''' + ", password='" + password + ''' + ", hobby=" + Arrays.toString(hobby) + '}'; } } 复制代码
第三步:获取参数信息,并封装数据
法一:直接手动封装(简单粗暴)
/* 封装对象-手动方式 */ @WebServlet("/servletDemo04") public class ServletDemo04 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.获取所有的数据 String username = req.getParameter("username"); String password = req.getParameter("password"); String[] hobbies = req.getParameterValues("hobby"); //2.封装学生对象 Student stu = new Student(username,password,hobbies); //3.输出对象 System.out.println(stu); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } } 复制代码
法二:通过反射封装
PropertyDescriptor
: Describes a Java Bean property hosting validation constraints
调用javabean类的有参构造函数创建对象
构造函数 PropertyDescriptor(String,class);
注意第一个参数是javabean构造函数的第一个形式参数,第二个参数是已经创建的实类的字节码。
/* 封装对象-反射方式 */ @WebServlet("/servletDemo05") public class ServletDemo05 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.获取所有的数据 Map<String, String[]> map = req.getParameterMap(); //2.封装学生对象 Student stu = new Student(); //2.1遍历集合 for(String name : map.keySet()) { String[] value = map.get(name); try { //2.2获取Student对象的属性描述器 PropertyDescriptor pd = new PropertyDescriptor(name,stu.getClass()); //2.3获取对应的setXxx方法 Method writeMethod = pd.getWriteMethod(); //2.4执行方法 if(value.length > 1) { writeMethod.invoke(stu,(Object)value); }else { writeMethod.invoke(stu,value); } } catch (Exception e) { e.printStackTrace(); } } //3.输出对象 System.out.println(stu); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } } 复制代码
法三:BeanUtils工具类封装
BeanUtils.populate(stu,map);
(实类,参数map)
/* 封装对象-工具类方式 */ @WebServlet("/servletDemo06") public class ServletDemo06 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.获取所有的数据 Map<String, String[]> map = req.getParameterMap(); //2.封装学生对象 Student stu = new Student(); try { BeanUtils.populate(stu,map); } catch (Exception e) { e.printStackTrace(); } //3.输出对象 System.out.println(stu); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } } 复制代码
用流的形式读取请求信息
/* 流对象获取数据 */ @WebServlet("/servletDemo07") public class ServletDemo07 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //字符流(必须是post方式) /*BufferedReader br = req.getReader(); String line; while((line = br.readLine()) != null) { System.out.println(line); }*/ //br.close(); //字节流 ServletInputStream is = req.getInputStream(); byte[] arr = new byte[1024]; int len; while((len = is.read(arr)) != -1) { System.out.println(new String(arr,0,len)); } //is.close(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } } 复制代码
请求正文中中文编码问题
1.POST方式请求
问题:获取请求正文,会有乱码问题。是在获取的时候就已经乱码了。
解决:是request对象的编码出问题了。设置request对象的字符集
request.setCharacterEncoding("编码方式")
它只能解决POST的请求方式,GET方式解决不了
/* 中文乱码 */ @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); } } 复制代码
2.GET方式请求
问题:GET方式:正文在地址栏 username=%D5%C5%C8%FD%D5%C5%C8%FD
是已经被编过一次码了
GET方式请求的正文是在地址栏中,在Tomcat8.5版本及以后,Tomcat服务器已经帮我们解决了,所以不会有乱码问题了。
而如果我们使用的不是Tomcat服务器,或者Tomcat的版本是8.5以前,那么GET方式仍然会有乱码问题,解决方式如下:
使用正确的码表对已经编过码的数据进行解码。就是把取出的内容转成一个字节数组,但是要使用正确的码表。(ISO-8859-1)再使用正确的码表进行编码,把字节数组再转成一个字符串,需要使用正确的码表,是看浏览器当时用的是什么码表。
/** * 在Servlet的doGet方法中添加如下代码 */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); byte[] by = username.getBytes("ISO-8859-1"); username = new String(by,"GBK"); //输出到浏览器:注意响应的乱码问题已经解决了 response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); out.write(username); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } 复制代码
请求转发(与重定向的区别)
重定向特点:两次请求,浏览器行为,地址栏改变,请求域中的数据会丢失
请求转发:一次请求,服务器行为,地址栏不变,请求域中的数据不丢失
请求域的作用范围:当前请求(一次请求),和当前请求的转发之中
请求发送方:
/* 请求转发 */ @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); } } 复制代码
请求接收方:
/* 请求转发 */ @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); System.out.println("servletDemo10执行了..."); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } } 复制代码
开启服务器后进入/servletDemo09之后会在控制台输出
encoding servletDemo10执行了... 复制代码
而此时浏览器的url依然是/servletDemo09,不会跳转
请求重定向
resp.sendRedirect(req.getContextPath() + "/servletDemo07");
请求发送方:
/* 请求重定向 */ @WebServlet("/servletDemo06") public class ServletDemo06 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); } } 复制代码
请求接收方:
/* 请求重定向 */ @WebServlet("/servletDemo07") public class ServletDemo07 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("servletDemo07执行了..."); Object username = req.getAttribute("username"); System.out.println(username); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } } 复制代码
请求包含
需求:把两个Servlet的内容合并到一起来响应浏览器
问题:HTTP协议的特点是一请求,一响应的方式。所以绝对不可能出现有两个Servlet同时响应方式。
解决:把两个Servlet的响应内容合并输出。
/* 请求包含 */ @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); } } 复制代码
控制台输出
servletDemo11执行了...
servletDemo12执行了...
而且浏览器的url依然是/servletDemo11,不会跳转
细节
请求转发的注意事项:负责转发的Servlet,转发前后的响应正文丢失,由转发目的地来响应浏览器。
请求包含的注意事项:被包含者的响应消息头丢失。因为它被包含起来了。
响应
响应对象
关于响应
服务器端收到请求,同时也已经处理完成,把处理的结果告知用户。
在B/S架构中,响应就是把结果带回浏览器。
常用响应对象
协议无关的对象标准是:ServletResponse接口
协议相关的对象标准是:HttpServletResponse接口
常用方法介绍
常用状态码:
状态码 | 说明 |
---|---|
200 | 执行成功 |
302 | 它和307一样,都是用于重定向的状态码。只是307目前已不再使用 |
304 | 请求资源未改变,使用缓存。 |
400 | 请求错误。最常见的就是请求参数有问题 |
404 | 请求资源未找到 |
405 | 请求方式不被支持 |
500 | 服务器运行内部错误 |
状态码首位含义:
状态码 | 说明 |
---|---|
1xx | 消息 |
2xx | 成功 |
3xx | 重定向 |
4xx | 客户端错误 |
5xx | 服务器错误 |
响应对象的使用示例
字节流输出中文问题
项目中常用的编码格式是u8,而浏览器默认使用的编码是gbk。导致乱码!
解决方式一:修改浏览器的编码格式(不推荐,不能让用户做修改的动作)
解决方式二:通过输出流写出一个标签: response.getOutputStream().write("<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");
(常用)
/* 字节流响应消息及乱码的解决 */ @WebServlet("/servletDemo01") public class ServletDemo01 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String str = "你好"; resp.setContentType("text/html;charset=UTF-8"); sos.write(str.getBytes("UTF-8")); sos.close(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } } 复制代码
字符流输出中文问题
/* 字符流响应消息及乱码的解决 */ @WebServlet("/servletDemo02") public class ServletDemo02 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String str = "你好"; //解决中文乱码 resp.setContentType("text/html;charset=UTF-8"); //获取字符流对象 PrintWriter pw = resp.getWriter(); //pw.println(str); pw.write(str); pw.close(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } } 复制代码
响应图片到浏览器
/* 响应图片到浏览器 */ @WebServlet("/servletDemo03") public class ServletDemo03 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //通过文件的相对路径来获取文件的绝对路径 String realPath = getServletContext().getRealPath("/img/hm.png"); System.out.println(realPath); BufferedInputStream bis = new BufferedInputStream(new FileInputStream(realPath)); //获取字节输出流对象 ServletOutputStream sos = resp.getOutputStream(); //循环读写 byte[] arr = new byte[1024]; int len; while((len = bis.read(arr)) != -1) { sos.write(arr,0,len); } bis.close(); sos.close(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } } 复制代码
控制缓存
resp.setDateHeader("Expires",(System.currentTimeMillis()+时间));
/* 缓存 */ @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); } } 复制代码
定时刷新
resp.setHeader("Refresh","定时时间(秒);URL=/虚拟路径/页面路径");
/* 定时刷新 */ @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=/虚拟路径/login.html"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } } 复制代码
文件下载
/* 文件下载 */ @WebServlet("/servletDemo08") public class ServletDemo08 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(); sos.close(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } } 复制代码