Request&Response(重定向&转发)

1、Servlet视图

先了解下框架
在这里插入图片描述

2、request对象

2.1、request的继承结构

虽然我们一直简称为Request,但实际上代表请求的接口为ServletRequest,其中定义了http请求相关的方法。
ServletRequest有一个子类HttpServletRequest,在ServletRequest的基础上增加了很多和http协议相关的方法。
既然Request代表HTTP,那么Http请求相关的请求行请求头实体内容等信息都可以通过这个对象获得。

2.2、request获取客户机信息

getRequestURL方法 – 返回客户端发出请求完整URL
getRequestURI方法 – 返回请求行中的资源名部分
getQueryString方法 – 返回请求行中的参数部分
getRemoteAddr方法 – 返回发出请求的客户机的IP地址
getMethod方法 – 返回客户机的请求方式
getContextPath方法 – 获得当前web应用的虚拟目录名称

2.3、Request获取请求头信息

getHeader(name)方法 — String
getHeaders(String name)方法 — Enumeration
getHeaderNames方法 — Enumeration
getIntHeader(name)方法 — int
getDateHeader(name)方法 — long(日期对应毫秒)

2.4、Request获取请求参数和乱码解决

2.4.1、获取请求参数

getParameter(String name) – String 通过name获得值
getParameterValues(String name) – String[] 通过name获得多值 如: 爱好
getParameterNames() – Enumeration 获得所有请求参数的name
getParameterMap() – Map<String,String[ ]> key :name value: 多值

public class MyServlet03 extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //1.getParameter(String name) -- String 通过name获得值
        String uname = req.getParameter("name");
        String uage = req.getParameter("age");
        System.out.println(uname+"~"+uage);
        //2.getParameterValues(String name) -- String[] 通过name获得多值 如: 爱好
        String[] likes = req.getParameterValues("like");
        System.out.println(likes);
        //3.getParameterNames() -- Enumeration<String> 获得所有请求参数的name
        Enumeration<String> names = req.getParameterNames();
        while(names.hasMoreElements()){
    
    
            String name = names.nextElement();
            String value = req.getParameter(name);
            System.out.println(name+"~"+value);
        }
        //4.getParameterMap() -- Map<String,String[ ]> key :name value: 多值
        Map<String,String[]> map = req.getParameterMap();
        System.out.println(map);
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>

<head>
    <title>request获取请求参数</title>
</head>

<body>

<h1>GET表单</h1><hr/>
<form action="<%=request.getContextPath()%>/myservlet03" method="get">
    用户名:<input type="text" name="uname" />
    地址:<input type="text" name="uaddr" />
    爱好:
    <input type="checkbox" name="like" value="zq">足球
    <input type="checkbox" name="like" value="lq">篮球
    <input type="checkbox" name="like" value="ppq">乒乓球
    <input type="submit"/>
</form>
<h1>POST表单</h1><hr/>
<form action="<%=request.getContextPath()%>/servlet/MyServlet03" method="post">
    用户名:<input type="text" name="uname" />
    地址:<input type="text" name="uaddr" />
    <input type="submit"/>
</form>

</body>
</html>

web.xml

<servlet>
        <servlet-name>ccc</servlet-name>
        <servlet-class>com.bj.MyServlet03</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ccc</servlet-name>
        <url-pattern>/myservlet03</url-pattern>
    </servlet-mapping>

2.4.2、请求参数乱码的原因和解决方法

