【JavaLearn】#(23)JSP相关语法、HTTP协议、Servlet介绍、Servlet生命周期、请求和响应、相对路径、转发和重定向

1. JSP简单内容

1.1 JavaEE

JavaEE 包含JSP

JavaEE是一个开发分布式企业级应用的规范和标准。JavaEE包含之前学过的所有内容(JavaSE)

真正开发中,很少使用JavaEE的原生内容,都是用 SSM 框架进行快速开发

image-20220404133550572

1.2 部署web项目到服务器

简单的总体流程,先看一下效果。

  • 安装服务器软件Tomcat,下载解压即可

  • 创建Web项目(使用IDEA创建Java Enterprise项目),开发静态页面

    新版 IDEA 没有JavaEE的选项,可以在项目中按Alt + Ctrl + Shift + / 键,然后选择 Registry,在弹框中找到 javaee-legacy.project.wizard,然后选中,再进行创建即可

    image-20220404135100466

  • 启动Tomcat

  • 不同的用户通过浏览器访问web项目

总结:Web项目需要JavaEE的类库。Web项目中还可以存放静态网页和动态网页

image-20220404141107615

1.3 获取并输出当前时间—JSP

  • 小脚本: <% Java代码 %>
  • 表达式: <%=表达式 %>
<%@ page import="java.util.Date" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>获取当前时间</title>
        <!-- 客户端(浏览器获取) -->
        <script type="text/javascript">
        	window.onload = function() {
                var now = new Date();
                document.getElementById("ctime").innerHTML="客户端时间:" + now.toLocaleString();
            };
        </script>
    </head>
    <body>
        <div id="ctime"></div>
        
        <!-- JSP的小脚本 -->
        <%
        	// 服务器端获取,Java代码
        	Date now = new Date();
        	// out 是 JSP的内建对象,不需要new,可以直接使用
        	out.println("服务器端时间:" + now.toLocaleString());
        %>
        
        <!-- JSP的表达式 -->
        服务器端时间2:<%=now.toLocaleString() %>
    </body>
</html>

JSP是动态网页技术,是动态生成网页数据,而不是具有动态效果的网页

JSP是服务器端技术

由应用服务器(例如Tomcat)来编译和执行嵌入的Java脚本代码,然后将生成的整个页面信息返回给客户端

1.4 JSP执行过程

Web容器处理 JSP文件请求需要经过 3个阶段

  • 翻译/转译阶段:.jsp --> .java(Servlet)
  • 编译阶段: .java --> .class
  • 执行阶段: .class — 解释执行

image-20220404145347365

1.5 声明和注释

  • 声明: <%! 内部可以定义变量和方法 %>,在此处定义的变量和方法,编译后会变成 Servlet的成员变量和方法
  • 注释: <%-- JSP的注释内容 --%>,不会传输到客户端
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>统计次数</title>
</head>
<body>
<%!
    // 在此声明中,不能使用 JSP 的内建对象
    int count = 0;

    public void countTip() {
        count++;
    }
%>
<%
    // count++;
    countTip();
    out.println("当前页面访问次数:" + count + "<br>");
%>
</body>
</html>

Servlet/JSP 是一种单实例,多线程的技术

  • 单实例:不管有多少个用户访问(不管有多少个请求),用一个 Servlet/JSP,只创建一个对象
  • 多线程:对于每一次请求,会开辟一个新的线程,调用 service()

1.6 静态包含和动态包含

  • 静态包含: <%@include file="header.jsp"%>
  • 动态包含: <jsp:include page="footer.jsp"></jsp:include>

静态包含与动态包含的区别:

  • 包含的时机不同
    • 静态包含:转译阶段
    • 动态包含:执行阶段
  • 包含的方式不同
    • 静态包含:内容包含
    • 动态包含:方法调用
  • 是否可以有同名变量
    • 静态包含:不可以
    • 动态包含:可以
  • 生成的 class 文件不同
    • 静态包含:被包含文件不生成 class 文件
    • 动态包含:被包含文件生成单独class

创建一个 header.jsp,一个 footer.jsp

