一、Servlet 介绍

  1. Servlet 是 JavaEE 规范 (接口) 之一
  2. Servlet 是 JavaWeb 三大组件之一,三大组件分别是 Servlet 程序、Filter 过滤器、Listener 监听器
  3. Servlet 是运行在服务器上的一个 Java 程序,可以接收客户端发来的请求,并响应数据给客户端

    手动实现 Servlet 程序

  4. 编写一个类实现 Servlet 接口,并重写 service 方法处理请求、响应数据

  5. 在 WEB-INF 文件夹中的 web.xml 文件中配置 Servlet 程序的访问地址

代码演示:1. 在 src 下创建一个 TestServlet 类

  1. public class TestServlet implements Servlet {
  2. @Override
  3. public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
  4. System.out.println("TestServlet被访问了!");
  5. }
  6. }

代码演示:2. 在 web.xml 中的配置

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  5. version="4.0">
  6. <!--内容写在<web-app></web-app>中-->
  7. <!--servlet标签给Tomcat配置Servlet程序-->
  8. <servlet>
  9. <!--servlet-name标签给Servlet程序起一个别名(一般是类名)-->
  10. <servlet-name>TestServlet</servlet-name>
  11. <!--servlet-class标签是Servlet程序的全类名-->
  12. <servlet-class>com.qizegao.servlet.TestServlet</servlet-class>
  13. </servlet>
  14. <!--servlet-mapping标签是servlet的映射,给Servlet程序配置访问地址-->
  15. <servlet-mapping>
  16. <!--此时servlet-name标签要与上一个servlet-name标签中相同-->
  17. <servlet-name>TestServlet</servlet-name>
  18. <!--url-pattern标签配置访问地址:
  19. /在服务器解析时表示为Tomcat的工程路径(在Edit Configurations中修改),
  20. /Test代表默认路径/Test,即http://localhost:8080/MyTest/Test,
  21. 也就是说在浏览器的地址栏中输入上述路径访问到的是TestServlet类-->
  22. <url-pattern>/Test</url-pattern> //任意起名,用这个名字来访问指定类
  23. </servlet-mapping>
  24. </web-app>
  25. 运行结果:
  26. 点击绿色按钮开启Tomcat服务器之后,会自动打开默认的地址http://localhost:8080/MyTest,
  27. 在地址栏继续输入/Test,会执行指定类的service方法,控制台输出:TestServlet被访问了!

Servlet 程序的访问原理

Servlet - 图1

Servlet 程序的常见错误

1. url-pattern 中配置的路径没有以斜杠打头

Servlet - 图2

2. servlet-name 中的两个映射值不一致

Servlet - 图3

3. servlet-class 标签的全类名配置错误

Servlet - 图4

Servlet 的生命周期

Servlet 程序被访问以后按以下顺序执行:

  1. 执行 Servlet 程序的构造方法
  2. 执行 init 方法
  3. 执行 service 方法
  4. 执行 destroy 方法
    其中 1 和 2 是在初次访问并创建 Servlet 程序时会执行 (每次启动服务只执行一次),第 3 步每次刷新 (访问) 都会执行,第 4 步点击停止时会执行一次

    GET 和 POST 请求的不同处理

    代码演示:1. 在 src 目录下创建此类

    public class TestServlet implements Servlet {
     @Override
     public void service(ServletRequest servletRequest, ServletResponse servletResponse) 
         throws ServletException, IOException {
         //转换的原因:HttpServletRequest有getMethod方法,可以得到请求的类型
         HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
    
         String method = httpServletRequest.getMethod();
    
         //method值为GET或POST,取决于表单提交的method
         if (method.equals("POST")){
             System.out.println("POST方式");
         } else if (method.equals("GET")) {
             System.out.println("GET方式");
         }
     }
    

    代码演示:2. 在 web 目录下创建 Test.html 页面

    <body>
     <form action="http://localhost:8080/MyTest/Test" method="post">
         <!--action属性值与web.xml中的<url-pattern>标签内容一致,用于访问到service方法-->
         <input type="submit">
     </form>
    </body>
    

    运行结果:服务器启动之后,在浏览器的地址栏中的后缀加上 Test.html,即可访问此页面,点击提交标签,即可跳转到 http://localhost:8080/MyTest/Test,执行 service 方法,控制台输出:POST 方式

    继承 HttpServlet 类实现 Servlet 程序

    在实际的项目开发中,都是使用继承 HttpServlet 类实现 Servlet 程序的方式,步骤如下:

  5. 编写一个类继承 HttpServlet 类

  6. 根据需求重写 doGet 或 doPost 方法,由 service 方法根据表单的 method 属性值调用二者之一
  7. 到 web.xml 中配置 Servlet 程序的访问地址

代码演示:1. 在 src 目录下创建此类

public class TestServlet2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doGet方法执行");
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doPost方法执行");
    }
}
//HttpServlet的service方法会根据method方式调用二者之一

