Servlet2-request、respone、请求转发、重定向

request对象

作用:request对象中封存了当前请求的所有请求信息

Request 继承体系

1、Tomcat需要解析请求数据,封装为request对象,并且创建request对象传递到service方法中
2、使用request对象,查阅JavaEE API文档的HttpServletRequest接口

Request 获取请求数据

获取请求行数据

GET /request-demo/req1?username=zhangsan HTTP/1.1

  String getMethod():获取请求方式: GET
  String getContextPath():获取虚拟目录(项目访问路径): /request-demo
  StringBuffer getRequestURL(): 获取URL(统一资源定位符):http://localhost:8080/request-demo/req1
  String getRequestURI():获取URI(统一资源标识符): /request-demo/req1
  String getQueryString():获取请求参数(GET方式): username=zhangsan&password=123
  String getScheme()://获取协议

获取请求头数据

User-Agent: Mozilla/5.0 Chrome/91.0.4472.106
  String getHeader(String name):根据请求头名称,获取值
  Enumeration<String> getHeaderNames():返回请求头的键名的枚举集合
 1 public class RequestServlet extends HttpServlet {
 2     @Override
 3     protected void service(HttpServletRequest req, HttpServletResponse resp)
 4             throws ServletException, IOException {
 5         //获取请求头数据
 6                 //获取请求方式:/GET
 7                     String method = req.getMethod();
 8                     System.out.println(method); 
 9                 //获取虚拟目录(项目访问路径):/request-demo
10                     String contextPath = req.getContextPath();
11                     System.out.println(contextPath);
12                 //获取请求URL(统一资源定位符):http://localhost:8080/request-demo/req1
13                     StringBuffer url=req.getRequestURL();
14                     System.out.println(url);
15                 //获取URI(统一资源标识符): /request-demo/req1
16                     String uri=req.getRequestURI();
17                     System.out.println(uri);
18                 //获取请求参数(GET方式): username=zhangsan
19                     String queryString = req.getQueryString();
20                     System.out.println(queryString);
21                 //获取协议
22                     String h=req.getScheme();
23                     System.out.println(h);
24         //获取请求行数据
25                 //获取指定的请求行信息
26                     String value=req.getHeader("aaa");
27                     System.out.println(value);
28                 //获取所有的请求行的键的枚举
29                     Enumeration e = req.getHeaderNames();
30                     while(e.hasMoreElements()){
31                         String name=(String) e.nextElement();
32                         String value2=req.getHeader(name);
33                         System.out.println(name+":"+value2);
34                     }      
35     }
36 }

获取请求体数据

浏览器在发送GET请求的时候是没有请求体的,所以需要把请求方式变更为POST

username=superbaby&password=123

  ServletInputStream getInputStream():获取字节输入流
  BufferedReader getReader():获取字符输入流

 1 @WebServlet("/req1")
 2 public class RequestDemo1 extends HttpServlet {
 3     @Override
 4     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
 5     }
 6     @Override
 7     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
 8          //获取post 请求体:请求参数
 9         //1. 获取字符输入流
10         BufferedReader br = req.getReader();
11         //2. 读取数据
12         String line = br.readLine();
13         System.out.println(line);
14     }
15 }

注:BufferedReader流是通过request对象来获取的,当请求完成后request对象就会被销毁,request对象被销毁后,BufferedReader流就会自动关闭,所以此处就不需要手动关闭流了。

获取请求参数的通用方式

请求参数获取方式:
  GET方式:String getQueryString()
  POST方式:BufferedReader getReader()
Request通用方式获取请求参数:
  Map<String, String[ ]> getParameterMap():获取所有参数Map集合
  String[ ] getParameterValues(String name) :根据名称获取参数值(数组)
  String getParameter(String name):根据名称获取参数值(单个值)
  Enumeration<String> getParameterNames():返回所有用户请求数据的枚举集合
  注:如果要获取的请求数据不存在,不会报错,返回null。

 1 @WebServlet("/req2")
 2 public class RequestDemo2 extends HttpServlet {
 3     @Override
 4     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
 5         //GET请求逻辑
 6         //1. 获取所有参数的Map集合
 7         Map<String, String[]> map = req.getParameterMap();
 8         for (String key : map.keySet()) {
 9             // username:zhangsan lisi
10             System.out.print(key+":");
11             //获取值
12             String[] values = map.get(key);
13             for (String value : values) {
14                 System.out.print(value + " ");
15             }
16             System.out.println();
17         }
18         
19         String[] hobbies = req.getParameterValues("hobby");
20         for (String hobby : hobbies) {
21             System.out.println(hobby);
22         }
23         
24         String username = req.getParameter("username");
25         String password = req.getParameter("password");
26         System.out.println(username);
27         System.out.println(password);
28     }
29 
30     @Override
31     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
32         this.doGet(req,resp);    
33     }
34 }

