Web
JavaWeb技术栈
B/S架构:Browser/Server,即浏览器/服务器 架构模式

定义
超文本传输协议(Hyper Text Transfer Protocol),规定了浏览器和服务器之间数据传输的规则(即浏览器的请求数据和服务器的响应数据需要按照一定的格式规则来写)
浏览器F12打开开发者模式,NetWork下为浏览器请求的所有数据包

现在随便打开一个请求数据包以www.baidu.com这个数据包为例,详见图示说明

特点


请求数据分为三部分
key:value键值对形式常见的HTTP请求头
| 请求头 | 解释 |
|---|---|
| Host | 表示请求的主机名 |
| User-Agent | 浏览器版本,例如:Chrome浏览器的标识类似Mozilla/5.0 …Chrome/79,IE浏览器的标识类似Mozilla/5.0 (Windows NT …)like Gecko; |
| Accept | 表示浏览器能接收的资源类型,如text/*,image/或者/*表示所有; |
| Accept-Language | 表示浏览器偏好的语言,服务器可以据此返回不同语言的网页; |
| Accept-Encoding | 表示浏览器可以支持的压缩类型,例如gzip, deflate等。 |
GET请求和POST请求的区别

响应数据分为三部分

key:value键值对形式...这部分内容就是响应体常见的HTTP响应头
| 请求头 | 解释 |
|---|---|
| Content-Type | 表示该响应内容的类型,例如text/html,image/jpeg; |
| Content-Length | 表示该响应内容的长度(字节数); |
| Content-Encoding | 表示该响应压缩算法,例如gzip; |
| Cache-Control | 指示客户端应如何缓存,例如max-age=300表示可以最多缓存300秒 |

查看所有状态码详见网址:https://cloud.tencent.com/developer/chapter/13553
常见的响应状态码
| 状态码 | 英文 | 解释 |
|---|---|---|
| 200 | OK | 客户端请求成功。即处理成功,这是最想看到的状态码 |
| 302 | Found | 提示所请求的资源已移动到由Location响应头给定的URL,浏览器会自动重新访问到这个页面 |
| 304 | Not Modified | 告诉客户端,你请求的资源至上次取得后,服务器端并未更改,你直接使用本地缓存即可。此为隐式重定向 |
| 400 | Bad Request | 客户端请求由语法错误,不能被服务器所理解 |
| 403 | Forbidden | 服务器端收到请求但是拒绝提供服务。比如没有权限访问相关资源 |
| 404 | Not Found | 请求资源不存在,一般是URL输入错误或网站资源被删除 |
| 405 | Method Not Allowed | 请求方式有误,比如应该用GET请求方式的资源使用了POST |
| 428 | Precondition Required | 服务器要求有条件的请求,告诉客户端要想访问资源就必须携带特定的请求头 |
| 429 | To Many Requests | 太多请求,可以限制客户端请求某个资源的数量,配合Retry-After(多长时间后可以请求)响应体一起使用 |
| 431 | Request Header Fields Too Large | 请求头太大,服务器不愿意处理请求,因为它的头部字段太大,请求可以在减少请求头域的大小后重新提交 |
| 500 | Internal Server Error | 服务器发生不可预期的错误。服务器出异常了,赶紧看日志 |
| 503 | Service Unavailable | 服务器尚未准备好处理请求,服务器刚刚启动,还未初始化好 |
| 511 | Network Authentication Required | 客户端需要进行身份验证才能获取网络访问权限 |
步骤
Step1:创建Web项目,导入Servlet依赖坐标以及Tomcat插件,详见图一及pom.xml文件代码
provided(即该坐标在编译和测试时有效),因为Tomcat中已经提供好了Servlet的jar包,所以在打包后应该排除掉自己所设置的jar包,若不排除则会报错。 (后期所学的JSP坐标依赖范围也是provided)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>WebDemoartifactId>
<packaging>warpackaging>
<version>1.0-SNAPSHOTversion>
<name>WebDemo Maven Webappname>
<url>http://maven.apache.orgurl>
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>3.8.1version>
<scope>testscope>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
dependencies>
<build>
<finalName>WebDemofinalName>
<plugins>
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<version>2.2version>
plugin>
plugins>
build>
project>
Step2:创建at.guigu.web包并在该web包下定义一个类(此处以ServletDemo1类为例)实现Servlet接口,如图一,并重写接口中所有的方法,并在service方法中随便输入一句话,若将来service方法执行成功则说明Servlet接口成功被访问,详见ServletDemo1类的代码

package at.guigu.web;
import javax.servlet.*;
import java.io.IOException;
public class ServletDemo1 implements Servlet{
//将来Servlet接口被访问时service方法会被自动执行
//若service执行成功就说明Servlet接口访问成功
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("SERVLET Hello World");
}
@Override
public String getServletInfo() {
return "";
}
@Override
public void destroy() {
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
}
Step3:若想要Servlet接口被访问,就必须在刚刚的ServletDemo1类上使用@WebServlet("路径")注解来配置该Servlet访问路径,如下所示
package at.guigu.web;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet("/demo1")
public class ServletDemo1 implements Servlet{
//将来Servlet接口被访问时service方法会被自动执行
//若service执行成功就说明Servlet接口访问成功
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("SERVLET Hello World");
}
@Override
public String getServletInfo() {
return "";
}
@Override
public void destroy() {
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
}
Step4:启动Tomcat,浏览器输入URL
注意:
若用Tomcat插件启动Web项目时URL为http://localhost:8080/WebDemo/demo1访问该Servlet
@WebServlet注解配置的Servlet访问路径若用配置的本地插件启动Web项目时URL为默认URL加上/demo1来访问该Servlet,如图,此时URL即为http://localhost:8080/WebDemo_war/demo1

浏览器打开的URL为http://localhost:8080/WebDemo或http://localhost:8080/WebDemo_war ,都不会带/demo1,所以需要手动将其加上,之后刷新浏览器,此时idea服务器端才会运行service方法,此时才说明Servlet接口被成功访问,如图所示

问题
Servlet接口的类ServeltDemo1,在我们并没有创建该类的对象以及调用该类中service方法的情况下,为什么能够运行成功?那么是谁创建了该类的对象并调用了service方法,请详见执行流程URL各部分解析,以http://localhost:8080/WebDemo/demo1为例
http://localhost:8080:可访问到Tomcat服务器/WebDemo:可访问到Web项目/demo1:可访问到对应的Servlet所以若想要使Servlet接口被访问到则需要将url后补充上/demo1,即注解@WebServlet(urlPatterns = "/demo2")中urlPatterns的属性值
注意
实现Servlet接口的类ServeltDemo1是由Tomcat Web服务器创建的,且该类中的service方法也是由Tomcat Web服务器调用的,当该方法被调用时服务端就会向浏览器发送响应数据,由于我们在快速入门中并未向该方法中写入什么代码,所以只能看到一个空白页面,如图所示,当我们为该方法public void service(ServletRequest servletRequest, ServletResponse servletResponse)传入ServletRequest对象和ServletResponse对象两个参数之后就会给客户端浏览器返回对应的信息了

注解@WebServlet(urlPatterns = "/demo2")有时会写为@WebServlet(value = "/demo2"),这两个属性表示的含义是一样的
urlPatterns:可映射多个URL模式,示例如下
@WebServlet(urlPatterns = {"/example", "/example2"})
value:只能映射单个URL模式,示例如下
@WebServlet("/example")
它俩不能同时使用
问题:服务器怎么知道Serlet中一定有servlet方法?
Serlet必须实现Servlet接口并重写该接口中的方法,而Servlet接口中有service方法生命周期定义
Servlet运行在Servlet容器(即Web服务器)中,其生命周期由容器来管理,分为四个阶段:
Servlet第一次被访问时,由容器创建对象
@WebServlet注解的属性loadOnStartup来更改创建Servlet对象的时机
@WebServlet(urlPatterns="/demo1", loadOnStartup=1):当loadOnStartup属性值为负整数 时,代表默认情况即Servlet第一次被访问时,由容器创建对象;当loadOnStartup属性值为非负整数 时,代表服务器启动时就立即由容器创建对象,此时数字越小优先级越高。Servlet实例化之后,容器将调用Servlet接口的init()方法初始化这个对象,并完成一些如加载配置文件、创建连接等初始化的工作。注意:init()方法只会调用一次Servlet容器都会调用Servlet的service()方法对请求进行处理destroy()方法来完成资源的释放。在destroy()方法调用之后,容器就会释放这个Servlet实例(说白了就是释放init()方法所生成的资源),该实例随后会被Java的垃圾收集器所回收示例
package at.guigu.web;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
//@WebServlet(urlPatterns = "/demo2", loadOnStartup = 1)
@WebServlet("/demo2")
public class ServletDemo2 implements Servlet {
/**
* 初始化方法
* 1. 调用时机:默认情况下,Servlet被第一次访问时,容器将调用该方法
* 2. 调用次数:只会被调用一次
* @param servletConfig
* @throws ServletException
*/
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("Init()方法被调用");
}
/**
* 提供服务的方法
* 1. 调用时机及调用次数:每次Servlet被访问时都会调用该方法
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service()方法被调用");
}
/**
* 销毁方法
* 1. 调用时机:内存释放或服务器关闭时,容器会调用该方法来进行Servlet对象的被销毁
* 2. 调用次数:只会被调用1次
*/
@Override
public void destroy() {
System.out.println("destroy()方法");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return "";
}
}
init()及service()方法演示
@WebServlet注解的属性loadOnStartup的属性值默认为负整数时(即@WebServlet("/demo2"),此时loadOnStartup属性省略),则会在Servlet接口被第一次访问时 调用init()和service()方法且init()方法只会被调用一次,而service()方法会被调用多次(Servlet接口被访问一次则会调用一次)。如下所示