在另一个jsp页面中,包含这两个文件

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>静态包含/动态包含</title>
</head>
<body>
    <%-- 指令标签  【静态包含:直接将整个header.jsp的代码全部拿过来】 --%>
    <%@include file="header.jsp"%>

    <%-- 动作标签  【动态包含】--%>
    <jsp:include page="footer.jsp"></jsp:include>
</body>
</html>

1.7 JSP页面构成

  • 静态内容(等同于HTML)

  • 动态内容

    • JSP标签
      • 指令标签
      • 动作标签
    • Java脚本
      • 小脚本
      • 表达式
      • 声明
  • 注释

image-20220404154439920

1.8 JSP用户登录功能

  • request:JSP内建对象

页面+Java处理,后续加上JDBC

HTML页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>
    <form action="doLogin.jsp" method="post">   <!-- get方式参数都在地址栏 -->
        用户名:<input type="text" id="username" name="username" /> <br>
        密码: <input type="text" id="password" name="pwd" /> <br>
        <input type="submit" value="登录">
    </form>
</body>
</html>

JSP页面:doLogin.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>处理登录</title>
</head>
<body>

<%
    // 接收用户编号和密码
    String username = request.getParameter("username"); // 此处是HTML页面 用户输入框的 name 属性
    String pwd = request.getParameter("pwd");

    // 应该调用后台(JDBC)判断登录是否成功;此处直接处理
    boolean flag = false;
    if (username.contains("lwclick") && pwd.length() >= 6) {
        flag = true;
    }

    if (flag) {
        out.println("登录成功");
    } else {
        out.println("登录失败");
    }
%>

</body>
</html>

image-20220404162513770

1.9 理解HTTP协议

HTTP协议

  • Hypertext Transfer Protocol,超文本传输协议
  • 从WWW服务器传输超文本到本地浏览器的传送协议
  • 是一个应用层协议
  • 承载于 TCP协议之上,有时也承载于TLS或SSL协议层,这时就成为了HTTPS(默认端口443)

1.9.1 HTTP请求

HTTP请求工作原理

  • 遵循请求(request)/ 应答(Response)模型
  • 请求需要建立连接,响应结束断开连接,HTTP/1.0,连接不能复用,无状态的
  • HTTP/1.1实现了连接的复用( 声明 Connection:keep-alive ),新的请求可以在上次请求建立的TCP协议上继续发送

请求方式格式

  • 请求行:统一资源标识符(URL)、协议版本号

    POST /demo/login/doLogin.jsp HTTP/1.1

  • 消息报头:包含请求修饰符、客户机信息

    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.60 Safari/537.36 Edg/100.0.1185.29

    Cookie: JSESSIONID=6ED04BBCB0C57FA2BB97A21A473EC3D2; Idea-d39dabc2=423e5159-22a3-425e-95b1-83c72c6ee33f; trdipcktrffcext=1

  • 可能的内容:POST请求的内容

    username=11111lwclick&pwd=a81c

请求方法GetPost、DELETE、PUT等

1.9.2 HTTP响应

响应信息格式

  • 状态行:信息的协议版本号、状态码

    HTTP/1.1 200

  • 消息报头:包括服务器信息、字符编码、MIME类型

    Content-Type: text/html;charset=UTF-8

  • 响应正文

状态代码

  • 1xx:指示信息–表示请求已接收,继续处理
  • 2xx:成功–表示请求已被成功接收、理解、接受; 200:客户端请求成功
  • 3xx:重定向–要完成请求必须进行更进一步的操作; 302:重定向
  • 4xx:客户端错误–请求有语法错误或请求无法实现; 403:服务器收到请求,但是拒绝提供服务。 404:请求资源不存在
  • 5xx:服务器端错误–服务器未能实现合法的请求; 500:服务器发生错误

2. Servlet

Servlet是一个基于Java技术的动态网页技术,运行在服务器端,由Servlet容器管理,用于生成动态的内容。是JSP的前身

编写一个Servlet,实际上就是按照Servlet规范编写一个java类