IDEA快速创建Servlet

由于格式固定,所以我们可以使用IDEA提供的模板来制作一个Servlet的模板,这样我们后期在创建Servlet的时候就会更高效,具体如何实现:
1、按照自己的需求,修改Servlet创建的模板内容,点击File --> Setting

2、使用servlet模板创建Servlet类

  -->  

请求参数中文乱码问题

请求参数如果存在中文数据,则会乱码

POST请求解决方案

分析POST出现中文乱码的原因:
  POST的请求参数是通过request的getReader()来获取流中的数据;
  TOMCAT在获取流的时候采用的编码是ISO-8859-1;
  ISO-8859-1编码是不支持中文的,所以会出现乱码。
解决方案:
  因为页面设置的编码格式为UTF-8;把TOMCAT在获取流数据之前的编码设置为UTF-8;
  通过request.setCharacterEncoding("UTF-8")设置编码,UTF-8也可以写成小写。

 1 @WebServlet("/req4")
 2 public class RequestDemo4 extends HttpServlet {
 3     @Override
 4     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 5         //1. 解决乱码: POST getReader()
 6         //设置字符输入流的编码,设置的字符集要和页面保持一致
 7         request.setCharacterEncoding("UTF-8");
 8        //2. 获取username
 9        String username = request.getParameter("username");
10        System.out.println(username);
11     }
12 
13     @Override
14     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
15         this.doGet(request, response);
16     }
17 }

GET请求解决方案

POST请求的中文乱码解决方案为什么不适用GET请求:
  GET请求获取请求参数的方式是 request.getQueryString();
  POST请求获取请求参数的方式是 request.getReader();
  request.setCharacterEncoding("utf-8")是设置request处理流的编码;
  getQueryString方法并没有通过流的方式获取数据。

分析GET请求出现乱码的原因:
  1、浏览器通过HTTP协议发送请求和数据给后台服务器(Tomcat)
  2、浏览器在发送HTTP的过程中会对中文数据进行URL编码
  3、在进行URL编码的时候会采用页面<meta>标签指定的UTF-8的方式进行编码,"张三"编码后的结果为`%E5%BC%A0%E4%B8%89`
  4、后台服务器(Tomcat)接收到`%E5%BC%A0%E4%B8%89`后会默认按照ISO-8859-1进行URL解码
  5、由于前后编码与解码采用的格式不一样,就会导致后台获取到的数据为乱码。

  

  URL编码:
    1. 将字符串按照编码方式转为二进制
    2. 每个字节转为2个16进制数并在前边加上%
    `张三`按照UTF-8的方式转换成二进制的结果为:(用utf-8方式编码:一个汉字占3个字节,两个汉字6个字节,每个字节8位,一共6*8=48位)

    

    URL编码实现方式:
      编码:URLEncoder.encode(str, "utf-8");
      解码:URLDecoder.decode(s, "ISO-8859-1");

 1 public class URLDemo {
 2   public static void main(String[] args) throws UnsupportedEncodingException {
 3         String username = "张三";
 4         //1. URL编码
 5         String encode = URLEncoder.encode(username, "utf-8");
 6         System.out.println(encode);    //打印:%E5%BC%A0%E4%B8%89
 7         //2. URL解码
 8         //String decode = URLDecoder.decode(encode, "utf-8");//打印:张三
 9         String decode = URLDecoder.decode(encode, "ISO-8859-1");
10         System.out.println(decode); //打印:`å¼ ä¸ `
11 
12         //3. 转换为字节数据,编码
13         byte[] bytes = decode.getBytes("ISO-8859-1");
14         for (byte b : bytes) {
15             System.out.print(b + " ");
16         }
17         //此处打印的是:-27 -68 -96 -28 -72 -119
18         //4. 将字节数组转为字符串,解码
19         String s = new String(bytes, "utf-8");
20         System.out.println(s); //此处打印的是张三
21     }
22 }