代码演示:2. 在 web.xml 中继续写配置

<!--不用删除原来的servlet标签,在<web-app>标签中继续写servlet标签-->
<servlet>
    <servlet-name>TestServlet2</servlet-name>
    <servlet-class>com.qizegao.servlet.TestServlet2</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>TestServlet2</servlet-name>
    <url-pattern>/Test2</url-pattern>
</servlet-mapping>

代码演示:3. 在 web 中创建 Test.html 页面

<body>
    <form action="http://localhost:8080/MyTest/Test2" method="post">
        <!--action属性值与web.xml中的<url-pattern>标签内容一致,用于访问到service方法-->
        <input type="submit">
    </form>
</body>

运行结果:服务器启动之后,在浏览器的地址栏中的后缀加上 Test.html,即可访问此页面,点击提交标签, 即可跳转到http://localhost:8080/MyTest/Test2,执行 service 方法,进而执行 doPost 方法

使用 IDEA 创建 Servlet 程序

Servlet - 图5
Servlet - 图6
创建之后,会在包下创建此类 (类名为全类名中的类名),此类继承于 HttpServlet 类,其中有 doGet 和 doPost 方法 (无函数体),并自动的在 web.xml 文件中补充新的标签,但无标签,需自己补充

Servlet 接口的继承体系

Servlet - 图7

二、ServletConfig 接口

从名字来看,得知此接口中是 Servlet 程序的配置信息

  • Servlet 程序和 ServletConfig 对象都是由 Tomcat 负责创建,编程人员负责使用
  • Servlet 程序默认是第一次访问时创建,每个 Servlet 程序创建时对应的创建 ServletConfig 对 象,二者相互对应,某个 Servlet 程序只可以获得他对应的 ServletConfig 对象,无法获得别的 Servlet 程序的 ServletConfig 对象

    ServletConfig 接口的三大作用

  • 可以获取 Servlet 程序的别名 (即 web.xml 的的内容)

  • 可以获取 web.xml 的初始化参数的值
  • 可以获取 ServletContext 对象

代码演示:1. 在 web.xml 中继续写配置 (写在标签中)

<servlet>
    <servlet-name>TestServlet4</servlet-name>
    <servlet-class>com.qizegao.servlet.TestServlet4</servlet-class>
    <!--<init-param>是初始化参数,每个servlet标签中都可以有,一个servlet标签中可以有多个-->
    <init-param>
        <!--参数名-->
        <param-name>username</param-name>
        <!--参数值-->
        <param-value>root</param-value>
    </init-param>
    <init-param>
        <param-name>url</param-name>
        <param-value>jdbc:mysql:localhost:3306/test</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>TestServlet4</servlet-name>
    <url-pattern>/Test4</url-pattern>
</servlet-mapping>

代码演示:2. 在 src 目录下创建此类

