1. JSP简单内容
1.1 JavaEE
JavaEE 包含JSP
JavaEE是一个开发分布式企业级应用的规范和标准。JavaEE包含之前学过的所有内容(JavaSE)
真正开发中,很少使用JavaEE的原生内容,都是用 SSM 框架进行快速开发
1.2 部署web项目到服务器
简单的总体流程,先看一下效果。
-
安装服务器软件Tomcat,下载解压即可
-
创建Web项目(使用IDEA创建Java Enterprise项目),开发静态页面
新版 IDEA 没有JavaEE的选项,可以在项目中按
Alt + Ctrl + Shift + /
键,然后选择 Registry,在弹框中找到javaee-legacy.project.wizard
,然后选中,再进行创建即可 -
启动Tomcat
-
不同的用户通过浏览器访问web项目
总结:Web项目需要JavaEE的类库。Web项目中还可以存放静态网页和动态网页
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 — 解释执行
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脚本
- 小脚本
- 表达式
- 声明
- JSP标签
-
注释
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>
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
请求方法:Get
、Post
、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>
指定为饿汉)
2.4 Servlet的API
Servlet必须直接或间接实现 javax.servlet.Servlet 接口
通过继承javax.servlet.GenericServlet 类实现跨协议的 Servlet
通过继承javax.servlet.HttpServlet 实现HTTP Servlet 【一般使用这个】
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 中的数据
转发:可以获取
重定向:无法获取
-
原理不同
-
效率不同
转发:效率高
重定向:效率低
-
跳转的范围低
转发:仅限当前项目跳转(最大也就是服务器)
重定向:可以跳转到互联网的任意位置
-
跳转时的路径不同
(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");