@WebServlet注解的属性loadOnStartup的属性值默认为非负整数(即@WebServlet(urlPatterns = "/demo2", loadOnStartup = 1))时,则会在服务器启动时 调用init()和service()方法init()方法只会被调用一次,而service()方法会被调用多次(Servlet接口被访问一次则会调用一次)。如下所示

destroy()方法演示
该方法在关闭服务器时会被调用,但是要注意,不能使用IDEA中的强制关闭按钮(它就相当于直接拔掉了电源),若使用则不会调用该方法,强制关闭按钮如图所示

若想要正常关闭则需要在Terminal控制台来启动该Web项目然后利用Ctrl+C来进行关闭,如下所示
Step1:打开Terminal控制台,输入mvn tomcat7:run命令运行当前Web项目

Step2:按住Ctrl+C关闭服务器

| 方法 | 解释 |
|---|---|
void init(ServletConfig config) | 初始化方法,在Servlet对象被创建时执行且只执行一次 |
void service(ServletRequest req, ServletResponse res) | 提供服务方法,每次Servlet接口被访问都会调用该方法 |
void destroy() | 销毁方法,当Servlet实例被销毁时调用该方法。在内存释放或服务器关闭时销毁Servlet实例 |
ServletConfig getServletConfig() | 获取ServletConfig对象 |
String getServletInfo() | 获取Servlet信息,比如:作者、版本、版权等,一般情况下返回空字符串或null |
前三个已在Servlet生命周期中讲解,后两个方法了解即可
HttpServlet实现类)
Servlet体系中提供了一个Servlet接口以及它的两个实现类
Servlet接口:Servlet体系的根接口GenericServlet类:Servlet接口的抽象实现类HttpServlet类:是对HTTP协议封装的Servlet接口的实现类,该类继承自GenericServlet类由于我i们开发的B/S架构的Web项目都是针对HTTP协议的,所以我们自定义实现Servlet接口的类就可以都继承自
HttpServlet类,并在重写其doGet()和doPost()方法后就完成了对Servlet的编写好处:不用像之前一样直接实现Servlet接口然后重写五个方法,因为那五个方法中其中有两个是不常用的,所以写了就会很麻烦,没有必要;使用继承自
HttpServlet的类则会大大减少不必要的代码冗余