public class TestServlet4 extends HttpServlet {
    @Override  //使用init方法的原因:1.一定会执行 2.参数中有ServletConfig对象
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        //1.获取Servlet程序的别名
        System.out.println("此程序的别名是:" + config.getServletName());
        //2.获取初始化参数init-param,该方法获取指定的参数名的值
        System.out.println("初始化参数username的值是:" 
                           + config.getInitParameter("username"));
        System.out.println("初始化参数url的值是:" + config.getInitParameter("url"));
        //3.获取ServletContext对象
        System.out.println(config.getServletContext());
    }
    /*运行结果(浏览器地址栏输入http://localhost:8080/MyTest/Test4):
      此程序的别名是:TestServlet4
      初始化参数username的值是:root
      初始化参数url的值是:jdbc:mysql:localhost:3306/test
      org.apache.catalina.core.ApplicationContextFacade@64d62c61 */
}

注意:重写 init 方法时,必须要在函数体内写:super.init(config);
原因:父类 GenericServlet 中的 init 方法将参数 config 保存起来,子类若不调用则无法保存

三、ServletContext 接口

  1. ServletContext 接口表示 Servlet 上下文对象
  2. 一个 web 工程只有一个 ServletContext 对象实例
  3. ServletContext 是在 web 工程启动时创建,在 web 工程停止时销毁
  4. ServletContext 对象是一个域对象
    域对象:像 Map 一样存取数据的对象称为域对象,域指的是存取数据的操作范围,

ServletContext 的域是整个 web 工程
Servlet - 图8

  1. ServletContext 接口的四个作用
  • 获取 web.xml 中配置的上下文参数标签中的值
  • 获取当前工程的路径,格式:/ 工程路径,也就是 Edit Configurations 中 Deployment 中的 Application context 的内容 (即地址中 8080 之后,具体的打开的页面之前的内容)
  • 获取工程部署后在硬盘上的绝对路径
  • 像 Map 一样存取数据

代码演示:1. 在 web.xml 中继续写配置 (在标签中)

<!--<context-param>标签中是上下文参数,属于整个web工程-->
<!--可以有多个,写在第一个<servlet>标签之外(之上)-->
<context-param>
    <param-name>username</param-name>
    <param-value>root</param-value>
</context-param>
<context-param>
    <param-name>password</param-name>
    <param-value>root</param-value>
</context-param>
<!--并写出下方的类对应的<servlet标签>-->

代码演示:2. 在 src 目录下创建此类

public class MyContextServlet extends HttpServlet {
    //默认执行doGet方法,故只重写doGet方法
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException {
        //GenericServlet类中有public ServletConfig getServletConfig()方法,返回this.config
        ServletContext servletContext = getServletConfig().getServletContext();

        //1.获取web.xml中配置的上下文参数<context-param>标签中的值
        String username = servletContext.getInitParameter("username");
        System.out.println("context-param参数的username值是" + username);
        /* 运行结果:context-param参数的username值是root */
        System.out.println("context-param参数的password值是" 
                           + servletContext.getInitParameter("password"));
        /* 运行结果:context-param参数的password值是root */

        //2.获取当前工程的路径
        System.out.println("当前工程路径:" + servletContext.getContextPath());
        /* 运行结果:当前工程路径:/MyTest */

        //3.获取工程部署后在硬盘上的绝对路径
        /* /斜杠(默认路径)对应IDEA代码的web目录 */
        System.out.println("工程部署的路径是:" + servletContext.getRealPath("/"));
        /* 运行结果:E:\IDEA WorkSpace\FirstWeb\out\artifacts\FirstWeb_war_exploded\ */
        //在web目录下创建一个css文件夹
        System.out.println("工程下css目录的绝对路径是:" 
                           + servletContext.getRealPath("/css"));
        /* 运行结果:E:\IDEA WorkSpace\FirstWeb\out\artifacts\FirstWeb_war_exploded\css */
        //在web目录下创建一个img文件夹,里面放1.gif文件
        System.out.println("工程下img目录1.gif的绝对路径是:" 
                           + servletContext.getRealPath("/img/1.gif"));
        /* 输出:E:\IDEA WorkSpace\FirstWeb\out\artifacts\FirstWeb_war_exploded\img\1.gif*/
    }
}
//记得要在浏览器的地址栏输入http://localhost:8080/MyTest/MyContextServlet才可访问到此类