JSP本质上是一个Servlet(.jsp --- 翻译 --- .java(Servlet) ---- 编译 --- .class ---- 执行—

Servlet/JSP 是单实例,多线程

2.1 使用Servlet开发动态网页

自定义一个 Servlet:

  • implements Servlet:需要实现所有的方法
  • extends GenericServlet:只需要实现 service方法即可
  • extends HttpServlet:自己选择实现,一般使用这个

1.创建MyServlet.java

public class FirstServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置字符编码
        resp.setContentType("text/html;charset=UTF-8");
        // 创建一个输出流
        PrintWriter out = resp.getWriter();

        // 使用输出流向客户端输出内容
        out.println("<HTML>");
        out.println("<HEAD>");
        out.println("<TITLE>第一个Servlet</TITLE>");
        out.println("</HEAD>");
        out.println("<BODY>");
        
        Date date = new Date();
        out.println("<p>当前时间是:" + date.toLocaleString() + "</p>");
        
        out.println("</BODY>");
        out.println("</HTML>");

        // 关闭输出流
        out.close();
    }
}

2.修改 web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
		  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
           version="2.5">
    <!-- 配置servlet类信息 -->
    <servlet>
        <servlet-name>FirstServlet</servlet-name>
        <servlet-class>com.lwclick.servlet.FirstServlet</servlet-class>
    </servlet>

    <!-- 配置映射,当请求 /servlet/FirstServlet 时,就转到FirstServlet -->
    <servlet-mapping>
        <servlet-name>FirstServlet</servlet-name>
        <url-pattern>/servlet/FirstServlet</url-pattern>
        <url-pattern>/abc/*</url-pattern>  <!-- 所有以/abc/开始的请求都会转到FirstServlet这个类 -->
    </servlet-mapping>
</web-app>

2.2 使用Servlet进行流程控制

使用 Servlet 修改登录流程,新增一个 success.jsp 页面,成功后跳转到该页面

将所有的判断、跳转逻辑放到 servlet中去处理

public class LoginServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1. 接收用户编号和密码
        String username = req.getParameter("username"); // 此处是HTML页面 用户输入框的 name 属性
        String pwd = req.getParameter("pwd");

        // 2. 应该调用后台(JDBC)判断登录是否成功;此处直接处理
        boolean flag = false;
        if (username.contains("lwclick") && pwd.length() >= 6) {
            flag = true;
        }

        // 3. 根据结果处理
        if (flag) {
            // 跳转到成功页面
            RequestDispatcher requestDispatcher = req.getRequestDispatcher("/login/success.jsp");
            requestDispatcher.forward(req, resp);
        } else {
            // 跳回登录页
            req.getRequestDispatcher("/login/login.html").forward(req, resp);
        }
    }
}

web.xml中配置 servelt

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
		  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
           version="2.5">

    <!-- 配置servlet类信息 -->
    <servlet>
        <servlet-name>LoginServlet</servlet-name>
        <servlet-class>com.lwclick.servlet.LoginServlet</servlet-class>
    </servlet>
    
    <!-- 配置映射,当请求 /servlet/LoginServlet 时,跳转到LoginServlet类进行处理 -->
    <servlet-mapping>
        <servlet-name>LoginServlet</servlet-name>
        <url-pattern>/servlet/LoginServlet</url-pattern>
    </servlet-mapping>
</web-app>

修改登录页面的form的action

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>
    <!-- action 修改为servlet映射路径(使用相对路径) -->
    <form action="../servlet/servlet/LoginServlet" method="post">
        用户名:<input type="text" id="username" name="username" /> <br>
        密码: <input type="text" id="password" name="pwd" /> <br>
        <input type="submit" value="登录">
    </form>
</body>
</html>

2.3 Servlet生命周期

public class LifeServlet extends HttpServlet {
    /**
     * 构造方法,  只执行一次(单实例)
     * 不管有多少次请求,一个Servlet只实例化一次
     */
    public LifeServlet() {
        System.out.println("构造方法");
    }

    /**
     * 初始化   只执行一次
     * 创建对象后执行
     * @param config
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("========== init(ServletConfig config) ==========");
    }

    /**
     * 服务   每次请求都会调用该方法(每一个请求,Tomcat都会开辟一个新的线程,调用该方法) 多线程的
     * 对请求响应进行处理
     * @param req
     * @param res
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println("========== service(ServletRequest req, ServletResponse res) ==========");
    }

    /**
     * 销毁   只执行一次
     * 关闭对象前执行
     */
    @Override
    public void destroy() {
        System.out.println("========== destroy() ==========");
    }
}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
		  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
           version="2.5">
    <!-- 配置servlet类信息 -->
    <servlet>
        <servlet-name>LifeServlet</servlet-name>
        <servlet-class>com.lwclick.servlet.LifeServlet</servlet-class>
    </servlet>
    <!-- 配置映射,当请求 /servlet/FirstServlet 时,就转到FirstServlet -->
    <servlet-mapping>
        <servlet-name>LifeServlet</servlet-name>
        <url-pattern>/servlet/LifeServlet</url-pattern>
    </servlet-mapping>
