Servlet
什么是Servlet
第一个Servlet程序
关于Servlet可以参考JavaEE文档
- Servlet是一个接口,我们只用实现这个接口就可以 了
关于为什么Servlet标红,因为Servlet是JavaEE里面的内容,我们点击红色的内容按照引导,下载JavaEE即可 - 实现步骤
我们需要去配置一下Servlet程序的访问地址,否则我们的服务器不知道Servlet程序
- Servlet程序(实现Servlet接口并重写Service方法)
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;
}
@Override
//service方法是专门用来处理请求和相应的
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() {
}
}
- 给servlet程序配置地址(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程序的类名)-->
<servlet-name>HelloServlet</servlet-name>
<!--servlet-class标签是servlet程序的全类名-->
<servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
</servlet>
<!--servlet-mapping标签给servlet程序配置访问地址-->
<servlet-mapping>
<!--servlet-name标签作用是告诉服务器配置的地址给哪个servlet程序使用(一般和上面的servlet-name相同)-->
<servlet-name>HelloServlet</servlet-name>
<!--url-pattern标签配置访问地址 <br/>
/斜杠在服务器解析的时候,表示地址为:http://ip:port/工程路径 <br/>
/hello 表示地址为http://ip:port/工程路径/hello
-->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
- 1.我们打开服务器将自动访问index.jsp文件
- 此时我们的程序将会自动访问servlet程序中重写的service方法
Servlet程序常见错误
- 1.建议servlet类名要和其地址有对应关系(其地址可以自己配置)
- 错误1.url-pattern中配置的路径不以斜杠开头
- 错误2:servlet-name值不存在
- 错误3:servlet-class标签的全类名出错
这个在IDEA中可以通过输入类名自动补全来避免这个错误
url地址如何定位到servlet程序去访问
Servlet生命周期方法
- 验证这个过程
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;
}
@Override
//service方法是专门用来处理请求和相应的
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("3.service方法");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("4.destroy销毁方法");
}
}
- 当我连续再次方法,发现只有service方法被方法
我们发现我们连续多次方法,我们构造方法只会执行一次,说明我们所访问的实例是同一个,说明我们的servlet对象是单例的
- 停止工程时会执行destroy方法销毁实例
servlet请求的分发处理
我们的service方法专门用来处理请求的,但是我们将html的时候知道有get和post两种请求
- 我们写一个html文件用来验证请求的处理
- 在web目录下创建
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--需要提交的表单:提交给servlet程序处理-->
<form action="http://localhost:8080/tomcatTest2/hello" method="get">
<input type="submit">
</form>
</body>
</html>
-
当我们点击提交将会访问servlet程序
-
当我们将提交方式改成post并且再次提交
-
修改后我们需要更新类和资源
-
查看源代码确认已经修改
-
点击提交后发现访问的还是同样的service方法
但是get和post请求干的事情是不一样的,我们需要实现这一点
我们可以通过获取现在请求的方式,来判断请求的类型,进而实现不同的请求干不同的事情
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;
}
@Override
//service方法是专门用来处理请求和相应的
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
//类型转换(因为HttpServletRequest有getMethod方法)
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String method = httpServletRequest.getMethod();//获取请求方式
if("GET".equals(method)){
System.out.println("GET请求");
}else if("POST".equals(method)){
System.out.println("POST请求");
}
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("4.destroy销毁方法");
}
}
但是如果我们的代码很多,都写在if else语句中会十分臃肿,所以我们可以将处理get和post请求的语句集成为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;
}
@Override
//service方法是专门用来处理请求和相应的
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
//类型转换(因为HttpServletRequest有getMethod方法)
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String method = httpServletRequest.getMethod();//获取请求方式
if("GET".equals(method)){
doGet();
}else if("POST".equals(method)){
doPost();
}
}
public void doGet(){
System.out.println("处理get请求");
System.out.println("处理get请求");
}
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销毁方法");
}
}
通过继承HttpServlet实现Servlet程序
在实际的开发中我们一般很少使用实现Servlet接口的方式去实现Servlet程序
我们通过继承HttpServlet类,并且重写HttpServlet类中的doGet和doPost方法,来实现
- 通过继承HttpServlet的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;
//继承HttpServlet类,并重写doGet和doPost方法
public class HelloServlet2 extends HttpServlet {
/*
*doGet()在get请求的时候调用
*
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloServlet2的doGet方法");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloServlet2的doPost方法");
}
}
- 此时我们还需要在web.xml文件中标注新的servlet程序的访问地址
- 此时我们的web.xml文件中有2个servlet程序的访问地址
<?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程序的类名)-->
<servlet-name>HelloServlet</servlet-name>
<!--servlet-class标签是servlet程序的全类名(输入类名可以自动生成)-->
<servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
</servlet>
<!--servlet-mapping标签给servlet程序配置访问地址-->
<servlet-mapping>
<!--servlet-name标签作用是告诉服务器配置的地址给哪个servlet程序使用(一般和上面的servlet-name相同)-->
<servlet-name>HelloServlet</servlet-name>
<!--url-pattern标签配置访问地址 <br/>
/斜杠在服务器解析的时候,表示地址为:http://ip:port/工程路径 <br/>
/hello 表示地址为http://ip:port/工程路径/hello
-->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>HelloServlet2</servlet-name><!--servlet程序的别名-->
<servlet-class>com.atguigu.servlet.HelloServlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet2</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>
</web-app>
- 我们将a.html文件中表单提交的跳转地址改成新的servlet程序的地址就可以使用了
和前面的方式相比:我们的新的方式不需要判断提交的方式是get还是post。我们的程序可以自动根据提交的方式自动调用doGet或者是doPost方法
通过IDEA创建Servlet程序
在实际的开发中确实是用继承自HttpServlet类的方式实现Servlet程序,但是我们可以借助IDEA自动生成
-
将会自动生成
-
1.servlet程序的配置地址信息
-
2.继承自httpServlet的servlet程序
后面将a.html文件的提交地址改成新的servlet程序的地址即可使用了
整个Servlet类的继承体系
小总结
我们每次访问servlet程序的时候将会自动调用servlet的service方法,我们的HelloServlet程序继承自HttpServlet,所以将会调用HttpService的servicCe方法,service方法中将会调用doGet和doPost,而这2个方法我们在HelloServlet类中进行了重写,所以将会调用重写后的方法
ServletConfig类的使用介绍
init-parm是参数,在配置的时候可以配置多组
- servlet配置文件(看第一个即可)
<?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程序的类名)-->
<servlet-name>HelloServlet</servlet-name>
<!--servlet-class标签是servlet程序的全类名(输入类名可以自动生成)-->
<servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
<init-param>
<!--参数值(是一个键值对)-->
<param-name>username</param-name><!--参数名-->
<param-value>root</param-value><!--参数值-->
</init-param>
</servlet>
<!--servlet-mapping标签给servlet程序配置访问地址-->
<servlet-mapping>
<!--servlet-name标签作用是告诉服务器配置的地址给哪个servlet程序使用(一般和上面的servlet-name相同)-->
<servlet-name>HelloServlet</servlet-name>
<!--url-pattern标签配置访问地址 <br/>
/斜杠在服务器解析的时候,表示地址为:http://ip:port/工程路径 <br/>
/hello 表示地址为http://ip:port/工程路径/hello
-->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>HelloServlet3</servlet-name>
<servlet-class>com.atguigu.servlet.HelloServlet3</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet3</servlet-name>
<url-pattern>/hello3</url-pattern>
</servlet-mapping>
</web-app>
- servlet程序
package com.atguigu.servlet;
import javax.servlet.*;
import javax.servlet.http.HttpServlet;
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初始化方法");
//1.可以获取Servlet程序别名servlet-name
System.out.println("HelloServlet程序的别名servlet-name:"+servletConfig.getServletName());
//2. 可以获取初始化参数init-param
System.out.println("初始参数username的值是:"+servletConfig.getInitParameter("username"));
//3.可以获取ServletContext对象
System.out.println("ServletContext对象:"+servletConfig.getServletContext());
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
//service方法是专门用来处理请求和相应的
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
//类型转换(因为HttpServletRequest有getMethod方法)
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String method = httpServletRequest.getMethod();//获取请求方式
if("GET".equals(method)){
doGet();
}else if("POST".equals(method)){
doPost();
}
}
public void doGet(){
System.out.println("处理get请求");
System.out.println("处理get请求");
}
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.在之前的web.xml配置中,我们没有配置init-parm(初始化参数),在演示的时候必须配置
我们的ServletConfig类是专门记录Servlet程序配置信息的类,在Servlet程序被创建的时候,将会随之创建一个ServletConfig对象用来记录Servlet程序的配置。当在执行init方法的时候,将会读取web.xml文件的信息,此时的ServletConfig对象也会被初始化。
所以在我们输出一些ServletConfig对象的信息的时候,其实他已经被初始化了
ServletConfig类的补充说明
每一个ServletConfig对象是对应他自己的Servlet程序的
- 1.在servlet程序中其他地方也可以使用servletConfig对象
- 2.每一个ServletConfig对象对应自己的servlet程序,只能获取自己servlet程序的配置
- 包含3个servlet程序配置信息的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程序的类名)-->
<servlet-name>HelloServlet</servlet-name>
<!--servlet-class标签是servlet程序的全类名(输入类名可以自动生成)-->
<servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
<init-param>
<!--参数值(是一个键值对)-->
<param-name>username</param-name><!--参数名-->
<param-value>root</param-value><!--参数值-->
</init-param>
</servlet>
<!--servlet-mapping标签给servlet程序配置访问地址-->
<servlet-mapping>
<!--servlet-name标签作用是告诉服务器配置的地址给哪个servlet程序使用(一般和上面的servlet-name相同)-->
<servlet-name>HelloServlet</servlet-name>
<!--url-pattern标签配置访问地址 <br/>
/斜杠在服务器解析的时候,表示地址为:http://ip:port/工程路径 <br/>
/hello 表示地址为http://ip:port/工程路径/hello
-->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>HelloServlet2</servlet-name>
<servlet-class>com.atguigu.servlet.HelloServlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet2</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>HelloServlet3</servlet-name>
<servlet-class>com.atguigu.servlet.HelloServlet3</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet3</servlet-name>
<url-pattern>/hello3</url-pattern>
</servlet-mapping>
</web-app>
package com.atguigu.servlet;
import javax.servlet.GenericServlet;
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;
//继承HttpServlet类,并重写doGet和doPost方法
public class HelloServlet2 extends HttpServlet {
/*
*doGet()在get请求的时候调用
*
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloServlet2的doGet方法");
final ServletConfig servletConfig = super.getServletConfig();//获取ServletConfig对象
System.out.println(servletConfig);//org.apache.catalina.core.StandardWrapperFacade@7bb1a9aa
//2. 可以获取初始化参数init-param(我们用这个类的servletConfig对象获取别的servlet程序的配置信息)
System.out.println("初始参数username的值是:"+servletConfig.getInitParameter("username"));//null
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloServlet2的doPost方法");
}
}
- 最后当然是得不到别的servlet程序的配置信息
- 3.当我们重写了init(ServletConfit confit)(有参Init方法)将会出现空指针异常
-servlet程序
package com.atguigu.servlet;
import javax.servlet.GenericServlet;
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;
//继承HttpServlet类,并重写doGet和doPost方法
public class HelloServlet2 extends HttpServlet {
//重写init方法
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("重写了init初始化方法,做了一些工作");
}
// *doGet()在get请求的时候调用
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloServlet2的doGet方法");
final ServletConfig servletConfig = super.getServletConfig();//获取ServletConfig对象
System.out.println(servletConfig);//org.apache.catalina.core.StandardWrapperFacade@7bb1a9aa
//2. 可以获取初始化参数init-param(我们用这个类的servletConfig对象获取别的servlet程序的配置信息)
System.out.println("初始参数username的值是:"+servletConfig.getInitParameter("username"));//null
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloServlet2的doPost方法");
}
}
- 为什么会这样呢
- 我们打开该方法的父类GenericServlet类
此时就可以解析这个异常了。当我们重写了inint方法,此时在创建servlet程序的时候调用的就是重写后的Init方法,此时并没有将ServletConfig引用保存下来,所以我们通过getInitParameter方法得到到对象就为null
ServletContext对象的介绍
ServletContext对象的作用演示
init-param只能是由ServletConfig对象获取,context-param只能由ServletContext对象获取
- 我们看到获取init-param和context-param使用的方法都是一样的,只是调用的对象不同,但是也不能由ServletContext对象获取init-param(两者不能串用)
package com.atguigu.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
public class ContextServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取 web.xml 中配置的上下文参数 context-param
ServletContext context = super.getServletConfig().getServletContext();//获取ServletContext对象
System.out.println("context-param参数username的值是:" +context.getInitParameter("username"));
System.out.println("context-param参数password的值是:" +context.getInitParameter("password"));
//2、获取当前的工程路径,格式: /工程路径
System.out.println(context.getContextPath());// /tomcatTest
///3、获取工程部署后在服务器硬盘上的绝对路径
/*
* /斜杠被服务器解析为:http://ip:port/工程名/
* /斜杠映射到idea代码的web目录
*
*/
System.out.println("工程部署的路径是"+context.getRealPath("/"));
System.out.println("css目录的绝对路是"+context.getRealPath("/css"));
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
ServletContext像Map一样存取数据
**web.xml里面ServletContext程序的地址里面/没有写,为什么会报错
解释:在我们启动服务器的时候,将会创建servlet程序的实例对象(一般是servlet接口的实现子类),在这个过程中会读取web.xml文件中servlet程序的配置信息。所以当在读取配置信息的时候servlet程序的访问地址不正确,将会报错
- 默认已经配置好了访问地址
package com.atguigu.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
public class ContextServlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取ServletContext对象(底层也是先获取ServletConfig对象,然后获取ServletContext对象)
ServletContext context = super.getServletContext();
System.out.println("保存之前:context1中获取域数据key1的值是"+context.getAttribute("key1"));
context.setAttribute("key1", "value1");
System.out.println("context1中获取域数据key1的值是"+context.getAttribute("key1"));
System.out.println("context1中获取域数据key1的值是"+context.getAttribute("key1"));
System.out.println("context1中获取域数据key1的值是"+context.getAttribute("key1"));
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
- 我们再创建一个ContextServlet2程序
我们在ContextServlet2中获取刚刚在ContextServlet1中存储的域数据key1的值
- 注意:在这个过程中我们不要重启服务器或者是重新部署,只用将访问路径改成contextServlet2的路径即可(相当于先访问contextServlet1,然后访问contextServlt2)
package com.atguigu.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
public class ContextServlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取Ser
ServletContext context = getServletContext();
System.out.println("context1中获取域数据key1的值是"+context.getAttribute("key1"));
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
- 然后我们再次访问contextServlet1程序
重新部署和重启服务器比较类似,重新部署会将正在运行的工程停止,然后将工程重新运行,只是不用重启服务器而已
-
对上面现象的解释
一个web工程只有一个ServletContext对象,在web工程启动的时候回被创建,在web工程停止的时候会被销毁。在上面的案例中我们在ContextServlet1中存储了key=key1 value=value1。然后我们在ContextServlet2中进行访问,此时已经存在,并且是属于整个工程的,所以就可以访问了。 -
当我们在重启服务器或者是重新部署的时候,我们的ServletContext对象会被销毁并且重新创建
注意:整个工程只有一个ServletContext对象,我们在ContextServlt1和ContextServlet2中获取的是同一个ServletCdontext对象(可以打印输出看一下)
什么是Http协议
GET请求HTTP协议内容介绍
当发送GET请求的时候,我们可以使用IE浏览器可以看到HTTP协议的内容
user-agent:用户代理,指的是代理用户发送信息,指的是浏览器(我们使用浏览器发送信息)
POST请求HTTP协议内容介绍
GET和POST大部分的请求头是相同的
- 假设我们下面的a.html程序向servlet程序提出请求(以post的方式)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--需要提交的表单:提交给servlet程序处理-->
<form action="http://localhost:8080/tomcatTest/hello3" method="post">
<input type="hidden" name="action" value="login"/>
<input type="hidden" name="username" value="root"/>
<input type="submit">
</form>
</body>
</html>
常用的请求头
- POST和GET有很多是重合的
哪些是GET请求 哪些是POST请求
- 现在的问题是知道请求分为GET和POST两种类型,但是还是不清楚哪些请求是GET请求哪些请求是POST请
响应的HTTP协议介绍
- 前面介绍的是请求时的HTTP协议,下面的是响应的时候的HTTP协议
常见的响应状态码说明
- 500举例:servlet程序在处理请求出现异常,响应状态码将为500
MIME数据类型
谷歌浏览器和火狐浏览器如何查看HTTP协议
- 在输入地址后按F12,并且查看network查看网络
- 我们在浏览器中输入请求的内容,并点击回车(是GET请求)
- 火狐和谷歌类型
Servlet2
HttpServletRequest类的介绍
HttpServletRequest类常用API演示
package com.atguigu.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
public class RequestAPIServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// i. getRequestURI() 获取请求的资源路径
System.out.println("URI=>"+request.getRequestURI());
// ii. getRequestURL() 获取请求的统一资源定位符(绝对路径)
System.out.println("URL=>"+request.getRequestURL());
// iii. getRemoteHost() 获取客户端的 ip 地址
/*
在IDEA中使用localhost访问时,得到的客户端ip地址是====>>127.0.0.1
在IDEA中使用127.0.0.1访问时,得到的客户端ip地址是====>>127.0.0.1
在IDEA中使用真实的ip访问时,得到的客户端ip地址是====>>真实的客户端Ip地址
*/
System.out.println("客户端ip地址是=>"+request.getRemoteHost());
// iv. getHeader() 获取请求头
System.out.println("请求头user-agent=>"+request.getHeader("User-Agent"));
// vii. getMethod() 获取请求的方式 GET 或 POST
System.out.println("请求的方式===>>"+request.getMethod());
}
}
获取请求的参数值
-
在Java Web开发中,请求参数主要指的是在HTTP请求中发送到服务器的数据
-
1.我们先创建一个准备提交数据的form.html表单(get请求)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://localhost:8080/tomcatTest/paramServlet" method="get">
用户名:<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
<input type="submit">
</form>
</body>
</html>
- 我们新创建了一个servlet程序,访问地址是
/paramServlet
package com.atguigu.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
public class ParamServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取请求参数
final String username = request.getParameter("username");
final String password = request.getParameter("password");
final String hobby = request.getParameter("hobby");
System.out.println("用户名:"+username);
System.out.println("密码:"+password);
System.out.println("兴趣爱好:"+hobby);
}
}
- 提交后成功获取到了数据
- 我们发现当我们进行了多选,即我们请求的参数有多个值,我们的`getParameter方法只能获取到一个值
- servlet代码
package com.atguigu.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.Arrays;
public class ParamServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取请求参数
final String username = request.getParameter("username");
final String password = request.getParameter("password");
final String[] hobbies = request.getParameterValues("hobby");
System.out.println("用户名:"+username);
System.out.println("密码:"+password);
System.out.println(Arrays.toString(hobbies));
}
}
`
解决Post请求中文乱码问题
- 现象展示:当我们post请求时,我们请求的参数出现中文时,将出现乱码问题
- form.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://localhost:8080/tomcatTest/paramServlet" 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
<input type="submit">
</form>
</body>
</html>
- servlet程序
package com.atguigu.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.Arrays;
public class ParamServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
//获取请求参数
final String username = request.getParameter("username");
final String password = request.getParameter("password");
final String[] hobbies = request.getParameterValues("hobby");
System.out.println("用户名:"+username);
System.out.println("密码:"+password);
System.out.println(Arrays.toString(hobbies));
}
}
- 解决:我们需要使用一个API设置请求头的字符串为UTF-8就可以解决了
- servlet程序
package com.atguigu.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.Arrays;
public class ParamServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
//设置请求体的字符为UTF-8,从而解决POST请求的中文乱码问题
request.setCharacterEncoding("UTF-8");
//获取请求参数
final String username = request.getParameter("username");
final String password = request.getParameter("password");
final String[] hobbies = request.getParameterValues("hobby");
System.out.println("用户名:"+username);
System.out.println("密码:"+password);
System.out.println(Arrays.toString(hobbies));
}
}
- 乱码问题消失了
setCharacterEncoding()设置请求体的字符集 使用注意
-
现象展示
-
发现此时还是出现了乱码问题
-
注意点结论
我们设置请求头的API需要放置在获取请求参数的函数之前才有效
请求转发
- 请求转发的操作步骤
- 我们创建2个servlet程序:servlet1和servlet2,地址分别为/servlet1和/servlet2
- servlet1
package com.atguigu.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
public class Servlet1 extends HttpServlet {//柜台1
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取请求的参数(办事的材料)查看
final String username = request.getParameter("username");
System.out.println("在servlet1(柜台1)中参看参数(材料)"+username);
//2.给材料盖一个章,并传递到servlet2(柜台2)去查看
request.setAttribute("key1","柜台1的章");
//3.问路:servlet2(柜台2)怎么走
/*
*请求转发必须要以/打头,/斜杠表示地址为:http://ip:port/工程名/ 映射到idea代码的web目录
*/
final RequestDispatcher requestDispatcher = request.getRequestDispatcher("/servlet2");
//4.走向servlet2(柜台2)
requestDispatcher.forward(request,response);
}
}
- servlet2
package com.atguigu.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
public class Servlet2 extends HttpServlet {//柜台2
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取请求的参数(办事的参数)查看
final String username = request.getParameter("username");
System.out.println("在servlet2(柜台2)中参看参数(材料)"+username);
//2.查看柜台1 是否盖章
final Object key1 = request.getAttribute("key1");//键要和servlet1中设置的一样
System.out.println("柜台1是否有章:"+key1);
//如果有章:servlet2处理自己的业务
System.out.println("servlet2开始处理自己的业务");
}
}
- 注意:虽然我们进行了请求的转发,但是我地址还是servlet1的地址
- 虽然访问了2个资源,但是属于一次请求
- WEB-INF下面的内容不能直接访问,但是请求的转发可以进去访问
- 演示:可以转发到WEB-INF目录下面的资源
- 直接转发到了form.html
base标签的作用
现象展示
- 我们让2个资源之间互相跳转(使用普通跳转)
- c.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
这是a下面的b下面的c.html页面
<a href= "../../index.html">a/b/c.html</a>跳回首页</a>
</body>
</html>
- index.hmtl
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
这是web下的index.html <br/>
<a href="a/b/c.html">a/b/c.html</a>
</body>
</html>
- 点击跳转
- 发现可以正常跳转
下面我们使用请求转发来跳转 - 我们先跳转到servlet程序,然后使用请求跳转到目标资源,然后从目标资源跳转到原资源
- c.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
这是a下面的b下面的c.html页面<br/>
<a href= "../../index.html">请求转发:a/b/c.html</a>跳回首页</a><!--跳回原来的文件-->
</body>
</html>
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
这是web下的index.html <br/>
<a href="http://localhost:8080/tomcatTest/forwardC">请求转发:a/b/c.html</a><!--跳转到forwardC的servlet程序-->
</body>
</html>
- servlet程序
package com.atguigu.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
public class ForwardC extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("经过了ForwardC程序");
request.getRequestDispatcher("/a/b/c.html").forward(request,response);
}
}
-
此时我们点击跳转后发现跳转失败了
跳转失败原因分析 -
所有的相对路径在工作的时候都会参照当前地址栏中的地址来跳转
发生这种情况的原因是相对路径跳转的时候参照的地址发生了变化
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--base标签设置页面相对路径工作时参照的地址
href属性就是参数的地址值
-->
<base href="http://localhost:8080/tomcatTest/a/b/c.html">
</head>
<body>
这是a下面的b下面的c.html页面<br/>
<a href= "../../index.html">请求转发:a/b/c.html</a>跳回首页</a><!--跳回原来的文件-->
</body>
</html>
回顾java web中的路径
斜杠在web中的不同意义
HttpServletResponse类的作用
两个响应流的介绍
- 如果同时使用的话,报错示例
如何给客户端回传字符串数据
- servlet程序
package com.atguigu.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
public class ResponseIOServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//要求:往客户端回传 字符串数据
final PrintWriter writer = response.getWriter();//获得字符流
writer.write("response contents");//往客户端回传数据
}
}
解决响应的中文乱码(方案一)
- 问题:当我们将前面回传的字符串改成中文,发现在显示的时候出现了乱码的问题
- 但我们将服务器的字符集改成UTF-8,发现还是出现了中文乱码
我们刚刚只是设置了服务器的字符集,但是当我们浏览器和服务器的字符集不统一的时候,也会出现显示乱码的问题 - 我们的浏览器中可以设置字符集,但是如果都需要用户来设置,这就太麻烦了,所以我们需要通过程序进行设置
- servlet程序
package com.atguigu.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
public class ResponseIOServlet extends HttpServlet {
@Override
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字符集
response.setHeader("content-type","text/html;charset=UTF-8");
//要求:往客户端回传 字符串数据
final PrintWriter writer = response.getWriter();//获得字符流
writer.write("你好啊");//往客户端回传数据
}
}
- 此时就显示成功了
解决响应中文乱码(方案二)(推荐使用)
- 使用
setContentType("text/html;charset=UTF-8");同时设置服务器和浏览器的字符集
package com.atguigu.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
public class ResponseIOServlet extends HttpServlet {
@Override
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字符集
// response.setHeader("content-type","text/html;charset=UTF-8");
//他会同时设置客户端和服务器都使用UTF-8,还设置了响应头
//注意:此方法一定要在获取流对象之前调用才有效
response.setContentType("text/html;charset=UTF-8");
//要求:往客户端回传 字符串数据
final PrintWriter writer = response.getWriter();//获得字符流
writer.write("你好啊");//往客户端回传数据
}
}
注意:此方法一定要在获取流对象之前使用才有效
请求重定向
- 请求重定向原理
- 创建2个servlet程序:response1和response2地址分别为/response1和response2
- response1
package com.atguigu.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
public class Response1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("曾到此一游Response1");
//1.设置响应码302,表示重定向(即表示已经搬迁)
response.setStatus(302);
//2.设置响应头,说明新的地址在哪里
response.setHeader("Location","http://localhost:8080//tomcatTest/response2");
}
}
- response2
package com.atguigu.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
public class Response2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//为了证明访问到了response2,向页面上打印一些内容
response.getWriter().write("response2's result");
}
}
- 解释:不共享Request域中的数据
此时我们的response2中不能得到response1中保存的对象
因为tomcat每次收到请求就会把请求收集起来,封装成一个Resquest对象,这是2次请求,所以封装成了2个对象
-
比如可以访问百度
-
为社么不能访问WB-INF下的资源
因为在第二次请求还是由浏览器发送的,而浏览器不能直接访问WEB-INF目录下,所以不能访问WEB-INF下的资源
请求重定向的第二种实现方案
- 方案2
package com.atguigu.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
public class Response1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("曾到此一游Response1");
//1.设置响应码302,表示重定向(即表示已经搬迁)
// response.setStatus(302);
//2.设置响应头,说明新的地址在哪里
// response.setHeader("Location","http://localhost:8080//tomcatTest/response2");
//方案二:直接设置跳转地址
response.sendRedirect("http://localhost:8080//tomcatTest/response2");
}
}