代码演示:3. 在 src 中创建此类,并在 web.xml 中写对应的配置信息

public class Servlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException {
        //GenericServlet类中有public ServletContext getServletContext()方法,
        //return this.getServletConfig().getServletContext();
        ServletContext servletContext = getServletContext();
        System.out.println(servletContext);
        /* 输出:org.apache.catalina.core.ApplicationContextFacade@2711c490 */
        servletContext.setAttribute("key1","value1");
        System.out.println(servletContext.getAttribute("key1"));
        /* 输出:value1 */
    }
}

注意:一个 web 工程只会创建一个 ServletContext 对象实例,换其他类输出 servletContext 得到的结果与上述相同,且一旦给此对象赋值,即使换另一个类 getAttribute(key1),得到的结果也是 value1

四、HTTP 协议

含义
所谓协议指的是双方或多方相互约定好,都要遵守的规则,而 HTTP 协议指的是客户端和 服务器之间通信时发送的数据需要遵守的规则,HTTP 协议中的数据又称为报文

请求的 HTTP 协议格式

请求分为 GET 请求和 POST 请求两种:
(1) GET 请求:由请求行、请求头两部分组成,如下图所示:
Servlet - 图9
内容分析:
请求行:
①请求的方式:GET
②请求的资源路径:/06_servlet/a.html
③请求的协议的版本号:HTTP/1.1

请求头:
①Accept:告诉服务器,客户端可以接收的数据类型
②Accept-Language:告诉服务器,客户端可以接收的语言类型:
zh_CN:中文中国
en_US:英文美国
③User-Agent:代表客户端浏览器的信息
④Accept-Encoding:告诉服务器,客户端可以接收的数据编码 (压缩) 格式
⑤Host:表示请求时的服务器 ip 和端口号
⑥Connection:告诉服务器,当前连接如何处理:
Keep-Alive:回传完数据不要马上关闭,保持一小段时间的连接
Closed:回传完数据马上关闭

(2) POST 请求:由请求行、请求头、空行、请求体组成,如下图所示:
Servlet - 图10

内容分析 (仅分析与 GET 请求的不同之处):
请求头:
①Referer:表示请求发起时,浏览器地址栏中的地址
②Content-Type:表示发送的数据的类型:
i. application/x-www-form-ur lencoded:表示提交的数据的格式是 name=value&name=value,然后对其进行 url 编码,url 编码是把非英文内容转换为:%xx%xx
ii. multipart/form-data:表示以多段的形式提交数据给服务器,即以流的形式提交,用于上传
③Content-Length:表示发送的数据的长度
④Cache-Control:表示如何控制缓存,no-cache 不缓存

响应的 HTTP 协议格式

Servlet - 图11

常见的响应码

200 表示请求成功
302 表示请求重定向
404 表示服务器收到请求,但是请求的数据不存在(请求地址错误)
500 表示服务器收到请求,但是服务器内部错误(代码错误)

MIME 类型说明

MIME 是 HTTP 协议中的数据类型,格式是:大类型 / 小类型,并与某一种文件的扩展名相对应:
Servlet - 图12

谷歌浏览器查看 HTTP 协议

首先点击 F12:
Servlet - 图13
注意点:

  1. 到目前为止除了 form 标签中 method=post 之外,其余均为 GET 请求
  2. 标签不一定与标签相邻,只要根据能对应上即可
  3. 默认地址值与工程路径是两个概念,上述只是将默认地址值修改为工程路径,即上述斜杠 等代表访问到的是工程路径:http://localhost:8080 / 工程名,而非默认路径

    五、HttpServletRequest 类

  4. HttpServletRequest 类的作用
    每次只要有请求进入 Tomcat 服务器,Tomcat 服务器就会把请求发来的 HTTP 协议信息解析好封装到 Request 对象中,然后传递到 service 方法中 (调用 doGet 或 doPost 方法) 供编程人员使用,编程人员通过 HttpServletRequest 对象,可以获取到请求的所有信息

  5. HttpServletRequest 类的常用方法
    **getRequestURI()**:获取请求的资源路径
    **getRequestURL()**:获取请求的绝对路径
    **getRemoteHost()**:获取客户端的 ip 地址
    **getHeader()**:获取请求头
    **getParameter()**:获取请求的参数
    **getParameterValues()**:获取请求的参数 (多个值时使用)
    **getMethod()**:获取请求的方式 (GET 或 POST)
    **setAttribute(key, value)**:设置域数据
    **getAttribute(key)**:获取域数据
    **getRequestDispatcher()**:获取请求转发对象

