我们在前面了解了前端页面与后台的联系,以及doGet和doPost的区别,我们知道,浏览器打开一个网页,就是对服务器端的一次请求,这个请求根据get和post的请求方式进入到应该进入的doGet和doPost的方法中,进行数据的处理,按照正常来说,应该是前端对后台进行访问,发出一次请求,那么理当也应该得到一次响应,以及在前面也牵扯了一些随着请求把数据传输到后台的案例,那么,我们现在来仔细的进行分析请求和响应这两个web项目中最重要的事情。
request和response
我们现在练习的时候,一般都是利用自己电脑的浏览器访问自己做的web项目,Tomcat服务器也是跑在自己的电脑上的,浏览器地址栏里访问地址一般都以:localhost:8080/开头。我们知道以后如果工作或者自己想要开发自己上线的项目,那么这个项目一定是连接到互联网上才可以让其他人能够访问的,所以在开始了解请求和响应之前,我们应该简单的了解一下我们现在所使用的的网络环境。
Http协议
我们自己联系使用的: localhost:8080 /项目名/资源名 ,全称为:http:// localhost:8080 /项目名/资源名
以及我们在网上进行访问的,比如csdn的官网:https://www.csdn.net/ 也都是以https://开头的。(这里以https和http的区别为,https的信息是以加密形式传输的,提高了数据的安全性,其他的都和http一模一样)
以http开头进行标识的意思就是,这里所使用的网络协议,都是使用的http://协议。即使是在自己电脑上访问自己的项目,也是通过http协议进行信息传输的。
Http协议版本
现在的Http版本有1.0 、 1.1
区别为:
1.0版本不支持常连接,也就意味着课程一个网页要对服务器发起N次连接请求,会有N次握手的机会,耽误时间。
例如:网站有100张图片,会连接服务器100次,握手100次。
1.1版本支持了常连接=
例如:网站有100张图片,会连接服务器100次,握手1次。
现在互联网使用的版本都为1.1版本
Http协议特点为无状态性,就是一次请求对应一次响应,而下一次请求将和上一次的请求响应没有任何关系,不会对上次的请求和响应进行任何的记录,以此来提高信息传递的安全性。
response
介绍过了网络协议,我们先来了解响应:response。
我们新建一个Servlet :
package com.yht.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HttpResponseTest extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//向控制台打印,表示doGet方式执行了。
System.out.println("goGet方法执行了.....");
//向前端页面进行传输,传输一段字符串,由浏览器进行解析。注意:这里还不能传输中文。
PrintWriter out = response.getWriter();
out.print("<h1> hello </h1>");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//这里的意思是不管发过来的是什么请求,最后都转向doGet方法,在doGet方法里也可以这样调用doPost。
doGet(request, response);
}
}
我们可以看到doGet和doPost方法中都有两个参数,一个是HttpServletRequest对象,一个是HttpServletResponse对象。
这两个对象,就分别存储着 请求传输的所有信息, 一个存储着要进行响应的所有信息。
我们这里是想要进行响应,所以就利用response参数对服务器端进行响应。
我们运行项目,在浏览器中输入地址(推荐用火狐浏览器,因为里面大部分为中文,可以看得更清晰。)输入地址栏后不用急着回车访问,先按f12,打开配置,然后点击网络选项,然后再进行访问:
我们就会点击下图的蓝色请求,就会出现这样的框架
我们可以看到这边有着很多的参数,而这些参数,就是基于这次请求和响应的。我们先来对响应进行分析这些参数的作用和代表的意思。
响应行
上面大图中消息头里面的这部分内容,就是我们这次访问响应行。里面存储的信息为:协议及版本 | 状态码
而这里的状态码 绿色的200代表着这次访问是成功的。比如我们平常会遇到的404,也是响应码,意思是,页面没有找到。
更多响应码:http://tools.jb51.net/table/http_status_code
我们可以自己设置这次响应,返回什么状态码,以及返回的状态码所对应的信息:
依照上面的代码把doGet方法修改为:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*//向控制台打印,表示doGet方式执行了。
System.out.println("goGet方法执行了.....");
//向前端页面进行传输,传输一段字符串,由浏览器进行解析。注意:这里还不能传输中文。
PrintWriter out = response.getWriter();
out.print("<h1> hello </h1>");*/
response.setStatus(404);
response.sendError(404, "sorry,sorry,sorry");
}
然后再次运行访问:
这里的提示信息就变成了我们预设好的提示信息。
响应头
这里面的信息,就是我们的响应头里面的信息。这里面只是显示了一部分。
这上面的东西,大部分都是了解即可。不用全部记住。知道有这些东西,真需要了再去了解具体的使用都可以。
响应头里面的信息也都是我们在后台可以自己设置的。我们来演示比较常用而且重要的:
重要响应头:
编辑代码:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setHeader("refresh", "3;url=https://blog.csdn.net/qq_39565202");
}
演示:
第二个重要响应头:
我们再次修改代码:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//向前端页面进行传输,传输一段字符串,由浏览器进行解析。注意:这里还不能传输中文。
PrintWriter out = response.getWriter();
out.print("<h1>好吃的都给你</h1>");
}
访问:
然后我们加上:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setHeader("Content-Type", "text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.print("<h1>好吃的都给你</h1>");
}
这样就解决了响应的传输不能传输中文的问题。
第三个重要响应头:
设置代码:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setStatus(302);
response.setHeader("Location", "https://blog.csdn.net/qq_39565202");
}
运行:
我们可以看到我们输入的地址栏是我们的servlet的地址,但是进行访问后直接跳转到了我们在代码中写入的页面。
这就是 重定向
重定向是一个非常重要的概念。
这里代码的意思是:
response.setStatus(302); 当程序运行到这句话的时候,就返回一个302状态码,意思为让浏览器去访问另一个资源
response.setHeader("Location", "https://blog.csdn.net/qq_39565202"); 这句告诉浏览器要去访问的资源的地址。
响应体
上面的响应行其实就是响应头的第一行,他们设定了一些基本的属性,而数据的传递主要依赖的就是 : 响应体。
响应体的应用主要有以下几个方面:
第一:通过PrintWriter向浏览器端打印信息。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//这里是响应头对信息格式的设置方法
//response.setHeader("Content-Type", "text/html;charset:utf-8");
//这里是响应体对信息格式的设置方法,两个功能是一样的,不过相比这个更加简洁一点。
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.print("<h1>好吃的都给你<h1>");
}
第二:通过Response对象获取输出流
我们上面其实也是使用的流,响应体的对象信息时无限长的,所以一般用来传输大量的信息,比如我们在网络进行的下载。
而上面的只是对文字进行打印。是字符流,里面进行文字的解析,如果是html语句,浏览器会自动进行解析。我们还可以使用字节流进行文件的传输。这里还是先对文本进行传输表示一下可以对字节型数据进行传输。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//这里是响应头对信息格式的设置方法
//response.setHeader("Content-Type", "text/html;charset:utf-8");
//这里是响应体对信息格式的设置方法,两个功能是一样的,不过相比这个更加简洁一点。
response.setContentType("text/html;charset:utf-8");
/*PrintWriter out = response.getWriter();
out.print("<h1>好吃的都给你<h1>");*/
OutputStream os = response.getOutputStream();
os.write("<h1>前程有梦乘帆远行</h1>".getBytes());//因为参数类型为字节,或者字节数组,所以这里转换为字节数组
}
在编码格式里,使用字节流时 charset=utf-8 改成了 charset:utf-8 一定要修改,否则会乱码
//这里是响应头对信息格式的设置方法
//response.setHeader("Content-Type", "text/html;charset:utf-8");
//这里是响应体对信息格式的设置方法,两个功能是一样的,不过相比这个更加简洁一点。
response.setContentType("text/html;charset:utf-8");
/*
* 设置响应体的编码字符集为utf-8
设置浏览器的解码字符集为utf-8
告知浏览器响应体里是html代码,需要进行解析
乱码解决的设置代码,必须放在获取字节流/字符流之前
还有一点就是,在一次响应里,不能够字节流和字符流共有,只可以有一个,否则会报错。
*/
总结来说,响应的应用主要有:
①利用响应行:查看和设置响应状态码
②利用响应头或者响应行:设置编码格式
③利用响应体:获取流来进行数据的传输
而请求的应用和重要性,相对来说是超过响应的。
request
我们来做这样一个网页:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="aaa"/>
<script src="vvv"></script>
</head>
<body>
<img src="xxx" />
</body>
</html>
这个网页什么也没有,我们可以访问一下,然后再f12在网络界面中观察。
(最后一个是 Tomcat 服务器的小猫的 图标的请求,可以忽略)
我们对比可以看到,这里面一共有四个主要的请求,分别是html文件本身 以及 link img script 等标签发出来的,所以我们可以得知,所有想要访问服务器端资源的都会发出一次请求,而不是整体发出一次请求(超链接或者按钮等是点击时会进行发送请求)
请求行
我们来观察:
这里就是显示出来的请求行部分的信息,但是其实远不止这些,我们可以通过以下方法来获取请求行中的信息:
方法声明 | 功能描述 |
String getMethod() | 该方法用于获取HTTP请求消息中的请求方式(如GET、POST等)
忽略大小写判断请求方式
|
String getRequestURI() | 该方法用于获取请求行中资源名称部分,即位于URL的主机和端口之后、参数部分之前的部分
仅获取资源名,不包含参数列表
|
String getQueryString() | 该方法用于获取请求行中的参数部分,也就是资源路径后面问号(?)以后的所有内容 |
String getProtocol() | 该方法用于获取请求行中的协议名和版本,例如,HTTP/1.0或HTTP/1.1 |
String getContextPath() | 该方法用于获取请求URL中属于WEB应用程序的路径,这个路径以“/”开头,表示相对于整个WEB站点的根目录,路径结尾不含“/”。如果请求URL属于WEB站点的根目录,那么返回结果为空字符串("")
动态获取项目名称。 例如:/day12
|
String getServletPath() | 该方法用于获取Servlet的名称或Servlet所映射的路径
url-pattern |
String getRemoteAddr() | 该方法用于获取请求客户端的IP地址,其格式类似于“192.168.0.3”
本机: 0:0:0:0:0:0:0:1 localhost 127.0.0.1
统计IP
|
String getRemoteHost() | 该方法用于获取请求客户端的完整主机名,需要注意的是,如果无法解析出客户机的完整主机名,该方法将会返回客户端的IP地址 |
int getRemotePort() | 该方法用于获取请求客户端网络连接的端口号 |
String getLocalAddr() | 该方法用于获取Web服务器上接收当前请求网络连接的IP地址 |
String getLocalName() | 该方法用于获取Web服务器上接收当前网络连接IP所对应的主机名 |
int getLocalPort() | 该方法用于获取Web服务器上接收当前网络连接的端口号 接收请求的端口号 |
String getServerName() | 该方法用于获取当前请求所指向的主机名,即HTTP请求消息中Host头字段所对应的主机名部分 |
int getServerPort() | 该方法用于获取当前请求所连接的服务器端口号,即如果HTTP请求消息中Host头字段所对应的端口号部分 发送响应的端口号 |
String getScheme() | 该方法用于获取请求的协议名,例如http、https或ftp |
StringBuffer getRequestURL() | 该方法用于获取客户端发出请求时的完整URL,包括协议、服务器名、端口号、资源路径等信息,但不包括后面的查询参数部分。注意,getRequestURL()方法返回的结果是StringBuffer类型,而不是String类型,这样更便于对结果进行修改
获取到包含http协议的资源路径,不包含参数列表
|
演示:
package com.yht.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class RequestTest1Servlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String method = request.getMethod();
System.out.println("访问方法::"+method);
String url = request.getRequestURL().toString();
System.out.println("访问地址:"+url);
String uri = request.getRequestURI();
System.out.println("uri:"+uri);//资源标志符
String protocal = request.getProtocol();
System.out.println("协议版本:"+protocal);
String scheme = request.getScheme();
System.out.println("协议名:"+scheme);
String contextPath = request.getContextPath();
System.out.println("项目名:"+contextPath);
String id = request.getRemoteAddr();
System.out.println("客服端ip地址"+id);
String queryString = request.getQueryString();
System.out.println("参数(?后带的值):"+queryString);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
}
页面调用:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="RequestTest1Servlet?name=yht&age=22">查看请求行内容</a>
</body>
</html>
访问页面点击超链接结果:
访问方法::GET
访问地址:http://localhost:8080/Servlet02/RequestTest1Servlet
uri:/Servlet02/RequestTest1Servlet
协议版本:HTTP/1.1
协议名:http
项目名:/Servlet02
客服端ip地址127.0.0.1
参数(?后带的值):name=yht&age=22
其他的都不太重要,有兴趣的可以自行演示。
请求头
这些是我们浏览器里f12网络栏里的请求头信息。同样,与response不同的是,这里都是只读信息,不能进行更改。
这些都存在于request中,我们也可以进行获取。
修改上面的Servlet代码为:
package com.yht.servlet;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class RequestTest1Servlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String agent = request.getHeader("User-agent");
System.out.println("浏览器版本为:"+agent);
String conn = request.getHeader("Connection");
System.out.println("连接方式为:"+conn);
System.out.println("------------以下为全部---------------");
Enumeration<String> headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()){
String key = headerNames.nextElement();
System.out.println(key+"-----"+request.getHeader(key));
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
}
访问页面,点击超链接,观察后台控制台:
浏览器版本为:Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:71.0) Gecko/20100101 Firefox/71.0
连接方式为:keep-alive
------------以下为全部---------------
host-----localhost:8080
user-agent-----Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:71.0) Gecko/20100101 Firefox/71.0
accept-----text/html,application/xhtml+xml,application/xml;q=0.9,*/*;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
referer-----http://localhost:8080/Servlet02/requestTest.html
upgrade-insecure-requests-----1
请求体
请求体其实就是我们进行请求时从前端传给后台主要数据的通道,get方式是没有请求体的,因为它的参数都通过url上直接进行传输了。
只有post请求方式时会产生请求体。不过通过get请求方式发送的参数和post产生的请求体里的参数都可以通过同样的方法获取。
我们进行post请求的请求体内的参数和get请求后面url后面如果缀的有参数的话都会显示到:
编写一个页面:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="ServletRequestServlet2" method="post">
username:<input type="text" name="username" /><br/>
password:<input type="text" name="password" /><br/>
hobby:<input type="checkbox" name="hobby" value="python">python
<input type="checkbox" name="hobby" value="c">c
<input type="checkbox" name="hobby" value="java">java<br/>
address:<select name="address" >
<option value="bj">北京</option>
<option value="sh">上海</option>
<option value="zz">郑州</option>
</select><br/>
<input type = "submit" value ="提交">
</form>
</body>
</html>
观察下面参数:
然后我们在去接受数据的Servlet中进行接受:
package com.yht.servlet;
import java.io.IOException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletRequestServlet2 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*String username = request.getParameter("username");
String password = request.getParameter("password");
String[] hobby = request.getParameterValues("hobby");
String address = request.getParameter("address");
System.out.println(username+"\t"+password+"\t"+Arrays.toString(hobby)+"\t"+address);*/
//yht yht [python, c, java] sh
/*Enumeration<String> re = request.getParameterNames();
while(re.hasMoreElements()){
String key = re.nextElement();
System.out.println(key+"-----"+request.getParameter(key));
}*/
/*
* username-----yht
password-----yht
hobby-----python
address-----sh
*/
Map<String, String[]> map = request.getParameterMap();
Set<Entry<String, String[]>> entrySet = map.entrySet();
for(Map.Entry<String, String[]> entry:entrySet){
System.out.println(entry.getKey()+"-----"+Arrays.toString(entry.getValue()));
}
/*
* username-----[yht]
password-----[yhttt]
hobby-----[python, c, java]
address-----[sh]
*/
}
}
三种接收方式,第一种当数据很多时比较好费时间,第二种无法接收传输类型为数组的全部元素,只能接收第一个,第三种较为完善。
到此,所有相关request和response的差不多都已经介绍完了。如果有看到这篇博客的盆友对此篇有问题,欢迎下面评论留言。