GET和POST请求的本质区别是什么?
前言
最近在公司实习工作的同时了解研究了下TCP/IP协议,对之前面试时的问题:GET和POST有何区别?对之前我一个相对标准的答案产生了怀疑。提示:以下是本篇文章正文内容,以后的文章我会尽量标准点
一、我的答案
GET和POST有何区别?
- GET在浏览器回退时候无害,而POST会再次提交请求。
- GET产生的URL地址可以被BookMark,而POST不可以
- GET请求会被浏览器主动cache,而POST不会,除非手动设置
- GET请求只能进行url编码,而POST支持多种编码方式
- GET请求参数会被完整保留在浏览器历史记录中,而POST中的参数不会被保留
- GET请求在URL中传送的参数长度是有限的,而POST没有
- 对参数的数据类型,GET只接受ASCLL字符,而POST没有限制
- GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息
- GET参数通过URL传递,POST放在Request body中
当时回答的可能没这么多,但是有其中两三条,此答案应该算是“标准答案”----如果不深入的话
二、简单介绍下GET和POST
在介绍GET和POST之前我们首先来了解一下HTTP——超文本传输协议(HTTP)的设计目的是保证客户端与服务器之间的通信。
HTTP 的工作方式是客户端与服务器之间的请求-应答协议。
web 浏览器可能是客户端,而计算机上的网络应用程序也可能作为服务器端。
举例:客户端(浏览器)向服务器提交 HTTP 请求;服务器向客户端返回响应。响应包含关于请求的状态信息以及可能被请求的内容。
在客户机和服务器之间进行请求-响应时,两种最常被用到的方法是:GET 和 POST。
GET - 从指定的资源请求数据。
POST - 向指定的资源提交要被处理的数据。
然后我们分别来使用一下GET和POST
建立项目
我们使用的软件是IDEA
首先 File -> New -> Project
点击Maven
选中 Create from archetype
选中找到第二个尾部是webapp
然后按照图中设置
next
Finish
右键main 选择new -> Directory
新建Java和resources
在pom.xml中引入Spring模块
<!-- spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<!-- spring-web-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
新建视图解析器
在resources右键 New -> XML Configuration File ->Spring Config
名字命名为 springmvc.xml (个人习惯)
由于本次主要目的不是新建一个Spring项目,所以视图解析器中内容直接给出
<?xml version="1.0" encoding="UTF-8"?>
<!--视图解析器-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--设置自动扫描的包-->
<context:component-scan base-package="com.test.getpost.handler"></context:component-scan>
<!-- 配置视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀-->
<property name="prefix" value="/WEB-INF/views/"></property>
<!-- 后缀-->
<property name="suffix" value=".jsp"></property>
</bean>
<!--配置处理静态资源-->
<mvc:default-servlet-handler/>
<!-- 配置了处理静态资源之后,Handler的方法上的@RequestMapping失效, -->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
在webapp/WEB-INF目录中找到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">
<!-- 使用Spring的过滤器解决POST请求中文乱码问题-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置前端控制器 DispatcherServlet-->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<!-- SpringMVC配置文件的路径-->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<!-- 配置映射的请求地址,设置为 / -->
<!-- 此处【/】 只处理除.jsp以外的文件-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
我们测试一下,
如图在目录中新建一个控制器目录——handler,新建一个testHandler.java
写入代码
@Controller
public class testHandler {
@RequestMapping("/hello")
public String testHelloWorld(){
System.out.println("Hello SpringMVC!");
return "success";
}
}
在index.jsp中新建一个超链接,同时在WEB-INF中新建一个文件views/success.jsp
配好tomcat服务器(过程略,有空写一个操作步骤,鸽~),运行
点击超链接
项目搞定了
下面我们就来看看GET和POST方式,为了方便我们都使用From
先随意新建两个实体类,代码
package com.test.getpost.beans;
//员工
public class Employee {
private Integer Id; //id
private String Last_Name; //姓名
private String Email; //邮箱
private Department Dept; //部门
// 无参构造器
public Employee(){
super();
}
//有参数构造器
public Employee(Integer id, String last_Name, String email, Department dept) {
super();
Id = id;
Last_Name = last_Name;
Email = email;
Dept = dept;
}
public Integer getId() {
return Id;
}
public void setId(Integer id) {
Id = id;
}
public String getLast_Name() {
return Last_Name;
}
public void setLast_Name(String last_Name) {
Last_Name = last_Name;
}
public String getEmail() {
return Email;
}
public void setEmail(String email) {
Email = email;
}
public Department getDept() {
return Dept;
}
public void setDept(Department dept) {
Dept = dept;
}
@Override
public String toString() {
return "Employee{" +
"Id=" + Id +
", Last_Name='" + Last_Name + '\'' +
", Email='" + Email + '\'' +
", Dept=" + Dept +
'}';
}
}
package com.test.getpost.beans;
//部门
public class Department {
private Integer Id;
private String Dept_Name;
public Integer getId() {
return Id;
}
public void setId(Integer id) {
Id = id;
}
public String getDept_Name() {
return Dept_Name;
}
public void setDept_Name(String dept_Name) {
Dept_Name = dept_Name;
}
@Override
public String toString() {
return "Department{" +
"Id=" + Id +
", Dept_Name='" + Dept_Name + '\'' +
'}';
}
}
1.GET方式
在index.jsp上新建一个表单,
<form action="${pageContext.request.contextPath}/testPOJO" method="get">
员工工号:<input type="text" name="Id"><br>
员工姓名:<input type="text" name="Last_Name"><br>
员工邮箱:<input type="text" name="Email"><br>
部门编号:<input type="text" name="Dept.Id"><br>
部门名称:<input type="text" name="Dept.Dept_Name"><br>
<input type="submit" value="Test POJO">
</form>
在testHandler中添加代码
@RequestMapping("/testPOJO")
public String testPOJO(Employee employee) {
System.out.println("信息:" + employee);
return "success";
}
运行:
点击提交Test POJO
2.POST方式
将index.jsp中的from代码中的method="get"改为method=“post”
再次运行
单独的从导航栏上来看,我们之前的回答好像是没有毛病,但是我们如果更加深层次的扒一扒,我们把GET和POST本质拿出来看看。
本文重点
首先我们应该明确一点GET和POST本质没有区别,上面也说了,GET和POST是HTTP协议中的两种发送请求的方法。 HTTP是什么?
HTTP是基于TCP/IP的关于数据在万维网中如何通信的协议。HTTP的底层是TCP/IP,所以GET和POST的底层也是TCP/IP,也就是说,GET/POST都是TCP链接,GET和POST能做的事情完全一样,你要给GET加上request
body,给POST带上url参数,技术上是完全可以的。 那么上述答案中的两者区别是怎么回事呢?
在万维网的世界中,HTTP协议像是一种规则,给TCP运输数据设置了好几个服务类别,有GET、POST、PUT、DELETE等等,HTTP规定,当执行GET时候,要给设置GET标签,就是上述例子中的method=“get”,而且要求把传送的数据放在URL中,方便记录。而POST请求,就需要设置method=“post”,并且不需要显示数据在url中。当然,如果你是好事者,你可以在用GET的时候,把数据不放到url中,也可以在使用POST时候,把数据显示到url中(具体操作:自行百度,我懒 ~ ~)
HTTP只是一个准则,TCP才是GET和POST怎么实现的基本,但是我们只看到HTTP对GET和POST参数的传送渠道(url还是request
body)提出了要求。 那么最开始我们说的关于参数有限又是怎么来的呢?
在万维网中,不同的浏览器:发起http请求和服务器:接受http请求,就是不同的运输公司,虽然原则上url可以无限的放资源数据。数据量太大对浏览器和服务器都是很大的负担和风险,所以需要限制单次数量节约资源和减低风险。业界不成文规定,(大多数)浏览器通常都会限制url长度在2K字节,(大多数)服务器最多处理64K的url,超过的部分,看着办吧。。。。
同时如果你用了GET在request body中偷偷藏了数据,不同的服务器的处理方式也是不同的,有些服务器会帮你读出数据,有的服务器会忽略,所以,上面的一种假想型操作我不想尝试。
好了,现在我们得出结论,GET和POST本质上就是TCP链接没区别,但是由于HTTP规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。还没完!!!然后接下来才是我想说的也是看到wx中某个博主猿某某说的:
很多地方都不会说到
GET和POST有一个重大区别:GET产生一个TCP数据包
POST产生两个TCP数据包
长一点的解释:
- 对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
- 而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 返回数据。(有点像 3次握手 那个东西,没事可以看看,有些面试为了提升自己面试B格会问问这个问题,我暂时还没遇到过,嘿嘿)
也就是说,GET只需要跑一趟就把货送到了,而POST需要跑两趟,首先先过去和服务器说一声,我要来送货,你准备一下,然后回头把货送回去。
因为POST需要两步,时间上消耗的多一些,看起来好像GET比POST更有效,因此Yahoo团队有推荐使用GET代替POST来优化网站,但是这是一个巨大的坑,why?
- GET和POST都有自己的语义,不能随便混用
- 据研究,在网络环境好的情况下,发一次包和发两次包基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。
- 并不是所有的浏览器都会在POST中发送两次包,Fixfox就只发送一次。