</web-app>
  • 加载类

    • 用户在客户端请求某个 servlet: http://localhost:8080/servlet/LifeServlet

    • Tomcat截取 servlet 路径:/servlet/LifeServlet,并且在web.xml中查找

    • 如果没找到,就报404

      如果找到了(servlet-mapping的url-pattern),就用<servlet-name>LoginServlet</servlet-name>的LoginServlet,去找对应的servlet-name,最后找到 com.lwclick.servlet.LifeServlet

    • 获取 Servlet 的完整路径字符串

      String className = “com.lwclick.servlet.LifeServlet”;

    • 获取类的结构信息

      Class clazz = Class.forName( className );

  • 实例化(只执行一次)

    • 不是通过 new LifeServlet() 构造方法创建
    • 是使用反射Servlet servlet = (Servlet) clazz.newInstance();
  • 初始化 init()(只执行一次)

    • Method m1 = clazz.getMethod("init", ServletConfig.class);
    • m1.invoke(servlet, config)
  • 服务 service() (每次)

    • 使用反射执行
  • 销毁 destroy() (一次)

    • 使用反射

Servlet的生命周期和反射密不可分,由 Tomcat负责

加载类、实例化、初始化的时机:

  • 情况1:第一次访问该 Servlet 的时候 – 懒汉模式(默认)
  • 情况2:项目启动时 – 饿汉模式(通过在 web.xml 的 servlet 中添加 <load-on-startup> 指定为饿汉)

image-20220404204457186

2.4 Servlet的API

Servlet必须直接或间接实现 javax.servlet.Servlet 接口

通过继承javax.servlet.GenericServlet 类实现跨协议的 Servlet

通过继承javax.servlet.HttpServlet 实现HTTP Servlet 【一般使用这个】

image-20220404210917923

HttpServlet的 protected service() 根据HTTP请求方法的类型调用相应doXXX()方法,我们自己编写的servlet应该继承HttpServlet,一般要覆盖 doPost 或者 doGet 方法(实际直接重写 service 即可)

2.5 获取上下文和初始化参数

  • ServletConfig 接口:

    • 表示单独的Servlet的配置和参数,只是适用于特定的Servlet

    • 从一个servlet被实例化后,对任何客户端在任何时候访问有效

      仅对本servlet有效,一个servlet的ServletConfig对象不能被另一个servlet访问

      ServletConfig config = this.getServletConfig();

  • ServletContext 接口:

    • WEB容器在启动时,会为每个WEB应用程序都创建一个对应的ServletContext对象(每个模块一个)

    • 由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯

      ServletContext context = this.getServletContext();

所有的外部参数都需要在 web.xml 中声明

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
		  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
           version="2.5">

    <!-- 【上下文参数】 所有servlet都可以获取的外部参数信息 -->
    <context-param>
        <param-name>path</param-name>
        <param-value>jdbc.properties</param-value>
    </context-param>
    
	<servlet>
        <servlet-name>ParamServlet</servlet-name>
        <servlet-class>com.lwclick.servlet.ParamServlet</servlet-class>
        <!-- 【初始化参数】 只有该servlet可以获取到 -->
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>ParamServlet</servlet-name>
        <url-pattern>/servlet/ParamServlet</url-pattern>
    </servlet-mapping>