HttpServlet实现类使用步骤
HttpServlet实现类原理
代码示例,供测试doGet()和doPost()方法使用
package at.guigu.web;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/demo3")
public class ServletDemo3 extends HttpServlet {
//若通过get方式访问Servlet接口时则会调用doGet方法
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get...");
}
//若通过post方式访问Servlet接口时则会调用doPost方法
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post...");
}
}
若通过get方式访问Servlet接口时则会调用doGet()方法

若通过post方式访问Servlet接口时则会调用doPost()方法
测试doPost()方法时我们需要先创建一个html文件并写入表单,设置表单的提交方式为post,并将action的属性值设置为服务器url,如图所示

Tomcat运行该Web项目,补全url并回车,如图一所示;填写页面的表单并提交,如图二所示,此时由于客户端是通过post方式向服务端发送数据,所以服务端调用doPost()方法,如图三所示




问题:HttpServlet中为什么要根据请求方式不同调用不同的方法,并且它们是如何被调用的?
若我们不使用HttpServlet这个实现类,自己写实现Servlet接口的实现类(假如类为ServletDemo4)的话,就需要重写service()方法,在该方法中我们首先要获取请求方式,然后根据请求方式的不同(get还是post)来进行不同的处理,service()方法的简略代码如下:
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
//由于请求方式需要在Http的协议下才能获得,所以需要先将ServletRequest对象强转为HttpServletRequest对象
HttpServletRequest request = (HttpServletRequest) req;
//获取请求方式
String method = request.getMethod();
//判断请求方式,根据请求方式的不同进行分别处理
if (method.equals("GET")) {
//get方式的处理逻辑
} else if (method.equals("POST")) {
//post方式的处理逻辑
}
}
HttpServlet实现类)供用户使用,这样用户可以通过继承HttpServlet实现类(相当于间接继承了Servlet接口)来使用Servlet接口中的方法Servlet要想被访问就必须配置其访问路径(即urlPattern)
一个Servlet可配置多个urlPattern,此时url+任意一个urlPattern就可成功访问Servlet
@WebServelt(urlPatterns = {"/demo5", "/demo6"})
代码示例(仅以get方式为例,post方式省略)
package at.guigu.web;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = {"/demo5", "/demo6"})
public class ServletDemo5 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get...");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post...");
}
}

urlPattern配置规则
精确匹配: 精确匹配特定的路径
我们在该小节之前的Servlet知识点中使用的都是精确匹配
比如@WebServlet(urlPatterns="/路径1/路径2"),那么此时在浏览器中url就需要加上/路径1/路径2之后才能成功访问Servlet接口
注意:精确匹配的路径可以自己随便写,不需要是真是存在的路径
示例如下
package at.guigu.web;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "/qwe/nihao")
public class ServletDemo6 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get...");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post...");
}
}