代码示例 (在 src 下创建此类):

public class RequestAPI extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取请求的资源路径
        String requestURI = request.getRequestURI();

        //2.获取请求的绝对路径
        StringBuffer requestURL = request.getRequestURL();

        //3.获取客户端的ip地址
        String requestRemoteHost = request.getRemoteHost();

        //4.获取请求头
        String requestHeader = request.getHeader("User-Agent");

        //5.获取请求的方式
        String requestMethod = request.getMethod();

        //输出
        System.out.println(requestURI); /* /MyTest/RequestAPI */
        System.out.println(requestURL); /* http://localhost:8080/MyTest/RequestAPI */
        System.out.println(requestHeader); //Mozilla/5.0 (Windows NT 10.0; Win64; x64)...
        System.out.println(requestMethod); //GET
        System.out.println(requestRemoteHost); //127.0.0.1
        /*在IDEA中,使用localhost访问时得到的客户端ip地址是127.0.0.1
                   使用真实ip访问时,得到的客户端地址是真实的客户端ip地址 */
    }
}
  1. 获取请求参数
    代码示例:1. 在 web 目录下创建 form.html 页面 (不可在 WEB-INF 中创建,无法访问到):
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>form</title>
</head>
<body>
    <form action="http://localhost:8080/MyTest/RequestAPI2" method="post">
        用户名:<input type="text" name="username"><br/>
        密码:<input type="password" name="password"><br/>
        兴趣爱好:<input type="checkbox" name="hobby" value="cpp">C++
                 <input type="checkbox" name="hobby" value="Java">Java
                 <input type="checkbox" name="hobby" value="JS">JavaScript<br/>
                 <input type="submit">
    </form>
</body>
</html>

Servlet - 图14

代码示例:2. 在 src 下创建此类

public class RequestAPI2 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //doPost方法会出现中文请求乱码问题
        //需要在获取任何参数之前修改字符编码集,而不仅仅获取中文参数时才修改:
        request.setCharacterEncoding("UTF-8");
        //获取请求的参数(此方法参数中放name属性值,得到value属性值)
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        //获取请求的参数的多个值
        String[] hobbies = request.getParameterValues("hobby");
        //输出
        System.out.println("用户名:" + username);
        System.out.println("密码:" + password);
        //将数组转换为集合输出
        System.out.println("兴趣爱好:" + Arrays.asList(hobbies));
    }
}

运行结果:
(在 web.xml 文件中创建对应的配置,在浏览器的地址栏输 http://localhost:8080/MyTest/form.html )
Servlet - 图15
Servlet - 图16
注意:doGet 请求的中文乱码问题的解决:
Servlet - 图17

请求转发

请求转发指的是服务器收到请求之后,从一个资源跳转到另一个资源的操作,如图所示:
Servlet - 图18

代码示例:1. 在 src 下创建此类,并在 web.xml 中配置相应的数据

public class Servlet1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取请求的参数(查看办事的材料)
        String username = request.getParameter("username");
        System.out.println("在Servlet1(柜台1)中查看参数(材料):" + username);

        //给材料盖章
        request.setAttribute("key1","柜台1的章");

        //获得通向Servlet2的路径(请求转发对象)
        //参数必须以斜杠打头,斜杠代表http://localhost:8080/工程名/,对应IDEA代码的web目录
        RequestDispatcher requestDispatcher = request.getRequestDispatcher("/Servlet2");

        //可以转发到WEB-INF目录下:request.getRequestDispatcher("/WEB-INF/xxx");
        //通过得到的路径走向Servlet2(柜台2)
        //forward方法将当前资源的request和response转发到该requestDispatcher指定的资源
        requestDispatcher.forward(request, response);

        //使得Servlet2中的request和response与Servlet1一致
    }
}