我们知道计算机中并不能真的存储字符,计算机的底层所有的数据都是由01这样的二进制组成的。
将字符映射成对应二进制的表叫做码表。
而大部分情况下乱码都是由于编码-解码过程中码表不一致产生的。
我们来分析一下请求乱码产生的原因。
首先浏览器是用什么码表来将字符转编码成二进制进行发送的呢?浏览器用什么码表来打开表单页面就用什么编码来发送数据。当前我们的注册页面指定了用utf-8来打开。
在这里插入图片描述
这就决定了浏览器是用utf-8打开的页面,浏览器在提交表单时是用utf-8编码的。
而tomcat默认情况下会使用iso8859-1来进行解码。
我们知道全世界的码表都兼容iso8859-1,所以英文处理是没有问题的。
但是iso8859-1中并没有中文,iso8859-1对于无法处理的字节都使用?替代,所以我们看到打印的是很多的“?”。
那么该如何解决这类乱码呢?
既然这个问题是服务器处理的过程中产生的,那么只要通知服务器不要使用iso8859-1而是使用正确的utf-8解析数据就可以了。
ServletRequest中提供了setCharacterEncoding方法,可以通知服务器在处理请求时使用哪个指定编码。
在这里插入图片描述
但是要注意这行代码必须在获取任何请求参数之前执行,如果之前获取过任何请求参数,则此方法失效。
之前我们的表单是POST提交,我们再将请求方式改为GET,再次测试,发现乱码又出现了。
很奇怪,明明已经设置过编码,为什么对POST可以,但GET不行呢?
原来setCharacterEncoding是通知服务器以什么编码处理请求实体内容中的数据,在POST提交时,数据在请求的实体内容中,这行方法可以起作用,而GET提交时,由于请求参数是赋在地址栏后的,这行代码管不到,所以仍然有乱码。
那么应该如何来处理这种乱码呢?回到问题的本质,由于客户端发送时使用的是utf-8编码而服务器用iso8859-1解码造成了乱码,虽然字符已经乱掉了,但底层的字节仍然是正确的,我们只要将乱码字符getBytes(“iso8859-1”)转换为字节,就是正确的字节,再将这些字节new String(bytes,“utf-8”)按照正确的码表编码,就可以转换回正确的字符了。从而解决了乱码。
这种方式乱码的解决是从原理上手动编解码解决的乱码,对GET和POST方式的提交都有效。

3、Response

3.1、概述

Servlet中应该如何向用户输出数据呢?在doGet和doPost方法的参数中,HttpServletRequest代表的是http请求,而HttServletResponse代表的是http响应。想要获取请求中的信息时使用HttpServletRequest对象,而有数据需要发送给客户端时,就要用到HttpServletResponse对象了。

3.2、继承结构

虽然我们经常简称为response,实际上是ServletResponse接口,其中定义了很多和响应对象相关的方法,HttpServletResponse是ServletResponse接口的子接口,在ServletResponse的基础上增加了很多和http协议相关的方法。

扫描二维码关注公众号,回复: 14721449 查看本文章

3.3、常用方法

1、设置状态码

setStatus(int sc)
setStatus(int sc, String sm)

2、设置响应头

setIntHeader(String name, int value)
setHeader(String name, String value)
setDateHeader(String name, long date)

3、获取输出流

PrintWriter getWriter()
ServletOutputStream getOutputStream();

3.4、输出信息到客户端

3.4.1、输出信息到客户端api

查询api,在Response向外输出数据的方法有如下两个:
PrintWriter getWriter()
ServletOutputStream getOutputStream();
其中getWriter获取的是字符流,可以输出字符数据到客户端。
getOutputStream获取的是字节流,可以输出字节数据到客户端。

3.4.2、响应乱码处理

这个乱码是如何产生的呢?乱码的产生大多是由于编码和解码时的码表不同产生的。
那么服务器是以什么码表来发送数据呢?我们发现乱码是以“?”的形式出现的。根据我们的经验,这种问题多半是由ISO8859-1编码导致的。
确实是的,如果不指定,服务器默认将用iso8859-1进行编码发送数据。浏览器用什么码表打开呢?一般来说如果不指定,浏览器默认会用所在的操作系统的平台码,我们当前的中文系统中,默认就是使用GB2312作为解码码表的。
首先iso8859-1中没有中文,对于无法表示的字符,iso8859-1会用“?”来替代,所以真正发送给浏览器的数据其实是“?”,世界上所有的码表都默认兼容iso8859-1,所以gb2312认识,显示为了“?”。
在这里插入图片描述
在解决这个问题时,可以通过设置response.setCharacterEncoding(“gbk”)来指定服务器发送数据时使用的码表。同时要注意,此行代码必须出现在任何输出数据的代码之前,如果在这行代码之前已经有任何数据写入给了response,则此行代码无效。
设置过后再重新测试。发现仍然是乱码,但不再是“??”而是变成了“涓浗”。
在这里插入图片描述
这种类型的乱码是怎么发生的呢?我们接着分析。服务器用utf-8发送数据给浏览器,而浏览器用平台码(当前为gbk)gbk打开自然产生了乱码。
在这里插入图片描述
这种乱码的产生是由于浏览器没有使用正确的编码打开造成的,那么我们该如何控制浏览器用指定码表打开数据呢?
在http协议中有一个响应头叫做Content-Type可以用来通知浏览器当前服务器发送的数据的格式,如果是字符格式的数据还可以指定解析时使用的码表。所以我们可以通过如下方法通知浏览器用指定码表打开发送的数据,代码如下,经测试没有乱码。
我们通过response.setHeader(“Content-Type”, “text/html;charset=utf-8”);通知服务器发送数据时的码表。
通过response.setCharacterEncoding(“utf-8”);通知浏览器解析时使用的码表。
两码相同就不会有乱码了。