目录匹配(路径前缀匹配): 匹配指定路径 下的所有请求,路径以/*结尾。
比如@WebServlet(urlPatterns="/路径1/*"),此时我们就可匹配指定路径下的所有请求,也就是说/路径1/aaa、/路径1/bbb都可以,*可以被我们替换成任意的路径,路径均可以是不存在的路径;而路径1是我们已经确定下来的具体的路径(它也可以是不存在的路径),所以就不能更改路径1,只能更改*
示例如下
package at.guigu.web;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "/qwe/*")
public class ServletDemo7 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get...");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post...");
}
}

注意:@WebServlet(urlPatterns = "/*")也属于目录匹配(即路径前缀匹配),但它比较特殊,会在四种配置规则学完之后讲解
扩展名匹配: 匹配特定扩展名的请求
比如@WebServlet(urlPatterns="*.do"),此时*.do 会匹配所有以.do结尾的请求,如/foo.do、/bar.do等等。
注意:扩展名匹配不能以斜杠/开头
示例
package at.guigu.web;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "*.do")
public class ServletDemo8 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get...");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post...");
}
}

任意匹配: @WebServlet(urlPatterns = "/"): 匹配未被其它三种配置规则匹配的请求,该方式会覆盖掉Tomcat中DefaultServlet,此时就无法访问静态资源(比如html页面)了
示例
package at.guigu.web;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "/")
public class ServletDemo9 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get...");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post...");
}
}

此时Tomcat运行该Web项目,并将url改为http://localhost:8080/WebDemo/a.html,我们发现并不会出现对应的html页面,如下图所示

目录匹配中的特例@WebServlet(urlPatterns = "/*") : 它属于目录匹配(即路径前缀匹配)中的特例,它会覆盖扩展名匹配和任意匹配,匹配除未被精确匹配和一般目录匹配匹配的请求;而且该方式也会覆盖掉Tomcat中DefaultServlet,此时就无法访问静态资源(比如html页面)了(这个不在进行示例,可自行按照任意匹配中的示例自己进行举例)
示例1:Tomcat运行该Web项目,解释详见图示

注意
/*>扩展名路径>/Servlet从3.0版本之后才开始支持使用注解配置,3.0之前只支持XML配置文件的配置方式
配置步骤
编写Servlet实现类(利用XML配置Servlet时,不需要用注解)
package at.guigu.web;
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 ServletXmlDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("xml get...");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("xml post...");
}
}
在web.xml中配置该Servlet
DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Applicationdisplay-name>
<servlet>
<servlet-name>demoservlet-name>
<servlet-class>at.guigu.web.ServletXmlDemoservlet-class>
servlet>
<servlet-mapping>
<servlet-name>demoservlet-name>
<url-pattern>/demourl-pattern>
servlet-mapping>
web-app>
运行截图如下


RequestFacade由Tomcat提供void service(ServletRequest request, ServletResponse response)方法,它的两个参数其实就是Request 对象和Response 对象。
Request是三层继承体系
ServletRequest :Java提供的请求对象根接口HttpServletRequest :Java提供的对Http协议封装的请求对象接口RequestFacade :由Tomcat定义的实现类HttpServletRequest是ServletRequest的子接口,RequestFacade是HttpServletRequest的实现类注意
使用request对象中的方法时只需查阅JavaEE API 文档的HttpServletRequest接口即可 ,因为它是起其实现类。
HttpServletRequest是HttpServlet实现类中所用到的接口,ServletRequest是Servlet接口中所用到的接口,Java不提供ServletRequest和HttpServletRequest 的实现类,他们的实现类RequestFacade 由Tomcat提供
我们学习Servlet时,如果我们实现的是Servlet接口的话,那么service方法中的参数是ServletRequest接口,如图一所示;但是我们是通过继承HttpServlet来写Servlet代码的话则doGet和doPost方法使用的是HttpServletRequest接口,如图二所示。


请求行由三部分组成:请求方式 /请求资源路径和请求参数 / 请求的协议和版本号

请求行用到的方法
| 方法 | 解释 |
|---|---|
String getMethod() | 获取请求方式: GET |
String getContextPath() | 动态获取虚拟目录(即项目访问路径): /request-demo |
StringBuffer getRequestURL() | 获取URL(统一资源定位符:?前面的那一串): http://localhost:8080/request-demo/req1 |
String getRequestURI() | 获取URI(统一资源标识符): /request-demo/req1 |
String getQueryString() | 获取GET方式的请求参数(get方式的请求参数在url中): username=zhangsan&password=123 |
doPost方法中,详见请求体代码示例2代码示例
package at.guigu.webb;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/req1")
public class RequestOne extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1 获取请求方式
String method = req.getMethod();
System.out.println(method);
//2 动态获取虚拟目录即项目访问路径
//由于我并未去设置项目访问路径,所以默认为改项目名称即WebDemo
String contextPath = req.getContextPath();
System.out.println(contextPath);
//3 获取URL
StringBuffer reqUrl = req.getRequestURL();
System.out.println(reqUrl.toString());//将其转为字符串
//4 获取URI
String uri = req.getRequestURI();
System.out.println(uri);
//5 获取Get的请求参数
String message = req.getQueryString();
System.out.println(message);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post...");
}
}

请求头:格式为key:value键值对形式

String getHeader(String name):根据请求头名称来获取对应的值
代码示例
package at.guigu.webb;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
@WebServlet("/req1")
public class RequestOne extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1 获取请求方式
String method = req.getMethod();
System.out.println(method);
//2 动态获取虚拟目录即项目访问路径
//由于我并未去设置项目访问路径,所以默认为改项目名称即WebDemo
String contextPath = req.getContextPath();
System.out.println(contextPath);
//3 获取URL
StringBuffer reqUrl = req.getRequestURL();
System.out.println(reqUrl.toString());//将其转为字符串
//4 获取URI
String uri = req.getRequestURI();
System.out.println(uri);
//5 获取Get的请求参数
String message = req.getQueryString();
System.out.println(message);
System.out.println("-------------------------------------");
//获取请求头
String header = req.getHeader("User-Agent");
System.out.println(header);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}

请求体(只有post请求才有请求体):存放浏览器的请求参数

ServletInputStream getInputStream():获取字节输入流
BufferedReader getReader():获取字符输入流
以上两个方法可用来读取客户端发送的JSON字符串数据,可详见Axios、JSON综合案例中的添加
代码示例1
java代码
package at.guigu.webb;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
@WebServlet("/req1")
public class RequestOne extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1 获取请求方式
String method = req.getMethod();
System.out.println(method);
//2 动态获取虚拟目录即项目访问路径
//由于我并未去设置项目访问路径,所以默认为改项目名称即WebDemo
String contextPath = req.getContextPath();
System.out.println(contextPath);
//3 获取URL
StringBuffer reqUrl = req.getRequestURL();
System.out.println(reqUrl.toString());//将其转为字符串
//4 获取URI
String uri = req.getRequestURI();
System.out.println(uri);
//5 获取Get的请求参数
String message = req.getQueryString();
System.out.println(message);
System.out.println("-------------------------------------");
//获取请求头
String header = req.getHeader("User-Agent");
System.out.println(header);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求体
//获取字符输入流
BufferedReader br = req.getReader();
//读取数据
String line = br.readLine();
System.out.println(line);
}
}
html代码
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<form action="/WebDemo/req1" method="post">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit">
form>
body>
html>
若想要使html页面展示出来,则在Tomcat运行之后需要在url后加上/html的文件名,而不是/req1

代码示例2:html代码与示例1一样,此处省略
package at.guigu.webb;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
@WebServlet("/req1")
public class RequestOne extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1 获取请求方式
String method = req.getMethod();
System.out.println(method);
//2 动态获取虚拟目录即项目访问路径
//由于我并未去设置项目访问路径,所以默认为改项目名称即WebDemo
String contextPath = req.getContextPath();
System.out.println(contextPath);
//3 获取URL
StringBuffer reqUrl = req.getRequestURL();
System.out.println(reqUrl.toString());//将其转为字符串
//4 获取URI
String uri = req.getRequestURI();
System.out.println(uri);
//5 获取请求体---获取Post请求参数
//5.1 获取字符输入流
BufferedReader br = req.getReader();
//5.2 读取数据
String line = br.readLine();
System.out.println(line);
System.out.println("-------------------------------------");
//获取请求头
String header = req.getHeader("User-Agent");
System.out.println(header);
}
}

不同方式获取请求参数(详见Request获取请求数据)
Get方式获取请求参数:String getQueryString()
POST方式获取请求参数:ServletInputStream getInputStream()、BufferedReader getReader()
问题:Get方式获取请求参数用doGet方法;Post方式获取请求参数用doPost方法。在doGet方法和doPost方法中除了获取请求参数用到的方法不同外,其余请求行所用到的方法均相同(可详见请求行示例和请求体示例2代码),所以就会造成代码冗余的问题,为了解决该问题就提供了一种统一获取请求参数的方式。
该方式会先获取请求方式,然后根据请求方式的不同来调用不同的方法来获取请求参数。如下图所示

获取到请求参数后会将请求参数中的各个参数分割出来,然后转为键值对形式,并将其存储到Map集合中(由于一个键可能对应多个值,所以键值用String[](即字符串数组)表示,由于请求参数中键是字符串,所以Map ),之后即可使用相应方法来获取Map集合中的键值对,如下图所示

用到的方法
| 方法 | 解释 |
|---|---|
Map | 获取所有参数Map集合 |
String getParameterValues(String name) | 根据键的名称获取参数值(数组) |
String getParameter(String name) | 根据键的名称获取参数值(单个值) |
注意:
getParameter无法获取JSON数据,JSON数据获取方式详见Axios、JSON综合案例的添加部分
测试get请求 代码示例:
html文件代码
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<form action="/WebDemo/req3" method="get">
<input type="text" name="username">
<input type="password" name="password">
<input type="checkbox" name="hobby" value="1">游泳
<input type="checkbox" name="hobby" value="2">爬山<br>
<input type="submit">
form>
body>
html>
java代码
package at.guigu.webb;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
@WebServlet("/req3")
public class RequestThree extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//get请求逻辑
System.out.println("get...");
//1 获取所有参数的Map集合
Map<String, String[]> map = req.getParameterMap();
for (String key : map.keySet()) {
//返回键
System.out.print(key + ":");
//返回键值
String[] values = map.get(key);
for (String value : values) {
System.out.print(value + " ");
}
System.out.println();//换行
}
System.out.println("-----------------------------------");
//2 根据键的名称获取参数值数组
String[] values = req.getParameterValues("hobby");
for (String value : values) {
System.out.print(value + " ");
}
System.out.println();//换行
System.out.println("-----------------------------------");
//3 根据键的名称获取单个参数值
String value = req.getParameter("username");
System.out.println(value);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//post请求逻辑
System.out.println("post...");
}
}

测试post请求 代码示例
html文件代码
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<form action="/WebDemo/req3" method="post">
<input type="text" name="username">
<input type="password" name="password">
<input type="checkbox" name="hobby" value="1">游泳
<input type="checkbox" name="hobby" value="2">爬山<br>
<input type="submit">
form>
body>
html>
java代码
package at.guigu.webb;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
@WebServlet("/req3")
public class RequestThree extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//get请求逻辑
System.out.println("get...");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//post请求逻辑
System.out.println("post...");
//1 获取所有参数的Map集合
Map<String, String[]> map = req.getParameterMap();
for (String key : map.keySet()) {
//返回键
System.out.print(key + ":");
//返回键值
String[] values = map.get(key);
for (String value : values) {
System.out.print(value + " ");
}
System.out.println();//换行
}
System.out.println("-----------------------------------");
//2 根据键的名称获取参数值数组
String[] values = req.getParameterValues("hobby");
for (String value : values) {
System.out.print(value + " ");
}
System.out.println();//换行
System.out.println("-----------------------------------");
//3 根据键的名称获取单个参数值
String value = req.getParameter("username");
System.out.println(value);
}
}

此时由于get方式和post方式是利用通用方式来获取请求参数,屏蔽了Get和Post请求方式的代码不同,所以此时代码定义为如下格式
将相同代码写在doGet(或doPost)方法中,然后在doPost(或doGet)方法中调用即可

package at.guigu.webb;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
@WebServlet("/req3")
public class RequestThree extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1 获取所有参数的Map集合
Map<String, String[]> map = req.getParameterMap();
for (String key : map.keySet()) {
//返回键
System.out.print(key + ":");
//返回键值
String[] values = map.get(key);
for (String value : values) {
System.out.print(value + " ");
}
System.out.println();//换行
}
System.out.println("-----------------------------------");
//2 根据键的名称获取参数值数组
String[] values = req.getParameterValues("hobby");
for (String value : values) {
System.out.print(value + " ");
}
System.out.println();//换行
System.out.println("-----------------------------------");
//3 根据键的名称获取单个参数值
String value = req.getParameter("username");
System.out.println(value);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
步骤
Step1:右键包名New→Servlet

Step2:填写类名→OK即可

创建成功后模板如下:

以上模板中还需自己写入this.doGet方法,所以可对该模板进行更改,步骤如下:
File→Editor→File and Code Templates→Files中找到Servlet,然后在该模板中加上this.doGet(request,response)即可

此时利用IDEA模板创建的Servlet即是完整的,如图所示

注意:右键包名创建Servlet时,若没有Servlet选项,处理方法如下:
File→Editor→File and Code Templates→Other→Web→复制Servlet Annotated Class.java的代码

从Other切换到Files,剩下操作如图所示

此时右键New中既有Servlet,如图所示

Post请求参数中文乱码代码示例
html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<form action="/WebDemo/req5" method="post">
<input type="text" name="username">
<input type="password" name="password">
<input type="checkbox" name="hobby" value="1">游泳
<input type="checkbox" name="hobby" value="2">爬山
<br>
<input type="submit">
form>
body>
html>
java
package at.guigu.webb;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet("/req5")
public class RequestFive extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取请求参数
String username = request.getParameter("username");
System.out.println(username);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}

Post方式出现乱码原因
getReader()方法来获取流中的数据,而TOMCAT在获取流的时候采用的编码是ISO-8859-1,ISO-8859-1编码是不支持中文的,所以会出现乱码解决方式: 利用request中的方法 void setCharacterEncoding("编码格式")来指定编码,如下所示
void setCharacterEncoding("编码格式")可写在doPost方法中package at.guigu.webb;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet("/req5")
public class RequestFive extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//解决Post方式中文乱码
request.setCharacterEncoding("UTF-8");
//获取请求参数
String username = request.getParameter("username");
System.out.println(username);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}

Get请求参数中文乱码代码示例
html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<form action="/WebDemo/req5" method="get">
<input type="text" name="username">
<input type="password" name="password">
<input type="checkbox" name="hobby" value="1">游泳
<input type="checkbox" name="hobby" value="2">爬山
<br>
<input type="submit">
form>
body>
html>
java代码与Post请求参数中文乱码解决方案中java代码一样,运行截图如下

Get请求方式产生中文乱码的原因

浏览器通过Http协议发送请求和数据给服务端,浏览器在发送Http协议的过程中会对中文数据进行URL编码 (URL编码会采用html文件中
标签指定的UTF-8的方式进行编码),而后台服务器接收到请求参数后Tomcat会对其进行处理并按照ISO-8859-1进行URL解码 ,由于前后进行编码和解码的格式不同,所以产生乱码问题
问题:
GET请求的中文乱码问题的解决是否可以用Post请求的中文乱码问题解决方案
GET请求获取请求参数的方式是
request.getQueryString(),POST请求获取请求参数的方式是request.getReader(),request.setCharacterEncoding("utf-8")是设置request处理流的编码,getQueryString方法并没有通过流的方式获取数据,所以GET请求不能用设置编码的方式来解决中文乱码问题
若把html文件的标签的charset属性改成ISO-8859-1,后台不做操作,能解决中文乱码问题么?
不能,因为
ISO-8859-1本身是不支持中文展示的,所以改了标签的charset属性后,会导致页面上的中文内容都无法正常展示。
定义
%解释
(以张三为例):一个汉字有3个字节,共有两个汉字,所以一共有6个字节,每个字节用8个二进制位表示,所以一共有48个二进制位,如图所示

一个字节有8个二进制位(其中四个二进制位可组成一个16进制数,也就是说一个字节可以变成两个16进制数),将每个字节转为两个16进制数(两个为一体),然后在这两个十六进制数前面加上%,如图所示


注意
将请求参数张三传递到服务端后虽然产生了乱码,但是请求参数张三和乱码的底层本质是一样的(即字节是一样的,只是说用不同的编码格式进行了编码和解码,所以造成了乱码),所以我们可以利用字节来进行乱码的处理
步骤
Step1:将乱码转换为字节数据
用到String类中的byte[] getBytes(Charset charset)方法,作用:使用指定的编码格式将字符串编码为指定的字节序列并将结果存储在字节数组中
Step2:将字节数组转换为字符串
用到String类中的构造器String(byte[] bytes, Charset charset)将字节数组按照指定编码格式转换为字符串
代码示例
package at.guigu.webb;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet("/req5")
public class RequestFive extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取请求参数
String username = request.getParameter("username");
//解决Get方式中文乱码
//Step1:将请求参数转换为字节数据
byte[] bytes = username.getBytes("ISO-8859-1");
//Step2:将字节数组转换为指定编码格式的请求参数
username = new String(bytes, "UTF-8");
System.out.println(username);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//解决Post方式中文乱码
request.setCharacterEncoding("UTF-8");
this.doGet(request, response);
}
}

注意
Tomcat8.0之后已将Get请求方式的中文乱码问题解决,设置了默认的编码方式为UTF-8
Get中文乱码的解决方式对Post中文乱码同样适用,即代码变为如下
package at.guigu.webb;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet("/req5")
public class RequestFive extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取请求参数
String username = request.getParameter("username");
//解决Get方式中文乱码---也可解决Post方式中文乱码
//Step1:将请求参数转换为字节数据
byte[] bytes = username.getBytes("ISO-8859-1");
//Step2:将字节数组转换为指定编码格式的请求参数
username = new String(bytes, "UTF-8");
System.out.println(username);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}

转发(forward)定义
转发是一种在服务器内部的资源跳转方式
解释
假设现在服务端有资源A和资源B,现在浏览器请求服务端的资源A,资源A处理了浏览器的一部分请求之后会跳转到资源B,由资源B继续处理浏览器的请求,资源B处理完之后会向浏览器发出响应,其中资源A跳转到资源B我们称之为转发(forward)
转发用到的request方法
| 方法 | 解释 |
|---|---|
RequestDispatcher getRequestDispatcher(String path) | 将请求转发到另一个资源(如 Servlet、JSP、HTML 文件等)或包含另一个资源的内容。path:表示请求转发的目标路径,该路径可以是相对路径或绝对路径。 |
void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException | 将请求从一个 Servlet 转发到服务器上的另一个资源,而不通过客户端(浏览器) |
转发实现方式

利用request.getRequestDispatcher("资源B路径").forward(request, response);方法进行实现,且该方法要写在资源A中
代码示例
资源A
package at.guigu.webb;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
/**
* 请求转发 :由RequestSix转发到RequestSeven
*/
@WebServlet("/req6")
public class RequestSix extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("现在是RequestSix...");
//请求转发
request.getRequestDispatcher("/req7").forward(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
资源B
package at.guigu.webb;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet("/req7")
public class RequestSeven extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("已转发到Request7...");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}