解决方案:
  先编码,再解码。通用方式(GET/POST都可用此方法解决)
    new String(username.getBytes("ISO-8859-1"),"UTF-8");

 1 @WebServlet("/req4")
 2 public class RequestDemo4 extends HttpServlet {
 3     @Override
 4     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 5         //1. 解决乱码:POST,getReader()
 6         //request.setCharacterEncoding("UTF-8");//设置字符输入流的编码
 7 
 8         //2. 获取username
 9         String username = request.getParameter("username");
10         System.out.println("解决乱码前:"+username);
11 
12         //3. GET,获取参数的方式:getQueryString
13         // 乱码原因:tomcat进行URL解码,默认的字符集ISO-8859-1
14         /*//3.1 先对乱码数据进行编码:转为字节数组
15         byte[] bytes = username.getBytes(StandardCharsets.ISO_8859_1);
16         //3.2 字节数组解码
17         username = new String(bytes, StandardCharsets.UTF_8);*/
18 
19         username  = new String(username.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8);
20         System.out.println("解决乱码后:"+username);
21     }
22     @Override
23     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
24         this.doGet(request, response);
25     }
26 }

注:Tomcat 8.0 之后,已将GET请求乱码问题解决,设置默认的解码方式为UTF-8

请求转发(forward)

作用:一种在服务器内部的资源跳转方式。实现多个servlet联动操作处理请求,这样避免代码冗余,让servlet的职责更加明确。
使用:
  req.getRequestDispatcher("要转发的地址").forward(req, resp);
  地址:相对路径,直接书写servlet的别名即可。
特点:
  浏览器地址栏路径不发生变化;
  只能转发到当前服务器的内部资源;
  一次请求,可以在转发的资源间使用request共享数据。
注意:
  请求转发后直接return结束即可。

reuqest作用域

问题:使用请求转发后,数据怎么从一个servlet流转给另一个servlet?
解决:使用request对象的作用域
使用:
  void setAttribute(String name, Object o):存储数据到 request域中
  Object getAttribute(String name):根据 key,获取值
  void removeAttribute(String name):根据 key,删除该键值对
作用:解决了一次请求内不同的servlet的数据(请求数据+其他数据)共享问题。
注意:使用Request对象进行数据流转,数据只在一次请求内有效。
特点:
  服务器创建
  每次请求都会创建
  生命周期一次请求

respone对象

作用:  用来响应数据到浏览器的一个对象

Response 设置响应数据

设置响应行

HTTP/1.1 200 OK

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

设置响应头
Content-Type: text/html
  void setHeader(String name,String value);//在响应头中添加响应信息,但是同键会覆盖
  void addHeader(String name,String value);//在响应头中添加响应信息,但是不会覆盖。
设置响应状态
  sendError(int num,String msg);//自定义响应状态码。
 1 public class ResponseServlet extends HttpServlet {
 2     @Override
 3     protected void service(HttpServletRequest req, HttpServletResponse resp)
 4             throws ServletException, IOException {
 5         //响应处理结果
 6             //设置响应头
 7             resp.setHeader("mouse", "two fly birds");
 8             resp.setHeader("mouse", "su");
 9             resp.addHeader("key", "thinkpad");
10             resp.addHeader("key", "wollo");
11             //设置响应状态码
12             resp.sendError(404, "this Method is not supported");       
13     }
14 }
设置响应体
<html><head>head><body></body></html>

  PrintWriter getWriter():获取字符输出流
  ServletOutputStream getOutputStream():获取字节输出流

Response响应字符数据

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

 1 @WebServlet("/resp3")
 2 public class ResponseDemo3 extends HttpServlet {
 3     @Override
 4     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 5         response.setContentType("text/html;charset=utf-8");
 6         //1. 获取字符输出流
 7         PrintWriter writer = response.getWriter();
 8          writer.write("aaa");
 9     }
10     @Override
11     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
12         this.doGet(request, response);
13     }
14 }

返回一串html字符串,并且能被浏览器解析

1 PrintWriter writer = response.getWriter();
2 //content-type,告诉浏览器返回的数据类型是HTML类型数据,这样浏览器才会解析HTML标签
3 response.setHeader("content-type","text/html");
4 writer.write("<h1>aaa</h1>");

返回一个中文的字符串`你好`,需要注意设置响应数据的编码为`utf-8`
  设置响应编码格式:
    resp.setContentType("text/html;charset=utf-8");

1 //设置响应的数据格式及数据的编码
2 response.setContentType("text/html;charset=utf-8");
3 PrintWriter writer = response.getWriter();
4 writer.write("你好");

注意:一次请求响应结束后,response对象就会被销毁掉,所以不要手动关闭流。

Response 响应字节数据