</web-app>

获取的过程:

public class ParamServlet extends HttpServlet {

    private String path;
    private String encoding;

    /**
     * 将获取参数放在 init中,只执行一次
     * @throws ServletException
     */
    @Override
    public void init() throws ServletException {
        // 获取【上下文参数】 全局的  getServletContext()
        ServletContext context = this.getServletContext();
        path = context.getInitParameter("path");

        // 获取【初始化参数】 自己的  getServletConfig()
        ServletConfig config = this.getServletConfig();
        encoding = config.getInitParameter("encoding");
        if (encoding == null) {
            encoding = "gbk";
        }
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(path);
        System.out.println(encoding);
    }
}

3. 请求和响应

请求:ServletRequest,子接口 HttpServletRequest;封装了用户提交的信息,request对象由 Tomcat创建,拿数据

响应:ServletResponse, 子接口 HttpServletResponse;响应客户请求并向客户端输出信息,response对象也由 Tomcat创建,放数据

web服务器收到客户请求时,会同时创建请求对象和响应对象

3.1 解决表单的中文乱码

POST请求在servlet文件中,接收数据前,设置编码

// POST请求  设置字符编码
request.setCharacterEncoding("utf-8");

处理GET请求:在 Tomcat 的 server.xml 文件中,设置 Connector 添加 URIEncoding="utf-8"

超链接请求地址栏请求都是 GET请求,表单提交一般都使用 POST请求(默认是GET)

  • GET请求是不安全的,POST的所有操作对用户来说是不可见的
  • 因为受URL长度的限制,GET传送的数据量较小,而POST一般默认不受限制
  • GET请求可被缓存,可被收藏为书签,POST不可以
  • GET执行效率比POST高

3.2 服务器端表单验证

客户端验证:使用 JavaScript 验证,可以降低服务器端负担,但可以被跳过,而且无法进行业务验证(用户名是否存在)

服务器端验证:使用 JSP 验证,可以进行格式验证和业务验证,安全,但增加了服务器端的负担

建议:同时进行客户端验证和服务器端验证

在 servlet中,进行数据验证,同时将结果放到 request中,返回到 jsp页面

public class LoginServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 接收用户编号和密码
        String username = req.getParameter("username"); // 此处是HTML页面 用户输入框的 name 属性
        String pwd = req.getParameter("pwd");

        // ================== 增加一个【服务器端格式验证】,要求用户名和密码大于6 ===============
        if (username == null || username.length() <= 6) {
            req.setAttribute("userMsg", "服务器端验证:用户名长度必须大于6");
            req.getRequestDispatcher("/login/login.jsp").forward(req, resp);
            return;
        }
        if (pwd == null || pwd.length() <= 6) {
            req.setAttribute("pwdMsg", "服务器端验证:密码长度必须大于6");
            req.getRequestDispatcher("/login/login.jsp").forward(req, resp);
            return;
        }

        // 应该调用后台(JDBC)判断登录是否成功;此处直接处理
        boolean flag = false;
        if (username.contains("lwclick") && pwd.contains("click")) {
            flag = true;
        }

        if (flag) {
            // 跳转到成功页面
            RequestDispatcher requestDispatcher = req.getRequestDispatcher("/login/success.jsp");
            requestDispatcher.forward(req, resp);
        } else {
            // 跳回登录页
            req.getRequestDispatcher("/login/login.jsp").forward(req, resp);
        }
    }
}

需要修改 login.html 为 login.jsp,因为需要动态处理返回数据

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
    <script type="text/javascript" src="../js/jquery-1.9.1.js"></script>  <!-- 此处使用的是相对路径 -->
    <script type="text/javascript">
        //    客户端验证    
        function checkUser() {
            $("#userError").empty();
            var username = $("#username");
            if (username.length <= 6) {
                $("#userError").html("用户名长度大于6")
                return false;
            }
            return true;
        }

        function checkPwd() {
            $("#pwdError").empty();
            var pwd = $("#password");
            if (pwd.length <= 6) {
                $("#pwdError").html("密码长度大于6")
                return false;
            }
            return true;
        }

        function checkForm() {
            if (checkUser() && checkPwd()) {
                return true;
            }
            return false;
        }
    </script>