代码示例:2. 在 src 下创建此类,并在 web.xml 中配置相应的数据

public class Servlet2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取请求的参数(查看办事的材料)
        String username = request.getParameter("username");
        System.out.println("在Servlet2(柜台2)中查看参数(材料):" + username);
        //查看是否有柜台1的章
        Object key1 = request.getAttribute("key1");
        System.out.println("柜台1的章为:" + key1);
        //出处理自己的业务
        System.out.println("Servlet2处理业务");
    }
}

运行结果:
(在浏览器的地址栏中输入:http://localhost:8080/MyTest/Servlet1?username=jaychou)
Servlet - 图19
可以得出地址栏的内容不发生变化,但页面自动跳转 (访问)
到了请求转发对象 Servlet2 中,即显示
http://localhost:8080/MyTest/Servlet2 的页面

base 标签的作用

代码示例:1. 在 web 目录下创建 a 文件夹下创建 b 文件夹下创建 c.html

<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    这是a下的b下的c.html<br/>
    <a href="../../index.html">跳到web下的index.html</a>
</body>

Servlet - 图20
代码示例:2. 在 web 目录下创建 index.html

<head>                            
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    这是web下的index.html页面<br/>
    <a href="a/b/c.html">跳转到a下的b下的c.html</a>
</body>

运行结果:两个页面可以来回跳转
Servlet - 图21
分析:当在 c.html 页面准备点击进行跳转时浏览器的地址栏是http://localhost:63342/FirstWeb/MyTest/web/a/b/c.html,
跳转到 index.html 页面时的 a 标签路径是…/…/index.html,所有相对路径在跳转时都会参照当前浏览器地址栏中的地址来进行跳转,此时跳转的路径是http://localhost:63342/FirstWeb/MyTest/web/a/b/c.html…/…/index.html,进行抵消之后,剩余的路径是 http://localhost:63342/FirstWeb/MyTest/web/index.html,路径正确,跳转成功。

代码示例:1. 在 web 目录下创建 a 文件夹下创建 b 文件夹下创建 c.html

<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    这是a下的b下的c.html<br/>
    <a href="../../index.html">跳到web下的index.html</a>
</body>

Servlet - 图22
代码示例:2. 在 src 下创建此类,并在 web.xml 中配置

public class Forward extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.getRequestDispatcher("a/b/c.html").forward(request,response);
    }
}

代码示例:3. 在 web 目录下创建 index.html

<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    这是web下的index.html页面<br/>
    <a href= http://localhost:8080/MyTest/Forward>请求转发:a/b/c.html</a>
</body>

Servlet - 图23
分析:在地址栏输入http://localhost:63342/FirstWeb/MyTest/web/index.html,点击后成功跳转到 http://localhost:8080/MyTest/Forward,此时的页面是:
Servlet - 图24
点击之后无法跳转,根据以上原因,要跳转的地址是http://localhost:8080/MyTest/Forward…/…/index.html,抵消之后为 http://localhost:8080/…/index.html,这是错误的路径,因此跳转失败。解决方案如下:

base 标签可以设置当前页面中所有相对路径跳转时参照指定的路径来进行跳转,在 href 属性中设置指定路径

代码示例:4. 将上述 c.html 文件修改为如下即可成功跳转

<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!--base标签写在<title>标签之后-->
    <base href="http://localhost:8080/MyTest/a/b/">
</head>
<body>
    这是a下的b下的c.html<br/>
    <a href="../../index.html">跳到web下的index.html</a>
</body>

注:实际开发中都使用绝对路径,而不简单的使用相对路径