注意
由于资源A处理了一部分请求后会跳转到资源B由资源B继续处理,所以资源A和B共享浏览器发送的请求参数的数据资源
请求转发资源间共享数据用到的方法
Request方法 | 解释 |
|---|---|
void setAttribute(String name,Object o) | 存储数据到request域中 |
Object getAttribute(String name) | 获取request域中对应属性的值(即根据key获取值) |
void removeAttribute(String name) | 删除request域中的数据(即根据key删除该键值对) |
注意
String name:要存储的属性名
Object o:要存储的属性值
示例
资源A
package at.guigu.webb;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
/**
* 请求转发 :由RequestSix转发到RequestSeven
*/
@WebServlet("/req6")
public class RequestSix extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("现在是RequestSix...并已进行数据存储");
//存储数据
request.setAttribute("message", "zhang");
//请求转发
request.getRequestDispatcher("/req7").forward(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
资源B
package at.guigu.webb;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet("/req7")
public class RequestSeven extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("已forward到Request7...且共享的数据为");
//获取数据
String message = (String) request.getAttribute("message");
System.out.println(message);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}

请求转发的特点
/req6转发到/req7,但是浏览器的地址一直是/req5,未发生变化/req3转发到`/req67,但是这个只有一次请求
response对象来将响应数据发送到客户端,因为response对象中存放已经设置好并要发送的响应数据,所以Tomcat在做出响应之前会先把response中的数据取出来并拼成字符串传给浏览器。Response是一个接口,它的实现类ResponseFacade是由Tomcat提供的void service(ServletRequest request, ServletResponse response)方法,它的两个参数其实就是Request 对象和Response 对象。
Response是也是三层继承体系
ServletResponse :Java提供的请求对象根接口HttpServletResponse :Java提供的对Http协议封装的请求对象ResponseFacade :由Tomcat定义的实现类注意
使用response对象时只需查阅JavaEE API文档的HttpServletResponse接口即可,因为它是其实现类
HttpServletResponse是HttpServlet实现类中所用到的接口,ServletResponse是Servlet接口中所用到的接口,Java不提供ServletResponse和HttpServletResponse 的实现类,他们的实现类ResponseFacade 由Tomcat提供
我们学习Servlet时,如果我们实现的是Servlet接口的话,那么service方法中的参数是ServletResponse接口,如图一所示;但是我们是通过继承HttpServlet来写Servlet代码的话则doGet和doPost方法使用的是HttpServletResponse接口,如图二所示。


响应数据分为三部分

key:value键值对形式...这部分内容就是响应体用到response对象的方法
| 响应行用到的方法 | 解释 |
|---|---|
void setStatus(int sc) | 设置响应状态码 |
| 响应头用到的方法 | 解释 |
void setHeader(String name,String value) | 设置响应头键值对 |
response.setHeader("content-type", "text/html") | 该示例是告诉浏览器带html标签的字符串是html语句,浏览器需按照html文件进行解析并显示在页面上,详见Response响应字符数据 |
| 响应体用到的方法 | 解释 |
PrintWriter getWriter() throws IOException | 获取字符输出流,可以向客户端(通常是浏览器)发送字符文本数据。 |
ServletOutputStream getOutputStream() | 获取字节输出流 |

重定向(Redirect)定义
一种资源跳转方式
解释1
假设现在服务端有资源A和资源B,现在浏览器请求服务端的资源A,但是服务端的资源A处理不了浏览器的请求,但是资源A知道资源B能够处理浏览器的请求,此时资源A会向浏览器发送响应数据来告诉浏览器处理不了,并告诉他能够处理它的请求的资源B的位置,然后浏览器会重新向服务端B发送请求参数,最终由资源B处理浏览器的请求,这个过程就称之为重定向(Redirect)
解释2
浏览器发送请求给服务器,服务器中对应的资源A接收到请求
资源A现在无法处理该请求,就会给浏览器响应一个302的状态码+location的一个访问资源B的路径
浏览器接收到响应状态码为302就会 自动重新发送请求 到location对应的访问地址去访问资源B
资源B接收到请求后进行处理并最终给浏览器响应结果,这整个过程就叫重定向
注意:302含义:提示所请求的资源已移动到由Location响应头给定的URL,浏览器会自动重新访问到这个页面
重定向用到的response方法
| 方法 | 解释 |
|---|---|
void setStatus(int sc) | 设置响应的状态码 |
void setHeader(String name, String value) | 设置响应头字段。name: 表示响应头名称;value: 表示响应头对应的值 |
重定向的实现方式

resp.setStatus(302);resp.setHeader("location","资源B的访问路径");代码示例
package at.guigu.webbb;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet("/resp1")
public class ResponseOne extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("respOne get...");
//重定向实现
//Step1:设置响应状态码
response.setStatus(302);
//Step2:设置响应头location以及对应的值
response.setHeader("location", "/resp2");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
package at.guigu.webbb;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet("/resp2")
public class ResponseTwo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("respTwo get...");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}

重定向提供了一种简化方式
用到的方法如下:
| 方法 | 解释 |
|---|---|
void sendRedirect(String location) throws IOException | 向客户端发送一个重定向响应,使客户端(通常是浏览器)向一个新的 URL 重新发起请求。 |
注意:
location:表示要重定向的目标 URL。这个 URL 可以是相对路径或绝对路径。 相对路径: 相对于当前请求的 URL。
绝对路径: 通常包括协议(如
http://或https://)和完整的域名。
此时资源A的代码可改为
package at.guigu.webbb;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet("/resp1")
public class ResponseOne extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("respOne get...");
//重定向实现方式一
response.sendRedirect("/WebDemo/resp2");
/*
//重定向实现方式二
//1 动态获取虚拟目录
String contextPath = request.getContextPath();
//2 拼凑路径字符串
response.sendRedirect(contextPath + "/selectAllServlet");
*/
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
重定向特点

在进行请求转发时加上了虚拟目录(如图一所示),而在进行重定向时并没有加虚拟目录(如图二所示),那么问题来了:什么时候加虚拟目录什么时候不加?


路径问题解决

虚拟目录配置
静态配置: 在Web项目的pom.xml文件中配置,在Tomcat插件的标签体内添加如下代码
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<version>2.2version>
<configuration>
<path>写入自己配置的路径path>
configuration>
plugin>
动态配置路径(推荐): 可降低耦合性
用到的request对象的方法String getContextPath() ,作用:动态获取虚拟目录(即项目访问路径),如下所示
package at.guigu.webbb;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet("/resp1")
public class ResponseOne extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("respOne get...");
//动态获取虚拟目录
String contextPath = request.getContextPath();
//重定向实现
//Step1:设置响应状态码
response.setStatus(302);
//Step2:设置响应头location以及对应的值
response.setHeader("location", contextPath + "/resp2");
/*
//重定向实现 简化版
response.sendRedirect(contextPath + "/resp2");
*/
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
用到的方法
| Response方法 | 解释 |
|---|---|
PrintWriter getWriter() throws IOException | 获取字符输出流,可以向客户端(通常是浏览器)发送字符文本数据。 |
void setContentType(String type) | 设置响应的内容类型,告知客户端如何处理响应数据。例如,设置为text/html表示响应数据是HTML格式,浏览器会将其作为网页内容进行渲染。 |
void setHeader(String name,String value) | 设置响应头键值对 |
| PrintWriter方法 | 解释 |
void write(int c) | 写入单个字符 |
void write(char[] buf) | 写入字符数组 |
void write(char[] buf, int off, int len) | 写入字符数组的一部分 |
void write(String s) | 写入字符串 |
void write(String s, int off, int len) | 写入字符串的一部分 |
注意:
void setContentType(String type)方法可在指定内容类型后并指定编码格式,中间用;隔开,详见代码示例3
响应的内容类型如下
| 内容类型 | 解释 |
|---|---|
text/html | HTML格式,浏览器会将其作为网页进行渲染。 |
text/plain | 纯文本格式,浏览器会将其作为普通文本显示。默认情况下为该纯文本格式 |
application/json | SON格式,用于发送JSON数据,客户端(例如Ajax请求)可以解析为JSON对象 |
application/xml | XML格式,用于发送XML数据 |
image/png | PNG图片格式,用于发送图片数据 |
text/css | CSS格式,用于发送样式表 |
text/javascript | JavaScript格式,用于发送脚本 |
步骤
PrintWriter getWriter()获取字符输出流PrintWriter对象的writer方法写入字符数据代码示例1
package at.guigu.webbb;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 设置字符响应数据:设置字符数据的响应体
*/
@WebServlet("/resp3")
public class ResponseThree extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.write("this is output string!!!");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}

代码示例2:除了单纯响应字符数据外,还可以写入html数据,但是要进行特殊处理才能被浏览器当作html解析并显示在页面上
package at.guigu.webbb;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 设置字符响应数据:设置字符数据的响应体
*/
@WebServlet("/resp3")
public class ResponseThree extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.write("this is output string!!!");
response.setHeader("content-type", "text/html");
out.println("this is title
");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}

代码示例3:当我们输出中文数据时会产生乱码问题 ,因为response对象所调用出来的流会自动使用ISO-8859-1进行编码,所以此时我们可以调用response对象的void setContentType()方法来更改编码格式,代码如下所示
response.setHeader()方法的参数为"content-type", "text/html"时可代替它void setContentType()好处
content-type响应头的信息package at.guigu.webbb;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 设置响应数据
*/
@WebServlet("/resp3")
public class ResponseThree extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*告诉浏览器带html标签的字符串需要按照html文件进行解析
并按照utf-8的编码格式对中文字符进行编码输出使其能
在页面上正常显示
*/
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.write("this is output string!!!");
out.println("this is title
");
out.println("这就是更改编码后可正常输出的中文
");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}

注意
PrintWriter输出流,因为它是response对象调用出来的,当响应结束后,response对象会被自动销毁,从而使输出流被自动释放。(也就是说该流会被服务器关闭)用到的方法
| 方法 | 解释 |
|---|---|
ServletOutputStream getOutputStream() | 获取字节输出流 |
void setContentType(String type) | 设置响应的内容类型,告知客户端如何处理响应数据。例如,设置为text/html表示响应数据是HTML格式,浏览器会将其作为网页内容进行渲染。 |
void setHeader(String name,String value) | 设置响应头键值对 |
| ServletOutputStream方法 | 解释 |
write(int b) | 写入单个字节 |
write(byte[] b) | 写入字节数组 |
write(byte[] b, int off, int len) | 写入字节数组的一部分 |
| 内容类型 | 解释 |
|---|---|
text/html | HTML格式,浏览器会将其作为网页进行渲染。 |
text/plain | 纯文本格式,浏览器会将其作为普通文本显示。默认情况下为该纯文本格式 |
application/json | SON格式,用于发送JSON数据,客户端(例如Ajax请求)可以解析为JSON对象 |
application/xml | XML格式,用于发送XML数据 |
image/png | PNG图片格式,用于发送图片数据 |
text/css | CSS格式,用于发送样式表 |
text/javascript | JavaScript格式,用于发送脚本 |
步骤
Response对象的ServletOutputStream getOutputStream()获取字节输出流ServletOutputStream对象的writer方法写入字节数据代码示例1
package at.guigu.webbb;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet("/resp4")
public class ResponseFour extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletOutputStream out = response.getOutputStream();
out.write(98);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}

代码示例2
package at.guigu.webbb;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
@WebServlet("/resp4")
public class ResponseFour extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("image/png");
//Step1:将服务端的图片、视频、音频等文件读取到内存中
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:/大数据/大数据.png"));
//Step2:获取字节输出流
ServletOutputStream out = response.getOutputStream();
//Step3:写入响应数据
byte[] bytes = new byte[1024];
int len;
while ((len = bis.read(bytes)) != -1) {
out.write(bytes, 0, len);
}
out.flush();
//释放资源
bis.close();
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}

在向客户端传输响应数据时,可以不使用javase基础day16所讲的内容,因为我们可以在Web项目的pom.xml文件中的标签体内导入一个commons-io依赖,如下所示。它是apache提供的一种IO工具类可简化输入输出流
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>2.13.0version>
dependency>
简化后的代码如下所示


package at.guigu.webbb;
import org.apache.commons.io.IOUtils;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
@WebServlet("/resp4")
public class ResponseFour extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("image/png");
//Step1:将服务端的图片、视频、音频等文件读取到内存中
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:/大数据/大数据.png"));
//Step2:获取字节输出流
ServletOutputStream out = response.getOutputStream();
/*
//Step3:写入响应数据
byte[] bytes = new byte[1024];
int len;
while ((len = bis.read(bytes)) != -1) {
out.write(bytes, 0, len);
}
out.flush();
*/
//Step3:写入响应数据简化版
IOUtils.copy(bis, out);//将图片、视频、音频等文件直接写入输出流
//释放资源
bis.close();
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
