1. Servlet 技术
1.1 什么是 Servlet
1、Servlet 是 JavaEE 规范之一。规范就是接口
2、Servlet 就 JavaWeb 三大组件
之一。三大组件分别是:Servlet 程序、Filter 过滤器、Listener 监听器。
3、Servlet 是运行在服务器上的一个 java 小程序,它可以接收客户端发送过来的请求,并响应数据给客户端。
1.2 手动实现 Servlet 程序
步骤:
- 编写一个类去实现 Servlet 接口
- 实现 service 方法,处理请求,并响应数据
- 到 web.xml 中去配置 servlet 程序的访问地址
Servlet 程序的示例代码:
说明:创建一个动态web工程,在创建包,创建一个类实现Servlet接口,实现接口中的方法。在service方法中输出一行进行测试。然后再web.xml配置文件中进行配置,之后通过idea整合的tomact服务器启动,在浏览器中输入地址进行测试,如果在idea控制台中输出了这行测试语句 说明访问成功。
package com.atguigu.servlet;
import javax.servlet.*;
import java.io.IOException;
public class HelloServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
/**
* service方法是专门用来处理请求和响应的
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("Hello Servlet 被访问了");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
web.xml 中的配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- servlet标签给Tomcat配置Servlet程序 -->
<servlet>
<!--servlet-name标签 Servlet程序起一个别名(一般是类名) -->
<servlet-name>HelloServlet</servlet-name>
<!--servlet-class是Servlet程序的全类名-->
<servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
</servlet>
<!--servlet-mapping标签给servlet程序配置访问地址,上下2个名字保持一致-->
<servlet-mapping>
<!--servlet-name标签的作用是告诉服务器,我当前配置的地址给哪个Servlet程序使用-->
<servlet-name>HelloServlet</servlet-name>
<!--
url-pattern标签配置访问地址,里面是自定义的访问路径
/ 斜杠在服务器解析的时候,表示地址为:http://ip:port/工程路径 ,
工程路径指的是在idea中进行的配置,一般是修改为当前项目名 详情查看tomact 7.5 <br/>
/hello 表示地址为: http://ip:port/工程路径/hello <br/>
-->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
访问测试:
启动Tomact服务器,在浏览器输入地址:
可以看出在控制台上输出了HelloServlet类中的service方法的输出语句,说明调用了service方法。
常见的错误 1:url-pattern 中配置的路径没有以斜杠打头。
常见错误 2:servlet-name 配置的值不存在:
常见错误 3:servlet-class 标签的全类名配置错误:
1.3 Servlet的调用过程
1.4 Servlet 的生命周期
1、执行 Servlet 构造器方法
2、执行 init 初始化方法
说明:Servlet 程序默认是第一次访问的时候创建,此时会调用这2个方法。
3、执行 service 方法
说明:第三步,每次访问都会调用。
4、执行 destroy 销毁方法
说明:第四步,在 web 工程停止的时候调用。
测试:
HelloServlet 类:
package com.atguigu.servlet;
import javax.servlet.*;
import java.io.IOException;
public class HelloServlet implements Servlet {
public HelloServlet() {
System.out.println("1 构造器方法");
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("2 init初始化方法");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
/**
* service方法是专门用来处理请求和响应的
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("3 service === Hello Servlet 被访问了");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("4 . destroy销毁方法");
}
}
在浏览器输入地址:
查看控制台输出:
1.5 GET 和 POST 请求的分发处理
问题:为什么要做请求分发处理???
- 答:
执行流程
:get请求和post请求都会执行service()方法,不同的请求干的是不同的事情,所以先通过method()方法进行判断是那种请求,之后在根据请求的不同写不同的代码。
缺点
:get请求和post请求如果干的事情很多,代码都写在了service方法或造成代码臃肿不好维护。
解决
:可以把get请求和post请求干的事情封装为2个不同的方法doGet()、doPost(),然后再service方法中分别进行调用这2个方法即可。
测试:
步骤1:在web目录下创建一个html文件:a.html,分别使用get请求和post请求。提交的地址写为servlet程序访问的地址。
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://localhost:8080/dongtai-web-servlet/hello" method="get">
<input type="submit">
</form>
</body>
</html>
步骤2:在浏览器中先访问这个a.html页面
- 执行流程:
- 首先通过浏览器访问a.html页面,由于这个页面没有写在受保护的WEB-INF目录下,所以可直接访问。格式:https://ip:端口号/项目名(web应用)/资源名。
- 之后:通过提交按钮发送get请求访问到servlet程序。
步骤3:点击提交按钮会访问执行servlet程序的service方法。(此时为get请求)
步骤4:在a.html中修改为post再次进行访问测试,可以看出service方法仍然执行。
步骤5:一般get请求和post请求干的事情是不一样的,此时都走service方法无法区分究竟是那个请求,需要先判断下是什么请求,然后分别针对不同的请求做不同的处理,但是这样代码都写在了service方法,会造成代码臃肿不好维护。所以一般会抽取为2个方法get和post, 在service中调用这2个方法即可。
package com.atguigu.servlet;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class HelloServlet implements Servlet {
public HelloServlet() {
System.out.println("1 构造器方法");
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("2 init初始化方法");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
/**
* service方法是专门用来处理请求和响应的
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("3 service === Hello Servlet 被访问了");
// getMethod()方法是ServletRequest的子类HttpServletRequest提供的,所以需要类型转换
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
// 可以通过getMethod()方法获取请求是那种方式
String method = httpServletRequest.getMethod();//得到的是大写的 GET POST
/*
方式一:这种方式虽然可以进行区分请求类型,但是一般来说get和post请求要做的事情可能很多 代码不止一行,
如果代码都写在service方法,会造成代码臃肿不好维护。所以一般会抽取为2个方法get和post,
在service中调用这2个方法即可。
if ("GET".equals(method)) {
System.out.println("get请求");
System.out.println("get请求");
System.out.println("get请求");
System.out.println("get请求");
} else if ("POST".equals(method)) {
System.out.println("post请求");
System.out.println("post请求");
System.out.println("post请求");
System.out.println("post请求");
}*/
//方式二:
if ("GET".equals(method)) {
doGet();
} else if ("POST".equals(method)) {
doPost();
}
}
/**
* 做get请求的操作
*/
public void doGet(){
System.out.println("get请求");
System.out.println("get请求");
}
/**
* 做post请求的操作
*/
public void doPost(){
System.out.println("post请求");
System.out.println("post请求");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("4 . destroy销毁方法");
}
}
1.6 通过继承 HttpServlet 实现 Servlet 程序
一般在实际项目开发中,都是使用继承 HttpServlet 类的方式去实现 Servlet 程序。
- 编写一个类去继承 HttpServlet 类
- 根据业务需要重写 doGet 或 doPost 方法
- 到 web.xml 中的配置 Servlet 程序的访问地址
a.xml文件:
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://localhost:8080/dongtai-web-servlet/hello02" method="get">
<input type="submit">
</form>
</body>
</html>
Servlet 类的代码:
package com.atguigu.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class HelloServlet02 extends HttpServlet {
private static final long serialVersionUID = -6975721664720501905L;
//快捷键重写方法:alt+insert---->Override Methods---->doGet() doPost()
/**
* doGet()在get请求的时候调用
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloServlet2 的doGet方法");
}
/**
* doPost()在post请求的时候调用
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloServlet2 的doPost方法");
}
}
web.xml 中的配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>HelloServlet02</servlet-name>
<servlet-class>com.atguigu.servlet.HelloServlet02</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet02</servlet-name>
<url-pattern>/hello02</url-pattern>
</servlet-mapping>
</web-app>
在浏览器中输入地址进行测试:
点击提交按钮:可以看出是get提交就执行doGet()方法。post请求就执行doPost()方法。
1.7 使用 IDEA 创建 Servlet 程序
说明:会自动生成doGet() doPost()方法,相比手动创建更简单一些。
注意:
- 手动创建的类继承HttpServlet的方式,重写的doGet(),doPost()方法里面方法的参数是:
(HttpServletRequest req, HttpServletResponse resp)
- 使用工具直接创建servlet程序,doGet(),doPost()方法里面方法的参数是:
(HttpServletRequest request, HttpServletResponse response)
- 没有啥区别,只是名字不同而已。
步骤:
菜单:new ->Servlet 程序
配置 Servlet 的信息:
HelloServlet03类:
package com.atguigu.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class HelloServlet03 extends HttpServlet {
private static final long serialVersionUID = 7204024401528404950L;
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("HelloServlet03的doPost方法");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("HelloServlet03的doGet方法");
}
}
web.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>HelloServlet03</servlet-name>
<servlet-class>com.atguigu.servlet.HelloServlet03</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet03</servlet-name>
<url-pattern>/hello03</url-pattern>
</servlet-mapping>
</web-app>
a.html:
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://localhost:8080/dongtai-web-servlet/hello03" method="get">
<input type="submit">
</form>
</body>
</html>
浏览器访问测试:
点击提交按钮:可以看出是get提交就执行doGet()方法。post请求就执行doPost()方法。
1.8 Servlet 类的继承体系
2. ServletConfig 类(配置信息类)
ServletConfig 类从类名上来看,就知道是 Servlet 程序的配置信息类。
Servlet
程序和ServletConfig
对象都是由 Tomcat 负责创建
,我们负责使用。
Servlet 程序默认是第一次访问的时候创建,ServletConfig 是每个 Servlet 程序创建时,就创建一个对应的 ServletConfig 对象。
2.1 ServletConfig 类的三大作用
说明:配置信息类的作用就是获取配置文件web.xml中的内容。
1、可以获取 Servlet 程序的别名 servlet-name 的值
2、获取初始化参数 init-param
3、获取 ServletContext 对象
测试1:ServletConfig可以在初始化的init()方法中使用
web.xml 中的配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- servlet标签给Tomcat配置Servlet程序 -->
<servlet>
<!--servlet-name标签 Servlet程序起一个别名(一般是类名) -->
<servlet-name>HelloServlet</servlet-name>
<!--servlet-class是Servlet程序的全类名-->
<servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
<!--init-param是初始化参数-->
<init-param>
<!--是参数名-->
<param-name>username</param-name>
<!--是参数值-->
<param-value>root</param-value>
</init-param>
<!--init-param是初始化参数-->
<init-param>
<!--是参数名-->
<param-name>url</param-name>
<!--是参数值-->
<param-value>jdbc:mysql://localhost:3306/test</param-value>
</init-param>
</servlet>
<!--servlet-mapping标签给servlet程序配置访问地址-->
<servlet-mapping>
<!--servlet-name标签的作用是告诉服务器,我当前配置的地址给哪个Servlet程序使用-->
<servlet-name>HelloServlet</servlet-name>
<!--
url-pattern标签配置访问地址 <br/>
/ 斜杠在服务器解析的时候,表示地址为:http://ip:port/工程路径 <br/>
/hello 表示地址为:http://ip:port/工程路径/hello <br/>
-->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
HelloServlet 中的代码:
注意:这里使用的是HelloServlet implements Servlet
package com.atguigu.servlet;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class HelloServlet implements Servlet {
public HelloServlet() {
System.out.println("1 构造器方法");
}
//在init方法中使用
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("2 init初始化方法");
// 1、可以获取Servlet程序的别名servlet-name的值,在web.xml中
System.out.println("HelloServlet程序的别名是:" + servletConfig.getServletName());//HelloServlet
// 2、获取初始化参数init-param
System.out.println("初始化参数username的值是;" + servletConfig.getInitParameter("username"));//root
System.out.println("初始化参数url的值是;" + servletConfig.getInitParameter("url"));//jdbc:mysql://localhost:3306/test
// 3、获取ServletContext对象
System.out.println(servletConfig.getServletContext());//org.apache.catalina.core.ApplicationContextFacade@6db7b70
}
@Override
public ServletConfig getServletConfig() {
return null;
}
/**
* service方法是专门用来处理请求和响应的
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("3 service === Hello Servlet 被访问了");
// 类型转换(因为它有getMethod()方法)
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
// 获取请求的方式
String method = httpServletRequest.getMethod();
if ("GET".equals(method)) {
doGet();
} else if ("POST".equals(method)) {
doPost();
}
}
/**
* 做get请求的操作
*/
public void doGet(){
System.out.println("get请求");
System.out.println("get请求");
}
/**
* 做post请求的操作
*/
public void doPost(){
System.out.println("post请求");
System.out.println("post请求");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("4 . destroy销毁方法");
}
}
测试2:除了在init()方法中使用,还可以在别的方法中使用,如doGet()
web.xml 中的配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>HelloServlet2</servlet-name>
<servlet-class>com.atguigu.servlet.HelloServlet2</servlet-class>
<init-param>
<param-name>username</param-name>
<param-value>root2</param-value>
</init-param>
<init-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/test2</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet2</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>
</web-app>
HelloServlet2 中的代码:
注意:这里是HelloServlet2 extends HttpServlet
package com.atguigu.servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class HelloServlet2 extends HttpServlet {
private static final long serialVersionUID = 253283331602568456L;
@Override
public void init(ServletConfig config) throws ServletException {
/*注意:使用extends HttpServlet方式重写的init方法一定要调用父类的int(config)操作。
因为:在父类GenericServlet中的init方法会保存这个config对象,在子类重写调用的是子类的
int()方法 父类的这个config对象保存操作就会丢失,子类没有config在使用是一定会报空指针异常,
解决:通过super调用父类的init方法即可。*/
super.init(config);
System.out.println("重写了init初始化方法,做了一些工作");
}
/**
* doGet()在get请求的时候调用
* 注意每一个servlet程序都对应着一个自己的ServletConfig,不能在
* 自己的servlet程序得到别人的servlet信息。
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloServlet2 的doGet方法");
// 也可以使用.
ServletConfig servletConfig = getServletConfig();
System.out.println(servletConfig);
// 2、获取初始化参数init-param
System.out.println("初始化参数username的值是;" + servletConfig.getInitParameter("username"));
System.out.println("初始化参数url的值是;" + servletConfig.getInitParameter("url"));
}
/**
* doPost()在post请求的时候调用
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloServlet2 的doPost方法");
}
}
访问测试:
注意点:
-
注意:使用extends HttpServlet方式重写的init方法一定要调用父类的int(config)操作。
因为:在父类GenericServlet中的init方法会保存这个config对象,在子类重写调用的是子类的nt()方法 父类的这个config对象保存操作就会丢失,子类没有config在使用是一定回报空指针异常。
解决:通过super调用父类的init方法即可。
-
注意每一个servlet程序都对应着一个自己的ServletConfig,不能在自己的servlet程序得到别人的servlet信息。
3. ServletContext 类(域对象)
3.1 什么是 ServletContext?
- ServletContext 是一个接口,它表示 Servlet 上下文对象
- 一个 web 工程,只有一个 ServletContext 对象实例。
- ServletContext 对象是一个域对象,就是后面学习的application域对象。
- ServletContext 是在 web 工程部署启动的时候创建。在 web 工程停止的时候销毁。
什么是域对象?
域对象,是可以像 Map 一样存取数据的对象,叫域对象。
这里的域指的是存取数据的操作范围,整个 web 工程。
Map集合与域对象存取数据对比:
存数据 取数据 删除数据
Map put() get() remove()
域对象 setAttribute() getAttribute() removeAttribute();
3.2 ServletContext 类的四个作用
- 获取 web.xml 中配置的上下文参数 context-param
- 获取当前的工程路径,格式: /工程路径
- 获取工程部署后在服务器硬盘上的绝对路径
- 像 Map 一样存取数据
测试1:测试前三项功能。
ServletContext 演示代码:
package com.atguigu.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ContextServlet extends HttpServlet {
private static final long serialVersionUID = -7502910448759289188L;
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1、获取web.xml中配置的上下文参数context-param
ServletContext context = getServletConfig().getServletContext();
String username = context.getInitParameter("username");
System.out.println("context-param参数username的值是:" + username);
System.out.println("context-param参数password的值是:" + context.getInitParameter("password"));
// 2、获取当前的工程路径,格式: /工程路径
System.out.println( "当前工程路径:" + context.getContextPath() );
// 3、获取工程部署后在服务器硬盘上的绝对路径
/**
* / 斜杠被服务器解析地址为:http://ip:port/工程名/ 映射到IDEA代码的web目录<br/>
*/
System.out.println("工程部署的路径是:" + context.getRealPath("/"));
System.out.println("工程下css目录的绝对路径是:" + context.getRealPath("/css"));
System.out.println("工程下imgs目录1.jpg的绝对路径是:" + context.getRealPath("/imgs/1.jpg"));
}
}
web.xml 中的配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--context-param是上下文参数(它属于整个web工程,也就是说它对整个web工程里面的程序servlet、过滤器等是共用的)-->
<context-param>
<param-name>username</param-name>
<param-value>context</param-value>
</context-param>
<!--context-param是上下文参数(它属于整个web工程)-->
<context-param>
<param-name>password</param-name>
<param-value>root</param-value>
</context-param>
<servlet>
<servlet-name>ContextServlet</servlet-name>
<servlet-class>com.atguigu.servlet.ContextServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ContextServlet</servlet-name>
<url-pattern>/contextServlet</url-pattern>
</servlet-mapping>
</web-app>
测试2:测试第四项功能,ServletContext 像 Map 一样存取数据:
注意: 数据在保存到域对象之前不能获取,只有保存之后才能获取。
ContextServlet1 代码:
package com.atguigu.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ContextServlet1 extends HttpServlet {
private static final long serialVersionUID = 400312190368000524L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取ServletContext对象,也可以直接通过getServletContext()方法获取,这个方法底层封装的还是getServletConfig().getServletContext()
ServletContext context = getServletContext();
System.out.println(context);//ContextServlet1和ContextServlet2打印地址相同,说明是一个 web 工程,只有一个 ServletContext 对象实例
//数据在保存之前不能获取
System.out.println("保存之前: Context1 获取 key1的值是:"+ context.getAttribute("key1"));
context.setAttribute("key1", "value1");
//数据在保存后才能获取值
System.out.println("Context1 中获取域数据key1的值是:"+ context.getAttribute("key1"));
}
}
ContextServlet2 代码:
package com.atguigu.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ContextServlet2 extends HttpServlet {
private static final long serialVersionUID = 5917018552732354379L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext context = getServletContext();
System.out.println(context);
//只要不重启整个web工程,那么在整个web项目下,都可以获取域对象在保存之后的值。
System.out.println("Context2 中获取域数据key1的值是:"+ context.getAttribute("key1"));
}
}
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>ContextServlet1</servlet-name>
<servlet-class>com.atguigu.servlet.ContextServlet1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ContextServlet1</servlet-name>
<url-pattern>/contextServlet1</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>ContextServlet2</servlet-name>
<servlet-class>com.atguigu.servlet.ContextServlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ContextServlet2</servlet-name>
<url-pattern>/contextServlet2</url-pattern>
</servlet-mapping>
</web-app>
在浏览器中分别输入2个路径进行测试:
4. HTTP 协议
4.1 什么是 HTTP 协议
什么是协议?
- 协议是指双方,或多方,相互约定好,大家都需要遵守的规则,叫协议。
什么是HTTP 协议?
- 所谓 HTTP 协议(超文本传输协议),就是指,客户端和服务器之间通信时,发送的数据,需要遵守的规则,叫 HTTP 协议。
- HTTP 协议中的数据又叫报文。
HTTP 协议的特点:
- 基于TCP协议:面向连接,安全(三次握手和四次挥手)
- 基于请----响应模型的:一次请求对应一次响应
- HTTP协议是无状态的协议:对于事务处理没有记忆能力。每次请求----响应都是独立的,后一次请求是不会记录前一次请求数据的。
- 缺点:多次请求间不能共享数据。
- 优点:速度快
4.2 请求的 HTTP 协议格式
- 请求:客户端给服务器发送数据叫请求
- 响应:服务器给客户端回传数据叫响应
- 请求种类:GET 请求,和 POST 请求两种
4.2.1 GET 请求
1、请求行
- 请求的方式 GET
- 请求的资源路径[+?+请求参数]
- 请求的协议的版本号 HTTP/1.1
2、请求头
key : value 组成 不同的键值对,表示不同的含义。
注意:GET 请求没有请求体,POST请求只有携带参数才有请求体。
4.2.2 POST 请求
1、请求行
- 请求的方式 POST
- 请求的资源路径[+?+请求参数]
- 请求的协议的版本号 HTTP/1.1
2、请求头
- key : value 不同的请求头,有不同的含义.
- 请求头和请求体之间有
空行
3、请求体 ===>>> 就是发送给服务器的数据
注意:
post提交和get提交的请求参数格式相同,只不过是请求参数提交的位置不同。
- GET 请求没有请求体,POST请求只有携带参数才有请求体。
4.2.3 常用请求头的说明
-
Accept: 表示客户端可以接收的数据类型
-
Accpet-Languege: 表示客户端可以接收的语言类型
-
User-Agent: 表示客户端浏览器的信息
-
Host: 表示请求时的服务器 ip 和端口号
4.2.4 哪些是 GET 请求,哪些是 POST 请求
GET 请求有哪些:
- form 标签 method=get
- a 标签
- link 标签引入 css
- Script 标签引入 js 文件
- img 标签引入图片
- iframe 引入 html 页面
- 在浏览器地址栏中输入地址后敲回车
POST 请求有哪些:
- form 标签 method=post
总结:在没写Ajax之前,只有当使用表单(form),并且在表单上明确的通过method指定提交方式为POST时,请求方式才是POST提交,其它方式都是GET提交。
4.2.5 GET 请求和POST 请求的区别
- get请求一般是获取数据,post请求一般的提交数据。
- post请求安全、get请求不安全。
- 本质区别是传参的方式不一样:
- get请求在地址栏后面以?的方式传参,多个参数之间用&分隔。
- post请求是在body以表单的方式传参。
4.3 响应的 HTTP 协议格式
4.3.1 响应行、响应头、响应体
1、响应行
- 响应的协议和版本号
- 响应状态码
- 响应状态描述符
2、响应头
- key : value 不同的响应头,有其不同含义。
- 响应头和响应体之间有
空行
3、响应体 ---->>> 就是回传给客户端的数据
4.3.2 常用的响应码说明
- 常见的响应状态码分类:
- 常见的响应状态码:
状态码大全:https://cloud.tencent.com/developer/chapter/13553
200 表示请求成功
302 表示请求重定向(明天讲)
404 表示请求服务器已经收到了,但是你要的数据不存在(请求地址错误)
500 表示服务器已经收到请求,但是服务器内部错误(代码错误)
4.4 HTTP-协议解析
- 浏览器和服务器之间通信需要遵守http协议,解析请求数据和响应数据分为2个部分:客户端、服务端
- 客户端:各大厂商提供了内置的解析http协议的程序,我们不需要操作。
- 服务端:作为服务端开发工程师,我们要做的是在服务器端通过java程序来接收客户端浏览器发起的请求并获取请求数据,然后参照http协议的请求数据格式对请求数据进行解析,然后还需要参照http协议的响应数据格式给浏览器在响应对应的数据。
- 具体做法:可以使用原生的方式TCP网络编程,通过socket关键字来编写java程序进行接收和响应数据,但是这种方式太过繁琐。因为Web项目开发需要解析http协议,而http协议又是标准的、统一固定的,所以这部分解析http协议的代码也是非常通用的,所以市面上很多公司封装了这些通用的代码到一个软件中,这个软件程序就是web服务器。
- 常见的Web服务器:
4.5 MIME 类型说明
MIME 是 HTTP 协议中数据类型。
MIME 的英文全称是"Multipurpose Internet Mail Extensions" 多功能 Internet 邮件扩充服务。MIME 类型的格式是“大类型/小类型”,并与某一种文件的扩展名相对应。
常见的 MIME 类型:
4.6 谷歌浏览器如何查看 HTTP 协议:
4.7 火狐浏览器如何查看 HTTP 协议:
5. HttpServletRequest 类
5.1 HttpServletRequest 类有什么作用。
每次只要有请求进入 Tomcat 服务器,Tomcat 服务器
就会把请求过来的 HTTP 协议信息解析好封装到 Request 对象中(即:发送一次请求,就对应一个request对象)。然后传递到 service 方法(doGet 和 doPost)中给我们使用。我们可以通过 HttpServletRequest 对象,获取到所有请求的信息。
注意:
- 手动创建的类继承HttpServlet的方式,重写的doGet(),doPost()方法里面方法的参数是:
(HttpServletRequest req, HttpServletResponse resp)
- 使用工具直接创建servlet程序,doGet(),doPost()方法里面方法的参数是:
(HttpServletRequest request, HttpServletResponse response)
- 没有啥区别,只是名字不同而已。
5.2 HttpServletRequest 类的常用方法
i.getRequestURI() 获取请求的资源路径
ii.getRequestURL() 获取请求的统一资源定位符(绝对路径)
iii.getRemoteHost() 获取客户端的 ip 地址
iv.getHeader() 获取请求头
v.getParameter() 获取请求的参数
vi.getParameterValues() 获取请求的参数(多个值的时候使用)
vii.getMethod() 获取请求的方式GET 或POST
viii.setAttribute(key, value); 设置域数据
ix.getAttribute(key); 获取域数据
x.getRequestDispatcher() 获取请求转发对象
测试常用API 示例代码:
说明:首先创建一个动态web工程,建议修改tomac启动的实例化名和项目名保持一致,在修改工程访问路径一般也和创建的模块名保持一致。
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>RequestAPIServlet</servlet-name>
<servlet-class>com.atguigu.servlet.RequestAPIServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RequestAPIServlet</servlet-name>
<url-pattern>/requestAPIServlet</url-pattern>
</servlet-mapping>
</web-app>
Servlet程序:RequestAPIServlet
package com.atguigu.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class RequestAPIServlet extends HttpServlet {
private static final long serialVersionUID = 6929316627289085482L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// i.getRequestURI() 获取请求的资源路径
System.out.println("URI => " + req.getRequestURI());
// ii.getRequestURL() 获取请求的统一资源定位符(绝对路径)
System.out.println("URL => " + req.getRequestURL());
// iii.getRemoteHost() 获取客户端的ip地址
/**
* 在IDEA中,使用localhost访问时,得到的客户端 ip 地址是 ===>>> 127.0.0.1<br/>
* 在IDEA中,使用127.0.0.1访问时,得到的客户端 ip 地址是 ===>>> 127.0.0.1<br/>
* 在IDEA中,使用 真实ip 访问时,得到的客户端 ip 地址是 ===>>> 真实的客户端 ip 地址<br/>
*/
System.out.println("客户端 ip地址 => " + req.getRemoteHost());
// iv.getHeader() 获取请求头
System.out.println("请求头User-Agent ==>> " + req.getHeader("User-Agent"));
// vii.getMethod() 获取请求的方式GET或POST
System.out.println( "请求的方式 ==>> " + req.getMethod() );
}
}
在浏览器中输入地址访问tomact服务器中的servlet程序(前提:tomact先启动)。
5.3 如何获取请求参数
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>ParameterServlet</servlet-name>
<servlet-class>com.atguigu.servlet.ParameterServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ParameterServlet</servlet-name>
<url-pattern>/parameterServlet</url-pattern>
</servlet-mapping>
</web-app>
表单fom.html:分别使用get,post请求,在web目录下
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://localhost:8080/dongtai-web-servlet2/parameterServlet" 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>
Java 代码:
package com.atguigu.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
public class ParameterServlet extends HttpServlet {
private static final long serialVersionUID = 2944615058694247321L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("-------------doGet------------");
// 获取请求参数,值为表单name属性的值 (这是通过form表单提交参数,获取里面的请求参数的方式)
String username = req.getParameter("username");
//getParameter()获取一个参数值,如果相同的name值有多个,这个方法只能获取第一个的value值。
String password = req.getParameter("password");
//getParameterValues()获取一个参数数组
String[] hobby = req.getParameterValues("hobby");
System.out.println("用户名:" + username);
System.out.println("密码:" + password);
//数组直接输出的是地址,使用toString()方法,或者转化为集合在输出 因为集合中的数据String是系统提供的类,会自动重写toString()方法。
System.out.println("兴趣爱好:" + Arrays.toString(hobby));//方式一
//System.out.println("兴趣爱好:" + Arrays.asList(hobby));//方式二
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("-------------doPost------------");
// 获取请求参数
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobby = req.getParameterValues("hobby");
System.out.println("用户名:" + username);
System.out.println("密码:" + password);
System.out.println("兴趣爱好:" + Arrays.asList(hobby));
}
}
分别使用get和post请求,在浏览器中输入地址,添加数据后点击提交按钮,可以看出不论是get还是post都可以获取到请求参数。
5.4 Get 请求的中文乱码解决:
如果请求是GET提交,并且tomcat是8.0及以后的版本,GET提交的中文参数,在获取时不会出现乱码问题!(8.0以后的tomcat包括8.0在获取GET提交的中文参数时,已经处理中文乱码问题。)
如果请求是GET提交,并且tomcat是7.0及以前的版本,GET提交的中文参数,在获取时会出现乱码问题!
解决:在[tomcat安装目录]/ conf/server.xml文件的(修改端口的)Connector标签上,添加一个 URIEncoding=“utf-8” 属性,如下:
<Connector port="80" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="utf-8" />
5.5 POST 请求的中文乱码解决
问题:get请求的参数值(接收用户输入文本框的值)是中文也不会乱码,post请求的参数值(接收用户输入文本框的值)是中文会发生乱码
解决:设置请求体的字符集为UTF-8,从而解决post请求的中文乱码问题
注意:要在获取请求参数之前调用才有效
package com.atguigu.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
public class ParameterServlet extends HttpServlet {
private static final long serialVersionUID = 2944615058694247321L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("-------------doGet------------");
// 获取请求参数,值为表单name属性的值 (这是通过form表单提交参数,获取里面的请求参数的方式)
String username = req.getParameter("username");
//getParameter()获取一个参数值,如果相同的name值有多个,这个方法只能获取第一个的value值。
String password = req.getParameter("password");
//getParameterValues()获取一个参数数组
String[] hobby = req.getParameterValues("hobby");
System.out.println("用户名:" + username);
System.out.println("密码:" + password);
//数组直接输出的是地址,使用toString()方法,或者转化为集合在输出 因为集合中的数据String是系统提供的类,会自动重写toString()方法。
System.out.println("兴趣爱好:" + Arrays.toString(hobby));//方式一
//System.out.println("兴趣爱好:" + Arrays.asList(hobby));//方式二
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("-------------doPost------------");
//问题:get请求的参数值(接收用户输入文本框的值)是中文也不会乱码,post请求的参数值(接收用户输入文本框的值)是中文会发生乱码
// 解决:设置请求体的字符集为UTF-8,从而解决post请求的中文乱码问题
// 注意:要在获取请求参数之前调用才有效
req.setCharacterEncoding("UTF-8");
// 获取请求参数
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobby = req.getParameterValues("hobby");
System.out.println("用户名:" + username);
System.out.println("密码:" + password);
System.out.println("兴趣爱好:" + Arrays.asList(hobby));
}
}
5.6 请求的转发(作为域对象携带数据)
什么是请求的转发?
请求转发是指,服务器收到请求后,从一个资源跳转到另一个资源的操作叫请求转发(属于服务器内部资源的跳转)。
测试1:
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>Servlet1</servlet-name>
<servlet-class>com.atguigu.servlet.Servlet1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet1</servlet-name>
<url-pattern>/servlet1</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Servlet2</servlet-name>
<servlet-class>com.atguigu.servlet.Servlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet2</servlet-name>
<url-pattern>/servlet2</url-pattern>
</servlet-mapping>
</web-app>
Servlet1 代码:
package com.atguigu.servlet;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Servlet1 extends HttpServlet {
private static final long serialVersionUID = -6204548247948921305L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求的参数(办事的材料)查看
String username = req.getParameter("username");
System.out.println("在Servlet1(柜台1)中查看参数(材料):" + username);
// 给材料 盖一个章,并传递到Servlet2(柜台 2)去查看 把数据存入到域对象
req.setAttribute("key1","柜台1的章");//此对象也有域方法,类似于ServletContex域对象
// 问路:Servlet2(柜台 2)怎么走
/**
* 请求转发必须要以斜杠打头,/ 斜杠表示地址为:http://ip:port/工程名/ , 映射到IDEA代码的web目录<br/>
* 路径:为xml文件中配置的servlet2的访问路径
*/
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servlet2");
// 走向Sevlet2(柜台 2) Dispatcher: 调度员
requestDispatcher.forward(req,resp);
}
}
Servlet2 代码:
package com.atguigu.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Servlet2 extends HttpServlet {
private static final long serialVersionUID = -302871879227334765L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求的参数(办事的材料)查看
String username = req.getParameter("username");
System.out.println("在Servlet2(柜台2)中查看参数(材料):" + username);
// 查看 柜台1 是否有盖章 取出域对象存入的数据
Object key1 = req.getAttribute("key1");//要和存的key保持一致
System.out.println("柜台1是否有章:" + key1);
// 处理自己的业务
System.out.println("Servlet2 处理自己的业务 ");
}
}
在浏览器中输入地址访问servlet1的地址,并添加请求参数进行测试:
可以看到在servlet2中拿到了servlet1存入域对象的数据,说明转发成功。
测试2:可以转发到WEB-INF目录下
把form.html放到WEB-INF目录下:
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://localhost:8080/dongtai-web-servlet2/parameterServlet" 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>
修改转发路径:
package com.atguigu.servlet;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Servlet1 extends HttpServlet {
private static final long serialVersionUID = -6204548247948921305L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求的参数(办事的材料)查看
String username = req.getParameter("username");
System.out.println("在Servlet1(柜台1)中查看参数(材料):" + username);
// 给材料 盖一个章,并传递到Servlet2(柜台 2)去查看
req.setAttribute("key1","柜台1的章");//此对象也有域方法,类似于ServletContex域对象
// 问路:Servlet2(柜台 2)怎么走
/**
* 请求转发必须要以斜杠打头,/ 斜杠表示地址为:http://ip:port/工程名/ , 映射到IDEA代码的web目录<br/>
* 路径:为xml文件中配置的servlet2的访问路径
*/
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/WEB-INF/form.html");
requestDispatcher.forward(req,resp);//执行转发
}
}
在浏览器中直接输入地址不能访问WEB-INF目录下的文件。
但是通过请求转发可以进行访问,在浏览器输入地址访问servlet1的程序,在servlet1中通过请求转发访问WEB-INF目录下的form.html文件。
测试3:不可以转发到工程以外的资源。
修改servlet1的访问路径:
package com.atguigu.servlet;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Servlet1 extends HttpServlet {
private static final long serialVersionUID = -6204548247948921305L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求的参数(办事的材料)查看
String username = req.getParameter("username");
System.out.println("在Servlet1(柜台1)中查看参数(材料):" + username);
// 给材料 盖一个章,并传递到Servlet2(柜台 2)去查看
req.setAttribute("key1","柜台1的章");//此对象也有域方法,类似于ServletContex域对象
// 问路:Servlet2(柜台 2)怎么走
/**
* 请求转发必须要以斜杠打头,/ 斜杠表示地址为:http://ip:port/工程名/ , 映射到IDEA代码的web目录<br/>
* 路径:为xml文件中配置的servlet2的访问路径
*/
RequestDispatcher requestDispatcher = req.getRequestDispatcher("http://www.baidu.com");
requestDispatcher.forward(req,resp);//执行转发
}
}
在浏览器中输入servlet1的访问地址进行测试:可以看出不可以转发到工程以外的资源。
5.7 base 标签的作用
解释:相对路径跳转原理:
浏览器地址:http://localhost:8080/07_servlet/a/b/c.html
跳转回去的路径是:../../index.html
参考后的得到地址过程解释:http://localhost:8080/07_servlet/a/b/c.html ---》../../index.html
../代表当前文件下所在目录的父目录,即c.html处在父目录a下,去掉../相当于把a目录下的目录文件去掉变为http://localhost:8080/07_servlet/a/ ---》../index.html。在次去掉../相当于把07_servlet下的目录去掉,变为http://localhost:8080/07_servlet/index.html
总结:去掉一层..相当于去掉一层目录。
注意:请求转发的浏览器地址不会发生变化指的是,访问servlet的过程到另一个servlet的浏览器地址不会改变。(地址浏览器–访问–> A --转发–> B,地址栏地址始终指向A的地址。)
而这里指的是一个通过servlet请求转发访问,一个是直接访问web/a/b/c.html目录下的资源,浏览器路径当然会发生变化。
测试:
1.在web下创建index.html文件
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
这是Web下的index.html <br/>
<!--省略了./ 用的是相对路径,解释:
2个页面结构为:web/index.html web/a/b/c.html 都在同一个web下,所以./就表示相对于web目录,
而项目启动自动访问到index.html页面的地址,http://localhost:8080/07_servlet/index.html
web目录就是项目名,所以相对于web目录就是相对于项目名,即./省略了http://localhost:8080/07_servlet
-->
<a href="a/b/c.html">a/b/c.html</a><br/>
<a href="http://localhost:8080/dongtai-web-servlet2/forwardC">请求转发:a/b/c.html</a><br/>
</body>
</html>
2.在web目录下创建a目录,在a目录下创建b目录,在b目录下创建c.html文件。
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--base标签设置页面相对路径工作时参照的地址
href 属性就是参数的地址值
注意:base标签中最后一个资源名可以省略,即c.html可以省略,但是/不能省,有/才代表是个目录,没有
斜杠就是个文件了。
-->
<base href="http://localhost:8080/dongtai-web-servlet2/a/b/c.html">
</head>
<body>
这是a下的b下的c.html页面<br/>
<a href="../../index.html">跳回首页</a><br/>
</body>
</html>
3.web.xml配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>ForwardC</servlet-name>
<servlet-class>com.atguigu.servlet.ForwardC</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ForwardC</servlet-name>
<url-pattern>/forwardC</url-pattern>
</servlet-mapping>
</web-app>
4.servlet程序:
package com.atguigu.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ForwardC extends HttpServlet {
private static final long serialVersionUID = -5960088181218849289L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("经过了ForwardC程序");
req.getRequestDispatcher("/a/b/c.html").forward(req, resp);
}
}
说明:
问题:启动tomact服务器,会自动访问web工程下的index.html页面,在index页面分别使用超链接直接跳转到c.html页面 和 使用超链接访问servlet程序 在通过servlet程序请求转发跳转到c.html页面,此时都可以调转成功。但是反过来在c.html页面使用超链接跳转到index.html,发现只有是通过超链接跳转到c.html页面的能够反过来跳转,而使用servlet程序请求转发跳转到的c.html不能反过来跳转。
原因:使用servlet程序进行求求转发跳转时,浏览器的访问路径发生了改变,而在跳转回去时相对路径是参照当前浏览器的路径而进行跳转的,此时浏览器地址发生了改变,也就不能跳转成功。
解决:使用base标签设置当前页面所有相对路径工作时,参考那个路径进行跳转。
在c.html中设置返回index.html页面时,相对路径参考的浏览器地址:
此时:从index.html页面跳转到c.html页面,在从c.html页面回跳到index.html页面,2中方式都能成功跳转和返回。
5.8 回顾:Web中的相对路径和绝对路径
在 javaWeb 中,路径分为相对路径和绝对路径两种:
相对路径是:
. 表示当前目录
.. 表示上一级目录
资源名 表示当前目录/资源名
绝对路径:
http://ip:port/工程路径/资源路径
在实际开发中,路径都使用绝对路径,而不简单的使用相对路径
。
1、绝对路径
2、base+相对
5.9 web中路径以/斜杠开头的不同意义
在 web 中 / 斜杠 是一种绝对路径。
/ 斜杠如果被浏览器解析,得到的地址是:http://ip:port/
如: <a href="/">斜杠</a>
/ 斜杠 如果被服务器解析,得到的地址是:http://ip:port/工程路径
如:
1、<url-pattern>/servlet1</url-pattern>
web.xml配置的servlet路径时讲的
2、servletContext.getRealPath(“/”);
域对象时讲的
3、request.getRequestDispatcher(“/”);
请求转发时讲的
特殊情况,请求重定向: response.sendRediect(“/”); 虽然是写在服务器上,但是他不直接解析,而是把斜杠发送给浏览器解析。得到 http://ip:port/
6. HttpServletResponse 类
6.1 HttpServletResponse 类的作用
HttpServletResponse 类和 HttpServletRequest 类一样。每次请求进来,Tomcat 服务器都会创建一个 Response 对象传递给 Servlet 程序去使用。
HttpServletRequest 表示请求过来的信息,HttpServletResponse 表示所有响应的信息,我们如果需要设置返回给客户端的信息,都可以通过 HttpServletResponse 对象来进行设置。
6.2 两个输出流的说明。
说明:服务器响应数据返回给客户端,是通过流的方式返回的。
字节流 getOutputStream(); 常用于下载(传递二进制数据)
字符流 getWriter(); 常用于回传字符串(常用)
两个流同时只能使用一个。
使用了字节流,就不能再使用字符流,反之亦然,否则就会报错。
测试:
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>ResponseIOServlet</servlet-name>
<servlet-class>com.atguigu.servlet.ResponseIOServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ResponseIOServlet</servlet-name>
<url-pattern>/responseIOServlet</url-pattern>
</servlet-mapping>
</web-app>
servlet程序:
package com.atguigu.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ResponseIOServlet extends HttpServlet {
private static final long serialVersionUID = -1915614522680378148L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//两个流同时只能使用一个。
//使用了字节流,就不能再使用字符流,反之亦然,否则就会报错。
response.getWriter();//使用字节流
response.getOutputStream();//使用字符流
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
注意:因为是在浏览器输入地址是get请求,调用的是doGet()方法,所以测试代码要写在doGet()方法中测试。
启动tomact服务器,在浏览器输入访问sevlet的地址进行测试,会报异常。
原因:
6.3 如何往客户端回传数据
要求 : 往客户端回传 字符串 数据。
测试:
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>ResponseIOServlet</servlet-name>
<servlet-class>com.atguigu.servlet.ResponseIOServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ResponseIOServlet</servlet-name>
<url-pattern>/responseIOServlet</url-pattern>
</servlet-mapping>
</web-app>
servlet程序:
package com.atguigu.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class ResponseIOServlet extends HttpServlet {
private static final long serialVersionUID = -1915614522680378148L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//要求 : 往客户端回传 字符串 数据。
PrintWriter wirter = response.getWriter();
wirter.write("response text");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
启动tomact,在浏览器输入地址进行测试:
6.4 响应的乱码解决
说明:响应的数据是中文时会产生乱码,需要设置字符集,有2种方式。
package com.atguigu.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class ResponseIOServlet extends HttpServlet {
private static final long serialVersionUID = -1915614522680378148L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//查看响应的默认字符集
System.out.println( response.getCharacterEncoding() );//默认ISO-8859-1
//方式一:分开写
// 设置服务器字符集为UTF-8
//response.setCharacterEncoding("UTF-8");
// 通过响应头,设置浏览器也使用UTF-8字符集 响应的数据类型 文本/html 编码
//response.setHeader("Content-Type", "text/html; charset=UTF-8");
//方式二(推荐使用):一步到位
// 它会同时设置服务器和客户端都使用UTF-8字符集,还设置了响应头
// 此方法一定要在获取流对象之前调用才有效
response.setContentType("text/html; charset=UTF-8");
//要求 : 往客户端回传 字符串 数据。
PrintWriter wirter = response.getWriter();
wirter.write("国哥很帅!!!!");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>ResponseIOServlet</servlet-name>
<servlet-class>com.atguigu.servlet.ResponseIOServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ResponseIOServlet</servlet-name>
<url-pattern>/responseIOServlet</url-pattern>
</servlet-mapping>
</web-app>
在浏览器输入访问servlet程序的地址,可以看到返回给浏览器页面的中文可以正确显示。
6.5 请求重定向
请求重定向,是指客户端给服务器发请求,然后服务器告诉客户端说:我给你一些地址,你去新地址访问,叫请求重定向(因为之前的地址可能已经被废弃)。
测试:
Response1:
package com.atguigu.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Response1 extends HttpServlet {
private static final long serialVersionUID = -6342912739252936213L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("曾到此一游 Response1 ");
//测试1:重定向在域对象中存入数据是否能够共享。不共享说明重定向不能携带数据。
req.setAttribute("key1", "value1");
//方式一:
// 设置响应状态码302 ,表示重定向,(已搬迁)
//resp.setStatus(302);
// 设置响应头说明 新的地址在哪里
//resp.setHeader("Location", "http://localhost:8080/dongtai-web-servlet2/response2");
//方式二(推荐使用) 302是固定的在源码中设置好了,你不需要在设置
resp.sendRedirect("http://localhost:8080/dongtai-web-servlet2/response2");
//测试2:重定向不能转发到WEB-INF目录下的资源
//原因:浏览器不能直接访问WEB-INF写的资源,而重定向的2次发送请求都是通过浏览器发送的,所以重定向不能转发到WEB-INF目录下的资源
//方式一:Location:位置,地址
//resp.setHeader("Location", "http://localhost:8080/dongtai-web-servlet2/WEB-INF/form.html");
//测试3:可以访问当前程序以外的资源
//resp.setHeader("Location", "http://www.baidu.com");
//方式二(推荐使用) 302是固定的在源码中设置好了,你不需要在设置
//resp.sendRedirect("http://www.baidu.com");
}
}
Response2:
package com.atguigu.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Response2 extends HttpServlet {
private static final long serialVersionUID = 5018722793226197068L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取域对象中的数据为null,说明重定向中的域对象数据不共享 ,也就不能携带数据。
//原因:tomact每次收到请求就会把请求中的数据解析好封装成一个request对象中,重定向是发送了
// 一个新的请求 地址栏发生了变化,也就是一个新的request对象,这个新的request对象哪会有原来request对象携带的数据。
System.out.println(req.getAttribute("key1"));//null
resp.getWriter().write("response2's result!");
}
}
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>Response1</servlet-name>
<servlet-class>com.atguigu.servlet.Response1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Response1</servlet-name>
<url-pattern>/response1</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Response2</servlet-name>
<servlet-class>com.atguigu.servlet.Response2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Response2</servlet-name>
<url-pattern>/response2</url-pattern>
</servlet-mapping>
</web-app>
启动tomact服务器,在浏览器输入访问Response1的servlet地址,可以看到重定向到百度成功。