通过Response对象获取字节输出流:ServletOutputStream outputStream = resp.getOutputStream();
通过字节输出流写数据: outputStream.write(字节数据);

 1 @WebServlet("/resp4")
 2 public class ResponseDemo4 extends HttpServlet {
 3     @Override
 4     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 5         //1. 读取文件
 6         FileInputStream fis = new FileInputStream("d://a.jpg");
 7         //2. 获取response字节输出流
 8         ServletOutputStream os = response.getOutputStream();
 9         //3. 完成流的copy
10         byte[] buff = new byte[1024];
11         int len = 0;
12         while ((len = fis.read(buff))!= -1){
13             os.write(buff,0,len);
14         }
15         fis.close();
16     }
17 
18     @Override
19     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
20         this.doGet(request, response);
21     }
22 }

上述代码中,对于流的copy的代码还是比较复杂的,所以我们可以使用别人提供好的方法来简化代码的开发,具体的步骤是:
1.pom.xml添加依赖

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

2.调用工具类方法
  IOUtils.copy(输入流,输出流);

 1 @WebServlet("/resp4")
 2 public class ResponseDemo4 extends HttpServlet {
 3     @Override
 4     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 5         //1. 读取文件
 6         FileInputStream fis = new FileInputStream("d://a.jpg");
 7         //2. 获取response字节输出流
 8         ServletOutputStream os = response.getOutputStream();
 9         //3. 完成流的copy
10         IOUtils.copy(fis,os);
11         fis.close();
12     }
13 
14     @Override
15     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
16         this.doGet(request, response);
17     }
18 }

请求重定向(Redirect)

一种资源跳转方式。解决了表单重复提交的问题,以及当前servlet无法处理的请求的问题。
实现方式:
方式一:resp.setStatus(302);
    resp.setHeader("location","资源B的路径");

方式二:resp.sendRedirect("资源B的路径");
示例:
  resp.sendRedirect("/login/main");
特点:
  浏览器地址栏路径发生变化;
  可以重定向到任意位置的资源(服务器内部、外部均可);
  两次请求,不能在多个资源使用request共享数据。
时机:
  如果请求中有表单数据,而数据又比较重要,不能重复提交,建议使用重定向。
  如果请求被Servlet接收后,无法进行处理,建议使用重定向定位到可以处理的资源。
代码:

 1 @WebServlet("/resp1")
 2 public class ResponseDemo1 extends HttpServlet {
 3     @Override
 4     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 5         //重定向
 6         /*//1.设置响应状态码 302
 7         response.setStatus(302);
 8         //2. 设置响应头 Location
 9         response.setHeader("Location","/request-demo/resp2");*/
10         
11         //重定向
12         resposne.sendRedirect("/request-demo/resp2");
13     }
14 
15     @Override
16     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
17         this.doGet(request, response);
18     }
19 }

请求重定向 vs  请求转发 区别

请求重定向:(浏览器行为)
  1)地址栏改变,改变为重定向到的地址
  2)可以重定向到当前web应用,其他web应用,甚至是其他站点资源。
  3)处于两次不同的请求。不可以使用request域对象来共享数据。
请求转发:(服务器行为)
  1)地址栏不会改变。
  2)只能转发到当前web应用内部资源。
  3)处于同一次请求。可以使用request域对象来共享数据

请求重定向路径 vs 请求转发路径

重定向路径:(加虚拟目录)
  相对路径:从当前请求的路径查找资源的路径
    相对路径如果servlet的别名中包含目录,会造成重定向资源查找失败。
  绝对路径:第一个/表示服务器根目录
    /虚拟项目名/资源路径
    resp.sendRedirect("/mg/login.jsp");//虚拟项目名为mg,login.jsp在WebContent下
请求转发:(不加虚拟目录)
  /表示项目根目录。
  req.getRequestDispatcher("/资源路径").forward(req, resp);
  req.getRequestDispatcher("/user/showUser.jsp").forward(req, resp);//虚拟项目名为mg,user是WebContent下的文件夹,showUser.jsp在user下

明确路径谁使用:
  浏览器使用:需要加虚拟目录(项目访问路径)
  服务端使用:不需要加虚拟目录

可以动态获取项目访问的虚拟路径,从而降低代码的耦合度:

 1 @WebServlet("/resp1")
 2 public class ResponseDemo1 extends HttpServlet {
 3     @Override
 4     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 5         //简化方式完成重定向
 6         //动态获取虚拟目录
 7         String contextPath = request.getContextPath();
 8         response.sendRedirect(contextPath+"/resp2");
 9     }
10 
11     @Override
12     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
13         this.doGet(request, response);
14     }
15 }
posted @ 2020-03-17 23:24  溯鸣  阅读(286)  评论(0编辑  收藏  举报