1. Servlet
2. HTTP协议
3. Request
1、Servlet补充
我们前面已经对Servlet做了初步介绍,主要讲了下面5点
1. 概念
2. 步骤
3. 执行原理
4. 生命周期
5. Servlet3.0 注解配置
下面我们继续对Servlet进行讲解
Servlet -- 接口
|
GenericServlet -- 抽象类
|
HttpServlet -- 抽象类
* GenericServlet:将Servlet接口中其他的方法做了默认空实现,只将service()方法作为抽象
* 将来定义Servlet类时,可以继承GenericServlet,实现service()方法即可。当然,如果有其他需求,就复写其他方法。但是真正开发过程中,我们会使用HTTPServlet
我们发现导入的源代码文件web目录下没有WEB-INF文件夹,说明之前在创建时候没有把Web Application下的Create web.xml勾选上,我们参考下面这篇文章,为项目添加WEB-INF文件夹。添加WEB-INF
我们创建一个新的项目,并将端口改为80,这样我们访问的时候不需要输入端口;再设置启动Tomcat不自动访问,同时将项目的虚拟目录设置为项目名(其实勾选让其自动访问比较方便)。启动Tomcat,并访问:http://localhost/TomcatAndServletTest1/demo1(记得将项目虚拟目录名写上),成功访问。
大多数时候我们只会使用Servlet里面的service方法,其他方法不会使用到,每次都要复写所有方法比较麻烦。可以继承Servlet的子抽象类GenericServlet。
代码示例
package lkjTest.servlet;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet("/demo1")
public class ServletTest1 extends GenericServlet
{
//对于继承GenericServlet的类,只需要重写service方法即可
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException
{
System.out.println("service connect....");
}
}
* HttpServlet:对http协议的一种封装,简化操作
为什么要有这个类?(视频2-9.30):HTTPServlet里面,service方法已经对浏览器请求中的请求方式作出判断,而我们的Servlet如果继承HTTPServlet,就只需要复写HTTPServlet里面的doGet()与doPost()方法即可。而Tomcat在接收到客户端的请求后,会先执行HTTPServlet的service方法,HTTPServlet的service方法会根据其判断的请求的方式是get还是post,自动调用我们在自己的类中定义的doGet()与doPost()方法。
1. 定义类继承HttpServlet
2. 复写doGet/doPost方法
当浏览器访问的Servlet相关类的时候,会调用HTTPServlet实现的service方法,从而判断请求方式,然后service方法根据请求方式,调用我们复写的doGet/doPost方法
代码示例
package lkjTest.servlet;
import com.sun.net.httpserver.HttpServer;
import javax.servlet.*;
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("/demo1")
public class ServletTest1 extends HttpServlet
{
//必须复写doGet与doPost方法
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
System.out.println("doGet.....");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
System.out.println("doPost.....");
}
}
/*
访问:http://localhost/TomcatAndServletTest1/demo1
打印doGet.....
*/
我们通过浏览器直接访问HTTPServlet的实现类,是使用get()方式访问。
我们在web文件夹下创建一个表单,通过表单使用doPost方法访问
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<form action="/demo1" method="POST">
<input name="username">
<input type="submit" value="提交">
form>
<hr>
body>
html>
urlpartten:Servlet访问路径。在WebServlet注解里面,urlPatterns定义的是一个数组,意味着我们可以为一个Servlet定义多个访问路径,既浏览器可以通过多个路径访问这个类。
一个Servlet可以定义多个访问路径 如: @WebServlet({"/d4","/dd4","/ddd4"})
当然,一般情况下只定义一个。
路径定义规则,既怎么去写urlparttern。
1. /xxx:路径匹配
2. /xxx/xxx:多层路径,目录结构
3. *.do:扩展名匹配
相应代码如下
package lkjTest.servlet;
import com.sun.net.httpserver.HttpServer;
import javax.servlet.*;
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({"/d1" , "/d2" , "/d3"}) //定义多个路径,必须通过{}括起来
//发现通过:http://localhost/d1 、 http://localhost/d2 、 http://localhost/d3 三个路径都可以访问ServletTest1类
//多层路径。启动服务器访问,需要把2层路径都写上:http://localhost/user/demo
//@WebServlet("/user/demo")
//@WebServlet("/user/*") //第二层可以写为通配符,那么我们可以随便写第二层路径,如http://localhost/user/haah,都可以访问到
//@WebServlet("/*") //写什么都可以访问到。
//这个时候,如果我们在其他Servlet类中设置类的资源目录为demo,那么这个时候我们访问demo,会先访问其他Servlet类,最后找不到才访问通配符(通配符优先级低)
@WebServlet("*.do") //写任意路径.do即可访问,如:http://localhost/demo.do
public class ServletTest1 extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
System.out.println("doGet.....");
}
}
2、HTTP
HTTP相关的概念如下:
* 概念:Hyper Text Transfer Protocol 超文本传输协议
* 传输协议:定义了,客户端和服务器端通信时,发送数据的格式。如下图,超文本传输协议规定了请求消息与响应消息的组成格式。
* HTTP百度百科:http是一个简单的请求-响应协议,它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。
* 特点:
1. 基于TCP/IP的高级协议
2. 默认端口号:80。比如我们要访问某个域名:http://www.haha.com:80,必须指定要访问的端口号,如果我们把Tomcat服务器端口设置为80,那么访问的时候域名也可以不写80端口。(Tomcat与客户端基于HTTP协议,HTTP的默认端口是80)
3. 基于请求/响应模型的:一次请求对应一次响应
4. 无状态的:每次请求之间相互独立,不能交互数据
* 历史版本:
* 1.0:每一次请求响应都会建立新的连接
* 1.1:复用连接
每次我们访问一个页面,可能有多次请求,每一张图片,每一个JS或者CSS模块,都是一个请求(F12-newwork-刷新,可以查看这个页面相应向服务器发送的请求)
OSI七层模型1-7(低到高) 1.物理层 2.数据链路层:ppp点对点协议 CSMA/CD 3.网络层:IP协议,地址解析协议ARP,网际控制报文协议ICMP,路由信息协议,RIP 4.运输层:用户数据报协议UDP,传输控制协议TCP 5.会话层 6表示层 7.应用层:文件传输协议FTP,HTTP,SMTP,
HTTP请求消息的格式:包括4个部分:请求行,请求消息头、请求空行、请求体。
1. 请求行
请求方式 请求url 请求协议/版本
个:以get方式请求login.html信息:GET /login.html HTTP/1.1
* 请求方式:
* HTTP协议有7中请求方式,常用的有2种(get、post、put、delete、head、trace、opinions)
* GET:
1. 请求参数在请求行中,在url后。
2. 请求的url长度有限制的
3. 不太安全
eg:使用GET提交,我们将表单提交后,地址栏信息为:http://localhost/demo?username=zhangsan ,既请求参数会放在请求行中(注意,这里不是访问login.html的请求,而是通过login.html访问/demo类的请求。虚拟目录设置为"/")
* POST:
1. 请求参数在请求体中
2. 请求的url长度没有限制的
3. 相对安全
eg:使用POST提交,我们表单提交后,地址栏信息为:http://localhost/demo ,既请求参数会放在请求体中(这里同样是通过login.html访问/demo类的请求。虚拟目录设置为"/")
下面我们以get提交方式说明表单action的路径应该怎么填写的问题。
说明:我们都是在login.html中访问requestDemo2类。
(为什么即使2个资源在项目内部,有虚拟目录要加虚拟目录?因为项目内资源的跳转是通过重定向的方式,第二次访问的请求也是从浏览器发出的,必须加上项目的虚拟目录才能访问到项目!!!)
1) 在IDEA中将虚拟目录设置为“/”,将表单的action设置为:/requestDemo2
访问login.html的路径是:http://localhost:8080/login.html,提交login.html表单后访问/requestDemo2类,地址栏信息为:http://localhost:8080/requestDemo2?username=zhangsan
2) 在IDEA中将虚拟目录设置为“/ResponsePro”,将表单的action设置为:/ResponsePro/requestDemo2
访问login.html的路径是:http://localhost:8080/ResponsePro/login.html。提交login.html后地址栏信息为:http://localhost:8080/ResponsePro/requestDemo2?username=zhangsan
3) 在IDEA中将虚拟目录设置为“/”,将表单的action设置为:/ResponsePro/requestDemo2
访问login.html的路径是:http://localhost:8080/login.html,提交login.html表单后出现响应状态码404(服务器没有这个资源存在),地址栏信息为:http://localhost:8080/ResponsePro/requestDemo2?username=ssss (因为 /ResponsePro 相当于项目的虚拟目录,而此时虚拟目录是“/”,因此访问不到)
4) 在IDEA中将虚拟目录设置为“/ResponsePro”,将表单的action设置为:/requestDemo2
访问login.html的路径是:http://localhost:8080/ResponsePro/login.html,提交login.html表单后发现找不到localhost网页,地址栏信息为:http://localhost:8080/requestDemo2?username=zhangsan (如果项目有设置虚拟目录,表单提交需要加上虚拟目录)
5) 在IDEA中将虚拟目录设置为“/ResponsePro”,将表单的action设置为:action="http://localhost:8080/ResponsePro/requestDemo2"
访问login.html的路径是:http://localhost:8080/ResponsePro/login.html,提交login.html表单后地址栏信息为:http://localhost:8080/ResponsePro/requestDemo2?username=zhangsan
6) 在IDEA中将虚拟目录设置为“/”,将表单的action设置为:action="http://localhost:8080/ResponsePro/requestDemo2"
访问login.html的路径是:http://localhost:8080/login.html,提交login.html表单后出现响应状态码404,地址栏信息为:http://localhost:8080/ResponsePro/requestDemo2?username=zhangsan (同样是虚拟目录不对)
总结:通过上面的分析,我们发现,当我们将虚拟目录设置为“/”的时候,表单的action直接设置为要访问的类的资源路径即可:/requestDemo2,如果在action加上虚拟目录或者ip地址都会出错。当我们将虚拟目录设置为“/ResponsePro”的时候,表单的action需要设置为:/ResponsePro/requestDemo2 或者:http://localhost:8080/ResponsePro/requestDemo2 ,既action需要加上资源路径或者资源路径与ip地址都加上。
总而言之,action访问某一个类资源的时候,我们需要根据虚拟目录的设置来设置action。虚拟目录设置为“/”,action不需要加上虚拟目录;虚拟目录设置为“/虚拟目录”,action需要加上虚拟目录(ip地址也可以加上)
2. 请求头:客户端浏览器告诉服务器的一些信息
请求头名称: 请求头值(值如果有多个,用,分隔)
* 常见的请求头:
1. User-Agent:浏览器告诉服务器,我访问你使用的浏览器版本信息
* 可以在服务器端获取该头的信息,解决浏览器的兼容性问题(兼容性问题见视频06-2.00)
2. Referer:http://localhost/login.html
* 告诉服务器,我(当前请求)从哪里来?
* 作用:(这两部分解析见视频06-7.40)
1. 防盗链:
2. 统计工作:
3. 请求空行
空行,就是用于分割POST请求的请求头,和请求体的。
4. 请求体(正文):POST方式才有请求体
* 封装POST请求消息的请求参数的
测试:这里要特别注意我们说的是在login.html里面提交表单访问/requestTest2类的请求,而不是直接访问login.html的请求。(视频里面其实说错了)
我们访问之前创建的html文件:http://localhost/login.html ,同时提交表单,会访问表单指定的路径:action="/requestTest2"。
对于Chrome浏览器,F12-Network-刷新,找到login.html,点击Header,里面有Request Headers(请求头),但是高级的浏览器将一些消息隐藏了,我们使用火狐浏览器,F12-网络-刷新,找到login.html,点击,打开消息头,再点击原始头,就可以看到请求头的原始信息。
此时地址栏的信息为:http://localhost:8080/requestTest2?username=dsd ,说明get方式的请求参数在请求行中。
Get方式提交login.html访问action="/requestTest2"的请求消息的字符串格式:(注意这里访问的不是login.html,而是在login.html提交,访问/requestTest2类)
GET /requestTest2 HTTP/1.1(请求行)
Host: localhost(请求消息头)
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0 (告知服务器浏览器的版本信息)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/ *;q=0.8 (告知服务器浏览器可以接收服务器返回的信息的类型)
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 (浏览器支持的语言类型)
Accept-Encoding: gzip, deflate (浏览器支持的压缩格式)
Connection: keep-alive (设置连接的状态,keep-alive表示连接可以复用)
Upgrade-Insecure-Requests: 1 (升级相关信息)
If-Modified-Since: Sun, 02 Feb 2020 14:32:33 GMT
If-None-Match: W/"1162-1580653953829"
Cache-Control: max-age=0
请求空行(就是一行空行)
请求体(Get方式没有请求体,因为他的请求数据封装到请求行中)
我们将login.html的提交方式修改为POST,然后再次使用火狐访问login.html,提交数据,在网络-demo(点击)-参数 处,可以看到表单数据“username=zhangsan”,这就是POST方式的请求体,也就是我们请求的数据(参数)。
POST方式提交login.html访问action="/requestTest2"的请求消息的字符串格式:
POST /requestTest2 HTTP/1.1(请求行)
Host: localhost (请求消息头)
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/ *;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 17
Origin: http://localhost
Connection: keep-alive
Referer: http://localhost/login.html
Upgrade-Insecure-Requests: 1
(请求空行)
username=zhangsan (请求体)
3、Request
request对象和response对象的原理(视频07-1.30),参考下图
1. request和response对象是由服务器(Tomcat)创建的。我们来使用它们(在service方法中使用这两个方法)
2. request对象是来获取请求消息(服务器将客户端的请求消息封装到Request对象中,程序员从Request对象中取出响应消息使用),response对象是来设置响应消息(程序员给Response对象设置响应消息,服务器会Response对象中取出响应消息返回给客户端)
request对象继承体系结构
ServletRequest -- 接口
| 继承
HttpServletRequest -- 接口
| 实现
org.apache.catalina.connector.RequestFacade 类(tomcat实现了HttpServletRequest接口,编写这个RequestFacade 类。Tomcat通过这个RequestFacade 类来创建Request对象,并将Request对象传递给service方法使用。我们资料里面提供了“apache-tomcat-8.5.31-src”,既Tomcat的源码,可以看到RequestFacade 类)
我们打印doGet方法中的HTTPServlet对象,得到org.apache.catalina.connector.RequestFacade@dc1480a。
1. 获取请求消息数据
1)获取请求行数据
* 比如获取一个GET请求行:GET /day14/demo1?name=zhangsan HTTP/1.1
* 方法:
1. 获取请求方式 :GET
* String getMethod()
2. (*)获取虚拟目录(项目目录):/day14
* String getContextPath()
3. 获取Servlet路径(客户端访问的实现Servlet接口的类的映射名,通过web.xml或者@WebServlet注解设置): /demo1
* String getServletPath()
4. 获取get方式请求参数(将所有请求参数一次性获取出来):name=zhangsan
* String getQueryString()
5. (*)获取请求URI:/day14/demo1
* String getRequestURI(): /day14/demo1
* StringBuffer getRequestURL() :http://localhost/day14/demo1
URI与URL区别(视频10-7.50)
* URL:统一资源定位符 : http://localhost/day14/demo1 中华人民共和国
* URI:统一资源标识符 : /day14/demo1 共和国
6. 获取协议及版本:HTTP/1.1
* String getProtocol()
7. 获取客户机的IP地址:
* String getRemoteAddr()
2) 获取请求头数据
* 方法:
* (*)String getHeader(String name):通过请求头的名称获取请求头的值
* Enumeration<String> getHeaderNames():获取所有的请求头名称 (这里的Enumeration与之前说的枚举不太一样,我们可以将其理解为迭代器,见视频11-1.50解析)
3) 获取请求体数据:
* 请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数
* 步骤:
1. 获取流对象
* BufferedReader getReader():获取字符输入流,只能操作字符数据
* ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据(ServletInputStream 继承InputStream,可以当做InputStream使用)
* 在文件上传知识点后讲解
2. 再从流对象中拿数据
URI、URL、URN区别:添加链接描述
获取请求消息请求行的代码演示
//启动Tomcat,并访问:http://localhost/TomcatAndServletTest1/requestTest2?name=zhangsan&age=12 这里加上请求参数
/*
获取的结果如下:
GET
/TomcatAndServletTest1
/requestTest2
name=zhangsan&age=12
/TomcatAndServletTest1//requestTest2
http://localhost/TomcatAndServletTest1//requestTest2
HTTP/1.1
0:0:0:0:0:0:0:1 (自己访问自己,返回IPV6的地址。别人访问就可以获取别人的地址)
*/
//这几个方法的用处:视频10-6.10
package Request;
//我们可以直接new一个Servlet,而不用new一个java类,这样可以帮我们列出很多信息
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("/requestTest2")
public class RequestTest2 extends HttpServlet
{
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
//1. 获取请求方式 :GET
String method = request.getMethod();
System.out.println(method);
//2.(*)获取虚拟目录(项目目录)
String contextPath = request.getContextPath();
System.out.println(contextPath);
//3.获取Servlet路径(客户端访问的实现Servlet接口的类的映射名,通过web.xml或者@WebServlet注解设置)
String servletPath = request.getServletPath();
System.out.println(servletPath);
//4.获取get方式请求参数
String queryString = request.getQueryString();
System.out.println(queryString);
//5.(*)获取请求URI与URL
String requestURI = request.getRequestURI();
System.out.println(requestURI);
StringBuffer requestURL = request.getRequestURL();//注意URL在这里是StringBuffer类型
System.out.println(requestURL);
//6.获取协议及版本
String protocol = request.getProtocol();
System.out.println(protocol);
//7.获取客户机的IP地址
String remoteAddr = request.getRemoteAddr();
System.out.println(remoteAddr);
}
}
获取请求消息请求头的代码演示1
package Request;
//我们可以直接new一个Servlet,而不用new一个java类,这样可以帮我们列出很多信息
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.Enumeration;
@WebServlet("/requestTest2")
public class RequestTest2 extends HttpServlet
{
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
//演示获取请求头数据
//1.获取所有请求头名称
Enumeration<String> headerNames = request.getHeaderNames();
//2.遍历所有的请求头名称,并获取相应的值
while(headerNames.hasMoreElements())
{
String name = headerNames.nextElement();
String value = request.getHeader(name);
System.out.println(name+"..."+value);
}
}
}
/*
结果:
host...localhost
connection...keep-alive
upgrade-insecure-requests...1
user-agent...Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
accept...text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,**;q=0.8,application/signed-exchange;v=b3;q=0.9
sec-fetch-site...none
sec-fetch-mode...navigate
accept-encoding...gzip, deflate, br
accept-language...zh-CN,zh;q=0.9,zh-TW;q=0.8
cookie...JSESSIONID=951BF612EBE299A9976144668E51949E; Idea-dd3bbdc1=b3e3b18d-e0e0-4d55-9cda-35c4f1eb7dfe; JSESSIONID=E639928A837EC7ECAD44EA73F3FB2BAF
*/
获取请求消息请求头的代码演示2(识别浏览器版本)
package Request;
//我们可以直接new一个Servlet,而不用new一个java类,这样可以帮我们列出很多信息
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.Enumeration;
@WebServlet("/requestTest2")
public class RequestTest2 extends HttpServlet
{
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
String agent = request.getHeader("user-agent");//请求头不区分大小写
//根据user-agent来确认浏览器的版本,从而进行不同的操作,解决浏览器的兼容问题
if(agent.contains("Chrome"))
{
System.out.println("使用谷歌浏览器");
}
else if(agent.contains("Firefox"))
{
System.out.println("使用火狐浏览器");
}
}
}
获取请求消息请求头的代码演示3(防盗链)
package Request;
//我们可以直接new一个Servlet,而不用new一个java类,这样可以帮我们列出很多信息
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("/requestTest2")
public class RequestTest2 extends HttpServlet
{
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
//演示获取请求头数据:referer
String referer = request.getHeader("referer");//http://localhost/TomcatAndServletTest1/login.html
System.out.println(referer);
/*
我们在浏览器地址输入访问:http://localhost/TomcatAndServletTest1/requestTest2
注意,如果我们没有从别的超链接点击来访问这个“requestTest2 ”代表的类,而referer是告诉服务器这个请求从哪里来(比如从某一个页面的超链接过来),
而是直接访问的服务器,因此这个时候referer为null。既通过浏览器输入url直接访问不算referer
如果想要获取referer,想要通过一个路径或者超链接点击过来,我们在login.html里面定义一条超链接
referer访问...
通过访问login.html,点击这个超链接来访问requestTest2,这个时候就会有referer
http://localhost/TomcatAndServletTest1/login.html(发现谷歌没办法显示超链接,但是火狐可以)
referer表示referer是从login.html跳转过来的
*/
//防盗链
if(referer != null)
{
if(referer.contains("/TomcatAndServletTest1"))//包含项目名,说明是从正规网址跳转过来的
{
// System.out.println("播放电影....");
//我们把提示信息写到页面上,通过Response方法
response.setContentType("text/html;charset=utf-8");//设置页面可以写中文,不设置会显示乱码
response.getWriter().write("播放电影");
}
else
{
// System.out.println("去官方主页播放");
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("去官方主页播放");
//这样设置后提示信息会显示在浏览器页面
}
}
/*下面的演示从视频11-12.38开始,这部分演示比较复杂,看视频,并参考下面的截图
如果我们通过http://localhost/TomcatAndServletTest1/login.html访问,就会正常播放
为了演示通过其他路径访问,我们再创建一个hehe项目,并在里面创建一个index.html,里面添加一个超链接
盗链链接 我们通过其他项目访问“requestTest2”代表的当前类RequestTest2
其他项目虚拟目录不包含“/TomcatAndServletTest1”,便被视为盗链
关键!!!:如果我们想启动2个项目(hehe与TomcatAndServletTest1),当前的Tomcat Server部属了hehe项目,这是盗链的项目(Deployment可以看到)
在Run-Edit Configuration-Tomcat Server,点击左上角+,再添加一个Local的Tomcat Server,
并在Deployment中为这个新的Tomcat Server部属TomcatAndServletTest1项目(点击+号,添加Artifacts),并加项目的虚拟目录改为“/TomcatAndServletTest1”
并将新Tomcat Server的HTTP port改为80,JMX Port改为1088,Tomcat Server名称为Tomcat8(这是真实的网址)
hehe项目的HTTP port为8080,JMX Port为1099,这样2个Tomcat服务器的端口就不会冲突,Tomcat Server名称为Tomcat8.5.31(这是盗链的网址)
我们分别启动2个Tomcat Server(右上角下拉选择)。发现右下角有Tomcat8.5.31与Tomcat8两个界面
先访问Tomcat8(真实)下的login.html,:http://localhost/TomcatAndServletTest1/login.html ,点击“referer访问”超链接
Tomcat8的Output界面显示:
http://localhost/TomcatAndServletTest1/login.html
播放电影....
再访问Tomcat8.5.31(盗链):http://localhost:8080/index.html ,显示“盗链链接”字样的超链接,点击链接,从其他链接访问当前类,显示
http://localhost:8080/index.html
去官方主页播放
*/
}
}
获取请求消息请求体的代码演示
//被访问的继承HTTPServlet的类
package Request;
//我们可以直接new一个Servlet,而不用new一个java类,这样可以帮我们列出很多信息
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;
/**
* 演示Request对象获取请求行数据
* 我们这里继承HTTPServlet类,这样我们只需要复写doGet与doPost方法
* 这两个方法通过Request对象就可以调用获取请求消息的方法
*/
@WebServlet("/requestTest2")
public class RequestTest2 extends HttpServlet
{
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
//获取请求消息体--请求参数(只能在Post提交方式中演示)
//1.获取字符流
BufferedReader reader = request.getReader();//获得的是一个缓冲区读取流
String line = null;
while ((line = reader.readLine()) != null)
{
System.out.println(line);
}
/*
测试:我们将Tomcat8这个Tomcat Server的虚拟目录修改为“TomcatAndServletTest1”,方便后面表单提交。
创建一个新的表单regist,启动Tomcat8,访问:http://localhost/TomcatAndServletTest1/regist.html 输入相应内容并注册,发现控制台显示请求体的内容:
username=zhangsan&password=12345 参数格式与GET方式一样,只不过放的地方不一样
*/
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{ }
}
相应的regist.html表单
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面title>
head>
<body>
<form action="/TomcatAndServletTest1/requestTest2" method="post">
<input type="text" placeholder="请输入用户名" name="username"><br>
<input type="text" placeholder="请输入密码" name="password"><br>
<input type="submit" value="注册">
form>
body>
html>
其他功能如下
1. 获取请求参数通用方式:不论get还是post请求方式都可以使用下列方法来获取请求参数(前面,GET方式获取请求方式,要操作请求行,使用getQueryString;而POST方式要操作请求体,获取读取流getReader)
(注意,下面所指的参数名称,通过表单的name属性来设置)
1) String getParameter(String name):根据参数名称获取参数值 username=zs&password=123
2) String[] getParameterValues(String name):根据参数名称获取参数值的数组(一个参数有多个值,多用于复选框) hobby=xx&hobby=game
3) Enumeration<String> getParameterNames():获取所有请求的参数名称
4) Map<String,String[]> getParameterMap():获取所有参数的map集合
* 中文乱码问题:(提交中文的内容)(反正以后想获取请求参数,就在前面设置编码方式)
* get方式:tomcat 8 已经将get方式乱码问题解决了
* post方式:输入中文会乱码
* 解决:在获取参数前,设置request的编码request.setCharacterEncoding("utf-8");//我们提交的页面用什么编码,这里就将流的编码方式设置为什么编码
代码测试,测试之前我们再创建一个regist2.html表格。为了方便修改,我们将当前项目的2个按钮“On Update Action”和另一个的值都修改为“update resource”(改为“update resource and classes”会更新资源与类,该代码也不需要重启服务器。其实是需要重启的,只有在Debug下才会生效)。我们测试的时候修改regist2.html的提交方式即可(修改后记得Ctrl+s保存,右键查看源码,即可知道是否保存成功),访问:http://localhost/TomcatAndServletTest1/regist2.html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面title>
head>
<body>
<form action="/TomcatAndServletTest1/requestTest2" method="post">
<input type="text" placeholder="请输入用户名" name="username"><br>
<input type="text" placeholder="请输入密码" name="password"><br>
<input type="checkbox" name="hobby" value="game">游戏<br>
<input type="checkbox" name="hobby" value="study">学习<br>
<input type="submit" value="注册">
form>
body>
html>
相应的java代码如下
package Request;
//我们可以直接new一个Servlet,而不用new一个java类,这样可以帮我们列出很多信息
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;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* 演示Request对象获取请求行数据
* 我们这里继承HTTPServlet类,这样我们只需要复写doGet与doPost方法
* 这两个方法通过Request对象就可以调用获取请求消息的方法
*/
@WebServlet("/requestTest2")
public class RequestTest2 extends HttpServlet
{
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
//post 获取请求参数
//根据参数名称获取参数值
// String username = request.getParameter("username");
// System.out.println("POST");
// System.out.println(username);
//根据参数名称获取参数值的数组
// String[] hobbies = request.getParameterValues("hobby");
// for (String hobby : hobbies)
// {
// System.out.println(hobby);
// }
//获取所有请求的参数名称
// Enumeration pns = request.getParameterNames();
// while(pns.hasMoreElements())
// {
// String name = pns.nextElement();
// System.out.println(name);
// String value = request.getParameter(name);
// System.out.println(value);
// System.out.println("----------------");
// }
//这种方式对于有多个值的参数,没办法取全
Map<String, String[]> pm = request.getParameterMap();
//使用keySet方法取出Map的键,得到一个Set集合,然后就可以对Set进行遍历
Set<String> nameSet = pm.keySet();
/*迭代器
Iterator iterator = nameSet.iterator();//这里也可以使用foreach对nameSet进行遍历
while(iterator.hasNext())
{
String name = iterator.next();//将Map的键一个一个取出
String[] values = pm.get(name);//通过Map集合的get方法。获取Map的值。当然这里也可以通过getParameterValues方法获取请求参数的值,一样得到数组
System.out.println(name);
for (String value : values)
{
System.out.println(value);
}
System.out.println("-----------------");
}
//通过这种方法,可以将有多个值的参数全部显示
*/
//foreach快捷键:iter+回车 或者是 遍历数组名.for+回车
for (String name : nameSet)
{
String[] values = pm.get(name);
System.out.println(name);
for (String value : values)
{
System.out.println(value);
}
System.out.println("-----------------");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
//get 获取请求参数
//根据参数名称获取参数值
// String username = request.getParameter("username");
// System.out.println("GET");
// System.out.println(username);
//为了方便,doGet里面可以调用doPost方法,从而实现代码省略。因为所有获取请求参数的方法对GET与POST通用
this.doPost(request , response);//调用本类的doPost方法,将Request与Response对象存入
}
}
2. 请求转发:一种在服务器内部的资源跳转方式(请求转发原理:视频16)
1. 步骤:
1. 通过request对象获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path) :path为要转发的路径
2. 使用RequestDispatcher对象来进行转发:forward(ServletRequest request, ServletResponse response) :将当前Servlet的Request对象与Response对象传递给要转发的Servlet
2. 特点:(重要,面试要问)
1. 浏览器地址栏路径不发生变化
2. 只能转发到当前服务器内部资源中。
3. 转发是一次请求,既多个被访问的Servlet资源之间只有一次请求。(F12-network-刷新,可以看到请求数量)
3. 共享数据:请求转发过程中可能有一些数据需要转发到下一个Servlet资源中。
* 域对象:一个有作用范围的对象,可以在范围内共享数据
* request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据
* 方法:
1. void setAttribute(String name,Object obj):存储数据(数据通过键与值的方式存储),存储数据之后,参与转发的2个Servlet资源都能访问到设置的数据,实现了在请求转发范围的多个资源中共享数据。
2. Object getAttitude(String name):通过键获取值
3. void removeAttribute(String name):通过键移除键值对
请求转发与资源共享的代码测试:创建2个实现HTTPServlet的类,并指定资源名称(类名映射)为“/ServletTest3”、“/ServletTest4”。
//“/ServletTest3”
package lkjTest.servlet;
import javax.servlet.RequestDispatcher;
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("/ServletTest3")
public class ServletTest3 extends HttpServlet
{
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
System.out.println("ServletTest3333333333被访问了....");
//将请求从"/ServletTest3"转发到"/ServletTest4"
// RequestDispatcher rd = request.getRequestDispatcher("/ServletTest4");//转发到资源目录(映射名称)为"/ServletTest4"的类
// rd.forward(request , response);
//一般合成一行
// request.getRequestDispatcher("/ServletTest4").forward(request , response);
//结果:输出:
// ServletTest3333333333被访问了....
//ServletTest44444444被访问了....
//request.getRequestDispatcher("http://www.itcast.cn").forward(request,response);//失败,只能转发到当前服务器内部资源中
//资源共享
request.setAttribute("msg" , "hello world");//设置数据
request.getRequestDispatcher("/ServletTest4").forward(request , response);//设置数据后请求转发
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
this.doPost(request , response);
}
}
//“/ServletTest4”
package lkjTest.servlet;
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("/ServletTest4")
public class ServletTest4 extends HttpServlet
{
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
System.out.println("ServletTest44444444被访问了....");
Object msg = request.getAttribute("msg");//通过键获取请求转发过来的值
System.out.println(msg);
/*结果
ServletTest3333333333被访问了....
ServletTest44444444被访问了....
hello world
*/
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
this.doPost(request , response);
}
}
4. 获取ServletContext:
* ServletContext getServletContext()
区分这个对象与获取虚拟目录(getContextPath)、获取Servlet类路径(getServletPath)2个方法。
代码测试
package lkjTest.servlet;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
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("/ServletTest3")
public class ServletTest3 extends HttpServlet
{
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
ServletContext servletContext = request.getServletContext();
System.out.println(servletContext);//org.apache.catalina.core.ApplicationContextFacade@dc1480a
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
this.doPost(request , response);
}
}