六、HttpServletResponse 类

  1. HttpServletResponse 类的作用
    每次只要有请求进入 Tomcat 服务器,Tomcat 服务器就会创建一个 Response 对象传递给 Servlet 程序。HttpServletResponse 表示所有响应的信息 (HttpServletRequest 表示请求发来的信息),可以通过 HttpServletResponse 对象设置返回给客户端的信息
  2. 两个输出流的说明
    字节流 getOutputStream(); 常用于下载 (传递) 二进制数据
    字符流 getWriter(); 常用于回传字符串
    注:同一个 HttpServletResponse 对象两个流不可同时使用,只可二选一,否则报错:
    Servlet - 图25
  3. 从服务器往客户端 (浏览器) 回传字符串数据

代码示例:在 src 下创建此类并在 web.xml 中进行配置

public class ResponseIO extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //防止中文乱码问题,在获取流对象之前调用此方法:
        //同时设置服务器和客户端都使用UTF-8字符集
        response.setContentType("text/html; charset=UTF-8");
        //获取流对象
        PrintWriter writer = response.getWriter();
        writer.write("I Love China!");
    }
}

运行结果:
Servlet - 图26

请求重定向

请求重定向指的是客户端给服务器发送请求,然后服务器通知客户端去访问自己的新地址 (之前的地址可能被废弃) 叫请求重定向
Servlet - 图27
代码示例:1. 在 src 下创建此类并在 web.xml 中进行配置

public class Response1 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
        throws ServletException, IOException {
        System.out.println("曾到此一游 Response1 ");

        req.setAttribute("key1", "value1");

        // 设置响应状态码302 ,表示重定向,(已搬迁)
        // resp.setStatus(302);

        // 设置响应头,说明 新的地址在哪里
        // resp.setHeader("Location", "http://localhost:8080/07_servlet/response2");
        // resp.setHeader("Location", "http://localhost:8080/07_servlet/WEB-INF/form.html");    // 不可以
        // resp.setHeader("Location", "http://www.baidu.com");

        resp.sendRedirect("http://localhost:8080/07_servlet/response2");
    }
}

代码示例:2. 在 src 下创建此类并在 web.xml 中进行配置

public class Response2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.getWriter().write("Response2's result!");
    }
}

运行结果:在地址栏输入http://localhost:8080/MyTest/Response1,得到:
Servlet - 图28
并在控制台输出:会访问到 Response1
注:

  1. 在 Response1 中使用以下代码替代两个步骤可得到同样的效果 (推荐使用此方法):
    response.sendRedirect(“http://localhost:8080/MyTest/Response2”);
  2. 在 Response1 中 request.setAttribute(“key1”, “value1”);
    在 Response2 中 req.getAttribute(“key1”); 无法得到 key1 的值,结果为 null

    相对路径 绝对路径

    相对路径
    不以 / 开头的路径就是相对路径
    访问:http://localhost:80/bookstore/upload/upload.jsp
    超链接
    要去的是:http://localhost:80/bookstore/upload/index.jsp
    不要使用相对路径
    绝对路径
    以 / 开头的路径
    访问:http://localhost:80/bookstore/upload/upload.jsp
    超链接
    要去的是:http://localhost:80/index.jsp
    推荐使用绝对路径

/ 在不同情况下的不同意义

  • “/” 若被浏览器解析,得到的地址是:http://ip:port/
  • “/” 若被服务器解析,得到的地址是:http://ip:port / 工程路径
    • web.xml 中 /servlet1
    • servletContext.getRealPath(“/“);
    • request.getRequestDispatcher(“/“);
  • 特殊情况:response.sendRedirect(“/“); 把斜杠发送到浏览器解析,得到 http://ip:port/

    乱码问题

    解决get请求乱码问题,Tomcat—conf—server.xml
      <Connector port="8080" protocol="HTTP/1.1"
                 connectionTimeout="20000"
                 URIEncoding="UTF-8"
                 redirectPort="8443" />
    
    解决post请求乱码问题,需要在request.getParameter() 之前设置,因为请求到达后不调用前方法,tomcat并不急着解析请求内容
    request.getCharacterEncoding("UTF-8");
    
    解决响应乱码问题
    response.setContentType("text/html; charset=UTF-8");
    
    [

](https://blog.csdn.net/weixin_49343190/article/details/107878144)