</head>
<body>
<form action="../servlet/LoginServlet" method="post" onsubmit="return checkForm()">
    用户名:<input type="text" id="username" name="username" onblur="checkUser()"/>
    <span id="userError">
        <!--    动态处理返回结果    -->
        <%
            String userMsg = request.getAttribute("userMsg").toString();
            if (userMsg != null) {
                out.println(userMsg);
            }
        %>
    </span> <br>
    密码: <input type="text" id="password" name="pwd" onblur="checkPwd()"/>
    <span id="pwdError">
        <%
            String pwdMsg = request.getAttribute("pwdMsg").toString();
            if (pwdMsg != null) {
                out.println(pwdMsg);
            }
        %>
    </span> <br>
    <input type="submit" value="登录">
</form>
</body>
</html>

3.3 项目路径问题

针对发布后的项目而言,涉及 JSP页面、HTML页面的HTML标签的路径(form的action,link、script、img等的src):

  • 绝对路径:以 HTTP 开始,包含主机名、端口号、项目上下文(项目发布名)、路径、文件名,可以访问[不同服务器]的[不同项目]资源

    同一服务器同一个应用(myservlet)的 JSP文件: http://127.0.0.1:8080/myservlet/login/login.jsp

    同一服务器不同应用(other)的 servlet:http://127.0.0.1:8080/other/servlet/FirstServlet

    不同服务器,百度首页:https://www.baidu.com

  • 根路径:以 / 开始,需要写项目上下文,可以访问【当前服务器】的【所有项目】资源

    同一服务器同一个应用(myservlet)的 JSP文件:/myservlet/login/login.jsp

    同一服务器不同应用(other)的 servlet:/other/servlet/FirstServlet

  • 相对路径(相对于当前文件的位置)

    ./:当前文件所在目录,可以省略

    ../:当前文件的上级目录所在

    与当前文件在同一目录的 JSP文件: ./other.jsp

    文件位于当前文件的上级目录: ./…/third.jsp

  • 相对路径(相对于基准路径)

    在 某个JSP 文件中,添加 base 标签,后面一定要加 /

    <!--    http://127.0.0.1:8080/myservlet/   -->
    <%
    	String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
            + request.getContextPath() + "/";
    %>
    <base href="<%=basePath %>">
    

    同一服务器同一个应用(myservlet)的 JSP文件: login/login.jsp (绝对路径:http://127.0.0.1:8080/myservlet/login/login.jsp)

    同一服务器不同应用(other)的 servlet:…/other/servlet/FirstServlet(http://127.0.0.1:8080/other/servlet/FirstServlet)

3.4 转发和重定向

在 servlet中,进行转发(使用 request 对象)和重定向(使用 response 对象)的操作

if (flag) {
    // 跳转到成功页面 【重定向 redirect】!!!
    req.setAttribute("username", username);
    // req.getContextPath() 获取项目上下文
    resp.sendRedirect(req.getContextPath() + "/login/success.jsp");  
} else {
    // 跳回登录页 【转发 dispatcher】!!! 
    req.setAttribute("error", "用户名或密码错误");
    req.getRequestDispatcher("/login/login.jsp").forward(req, resp);
}
  • 用户控制的跳转方式,最终地址的变化都会体现在地址栏中

    • 点击超链接
    • 提交表单

    都是向服务器端发起一个新的请求

  • 服务器端控制的跳转方式

    • 转发
    • 重定向

    共同点:都实现了跳转

转发和重定向的不同点

  • 语句不同

    转发(request对象):req.getRequestDispatcher(“/login/login.jsp”).forward(req, resp);

    重定向(response对象):resp.sendRedirect(“/servlet/login/success.jsp”);

  • 跳转后地址栏的路径不同

    转发:跳转之前的地址, /servlet/LoginServlet

    重定向:跳转之后的地址, /login/success.jsp

  • 是否可以获取保存在 request 中的数据

    转发:可以获取

    重定向:无法获取

  • 原理不同

    image-20220405125339870

  • 效率不同

    转发:效率高

    重定向:效率低

  • 跳转的范围低

    转发:仅限当前项目跳转(最大也就是服务器)

    重定向:可以跳转到互联网的任意位置

  • 跳转时的路径不同(servlet处的路径问题)

    • 绝对路径

      转发:不支持(只能在当前项目跳转)

      重定向:支持

    • 根路径(跳转到当前服务器的项目)

      转发:不需要写项目上下文(项目部署名) 此时的 / 代表当前项目

      重定向:需要写项目上下文 /servlet/login/success.jsp(可以跳到其他项目) 此时的 / 代表当前服务器

    • 相对路径(servlet中不建议使用)

      其他文件 相对于当前文件的位置(发布之后的文件路径)

  • 刷新是否导致表单重复提交

    转发:会

    重定向:不会(以后的添加等操作,要采用重定向,避免重复操作)

  • 是否经过过滤器(后面过滤器中介绍)

    转发:不经过

    重定向:经过

转发和重定向的选择

  • 使用转发:
    • 希望前后两个组件共享 request 数据
    • 跳转到 同一个应用的 WEB-INF 目录下 【在servlet中 request.getRequestDispatcher(“/WEB-INF/login.jsp”).forward(req, resp);】
  • 使用重定向:
    • 跳转到不同的应用程序
    • 使用 Cookie 存储数据需要使用重定向
    • 注销一般使用重定向
    • 连续表单页面(添加完跳到查询页面)之间建议使用重定向,避免属性冲突

3.5 请求和响应的更多方法

请求 request 的方法:

  • 获取请求头信息

    // 还有其他的信息可以获取
    String userAgent = request.getHeader("User-Agent");  // 浏览器基础信息
    
    // 使用 Referer头信息,可以防止盗链!!!
    String referer = request.getHeader("Referer");  // 请求从哪里来
    // 处理盗链请求
    if (!referer.contains("lwclick.com")) {
        response.sendRedirect("https://www.lwclick.com");
        return;
    }
    
  • 获取请求路径信息

    // URI信息:/myservlet/servlet/MoreServlet
    String uri = request.getRequestURI();
    
    // URL信息:http://localhost:8080/myservlet/servlet/MoreServlet
    StringBuffer url = request.getRequestURL();
    
    // 协议名 http
    String scheme = request.getScheme();
    
    // 主机名 localhost
    String serverName = request.getServerName();
    
    // 端口号 8080
    int serverPort = request.getServerPort();
    
    // 项目上下文 myservlet
    String contextPath = request.getContextPath();
    
  • 获取请求表单信息

    // 基本表单信息
    String username = request.getParameter("username");
    
    // 多选框
    String[] hobbies = request.getParameterValues("hobby");
    
  • 获取网络地址信息

    // 服务器的地址
    String localAddr = request.getLocalAddr();
    
    // 请求的客户端的地址
    String remoteAddr = request.getRemoteAddr();
    
  • 获取 Session、Cookie、ServletContext(重点)

    HttpSession session = request.getSession();
    
    Cookie[] cookies = request.getCookies();
    
    // 【this.getServletContext() 】 获取web.xml中全局的配置参数
    ServletContext servletContext = request.getServletContext();  
    
  • 指定接收的表单数据的编码

    request.setCharacterEncoding("utf-8");
    

响应 response 的方法:

  • 重定向

    response.sendRedirect("");
    
  • Servlet 返回数据时,解决中文乱码问题(servlet作为调度器控制转发、重定向时,没必要指定)

    response.setContentType("text/html;charset=utf-8");
    PrintWriter writer = response.getWriter();
    
  • 下载文件,设置response响应

    response.setContentType("image/jpeg");
    response.setContentLength(1024);
    response.setHeader("Content-Disposition", "attachment;filename=logo.jpg");
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LRcoding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值