public class Servlet01 extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //浏览器默认使用系统环境打开,系统win10默认使用gbk编码

        /*
        //告知浏览器,当前发送的是文本数据,且采用utf-8编码
        resp.setHeader("Context-Type","text/html;charset=utf-8");
        //告诉服务器,使用指定码表发送数据
        resp.setCharacterEncoding("utf-8");
        */
        
        //既会通知服务器用什么编码发送,又会通知浏览器用什么编码打开,两码一直没有乱码
        resp.setContentType("text/html;charset=utf-8");
        resp.getOutputStream().write("获取输出流成功".getBytes("utf-8"));


    }
}

3.4.5、response输出数据时的细节

1、getOutputStream和getWriter这两个方法互相排斥,调用了其中的任何一个方法后,就不能再调用另一方法。
2、Servlet程序向ServletOutputStream或PrintWriter对象中写入的数据将被Servlet引擎从response里面获取,Servlet引擎将这些数据当作响应消息的正文,然后再与响应状态行和各响应头组合后输出到客户端。
3、Serlvet的service方法结束后,Servlet引擎将检查getWriter或getOutputStream方法返回的输出流对象是否已经调用过close方法,如果没有,Servlet引擎tomcat将调用close方法关闭该输出流对象。

3.5、Response实现定时刷新

在HTTP协议中,提供了refresh响应头,可以命令浏览器经过多少秒刷新页面到哪个地址。
我们经常见到这样的效果,当注册成功后提示“恭喜您注册成功。。3秒后回到主页。。”,紧接着经过3秒回到主页。这就是定时刷新的效果。
通过如下代码实现。
在这里插入图片描述
在这里插入图片描述

3.6、Response实现禁止缓存

浏览器为了减少对服务器的访问,会在第一次访问到资源后进行缓存,之后再访问,就直接用缓存中的数据,减少对服务器的访问。
这种缓存机制,提高了浏览器的响应数度,减少了服务器的访问量,在大多数情况下是好的,但是在某些场景下可能有问题。
比如:验证码 实时的数据 – 火车票余量 股票价格。。。

response.setDateHeader("Expires", 0);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");

4、重定向与转发

4.1、重定向

1、定义:重定向(Redirect)就是通过各种方法将各种网络请求重新定个方向转到其它位置(如:网页重定向、域名的重定向、路由选择的变化也是对数据报文经由路径的一种重定向)

2、特点:
地址栏:显示新的地址
请求次数:2次
请求域中的数据会丢失,因为是2次请求

3、方法

request.sendRedirect();

4.代码

public class MyServlet04 extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        System.out.println("servlet04!!!");
        resp.sendRedirect(req.getContextPath()+"/myservlet05");

    }
}
public class MyServlet05 extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        System.out.println("servlet05!!!");
    }
}
    <servlet>
        <servlet-name>myservlet04</servlet-name>
        <servlet-class>com.bj.MyServlet04</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>myservlet04</servlet-name>
        <url-pattern>/myservlet04</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>myservlet05</servlet-name>
        <servlet-class>com.bj.MyServlet05</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>myservlet05</servlet-name>
        <url-pattern>/myservlet05</url-pattern>
    </servlet-mapping>

4、图解
在这里插入图片描述

4.2、转发

1、定义:一种在服务器内部的资源跳转方式。
2、特点:
浏览器地址栏路径不发生变化。
只能转发到当前服务器内部资源中。
转发是一次请求。
请求域中数据不会丢失。
可以用request域传递数据
3、方法:

request.getRequestDispatcher("路径").forward(request, response);

4、代码
只需将myservlet04做修改

public class MyServlet04 extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        System.out.println("servlet04!!!");
        //resp.sendRedirect(req.getContextPath()+"/myservlet05");
        req.getRequestDispatcher("/myservlet05").forward(req,resp);

    }
}

5、图解
在这里插入图片描述

4.3、转发和重定向的区别

转发的地址栏不变,而重定向变成转发后的资源。
转发是一次请求,而重定向是两次请求。所以一般可以说重定向是2次转发。
转发只能在自己内部服务器资源内相互转发,而重定向可以访问其他站点。

猜你喜欢

转载自blog.csdn.net/stepleavesprint/article/details/127788268