GET & POST的区别
如果你对服务器和浏览器的交互还没有概念的话呢,建议先看一下「Rose Island」山外多功能调试助手用作虚拟服务器。
可以看到,通过域名或是IP地址加载网页内容是默认通过GET请求,之后服务器与客户端的交互可以通过GET,也可以通过POST。
GET和POST的区别大致如下:
GET POST
可传递数据类型 ASCII文本(汉字有专门的方法转换) 不限,支持二进制文件
可传递的数据量 有限制(2048字节减去URL长度) 无限制
内容编码类型 application/x-www-form-urlencoded application/x-www-form-urlencoded
或multipart/form-data
后退/刷新 回退后再次前进或刷新不会通知用户 数据会被再次提交
历史记录 浏览器会记录全部内容 浏览器只记录接收POST内容的URL但不记录POST的具体内容
典型应用 获取服务器上的资源,如url地址栏请求,下载 向服务器添加资源,如上传附件,提交表单
GET和POST还有一个区别就是TCP数据包的个数,GET产生一个数据包,POST产生两个数据包,分别放header和data。两者请求的过程如下:
GET请求过程:
浏览器请求tcp连接(第一次握手)
服务器答应进行tcp连接(第二次握手)
浏览器确认,并发送GET请求header和data(第三次握手,这个报文比较小,所以http会在此时进行第一次数据发送)
服务器返回200 OK响应
POST请求过程:
浏览器请求tcp连接(第一次握手)
服务器答应进行tcp连接(第二次握手)
浏览器确认,并发送POST请求header(第三次握手,这个报文比较小,所以http会在此时进行第一次数据发送)
服务器返回100 Continue响应
浏览器发送body中的数据
服务器返回200 OK响应
不过并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。
我对GET和POST区别的理解呢,最重要的是上传表单的时候GET会将内容都显示到浏览器url地址上,安全性很差,而POST会将参数名和参数值放到body中,不会在浏览器url地址上显示出来。
举个栗子看一下:
GET请求
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>用户登录 - MindMotion</title>
</head>
<body>
<form action="login.cgi" method="GET" name="login">
<label>
<h1>用户账号登录</h1>
<hr><br>
</label>
账 号:<input type="text" placeholder="请输入账号名" name="username" required><br>
密 码:<input type="password" placeholder="请输入密码" name="password" required><br><br>
<input id="submit" type="submit" name="checkinSubmit" value="login">
</form>
</body>
</html>
点击submit之后可以看到:
表单数据以param1=param1_value¶m2=param2_vlaue的形式添加在url后面,以?分割,每个Param之间用&分割。
POST请求
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>用户登录 - MindMotion</title>
</head>
<body>
<form action="login.cgi" method="POST" name="login">
<label>
<h1>用户账号登录</h1>
<hr><br>
</label>
账 号:<input type="text" placeholder="请输入账号名" name="username" required><br>
密 码:<input type="password" placeholder="请输入密码" name="password" required><br><br>
<input id="submit" type="submit" name="checkinSubmit" value="login">
</form>
</body>
</html>
点击submit之后可以看到:
表单数据封装在body中,url中只显示对应的action。
POST请求在CGI中的实现
介绍一下整个项目使用的软硬件环境:
硬件环境:MM32F3277、网线
软件环境:IAR、FreeRTOS、LWIP、makefsfile.exe
如果对CGI和SSI接口不熟悉的话,可以看一下「Rose Island」MM32搭建Web服务器——SSI和CGI接口,里面包括了GET请求在CGI中的实现方法。
先说一下浏览器和MM32服务器通过POST指令请求的过程:
浏览器和TCP建立连接后,函数http_recv()接收浏览器的请求数据
MM32调用http_parse_request()函数提取接收到的字符串中是GET指令还是POST指令
调用http_post_request处理POST请求
调用httpd_post_begin处理header部分的uri
调用httpd_post_rxpbuf处理接收数据长度
调用httpd_post_receive_data处理body部分的数据
调用httpd_handle_post_finished,在这里调用http_find_file
之后的过程就和上一篇写的一样啦
在LWIP中,默认使用GET方法,所以使用POST指令需要自己写几个函数。
首先得先将这个POST定义修改为1
#define LWIP_HTTPD_SUPPORT_POST 1
也需要关心一下可传递数据的大小,文件数据一般是1514字节一帧数据地发送给板端
#define PBUF_POOL_BUFSIZE 1524
虽然LWIP中没有写POST的相关函数,但在httpd.h中定义了三个函数,分别是httpd_post_begin,httpd_post_receive_data,httpd_post_finished
httpd_post_begin用于初始化POST数据接收功能,获取uri,就是header部分
err_t httpd_post_begin(void *connection, const char *uri,
const char *http_request,u16_t http_request_len,
int content_len, char *response_uri,
u16_t response_uri_len, u8_t *post_auto_wnd)
{
memset(http_uri_buf,0,sizeof(http_uri_buf));
strcpy(response_uri,uri);
return ERR_OK;
}
httpd_post_receive_data用于接收数据内容,就是body部分,分为params和param_vals
方法一:
在uri后面加上?然后加上*p中的param1=param1_val & param2=param2_val,在这里人为地把POST转成GET指令的样式,传到http_find_file中(这个函数默认处理GET指令),用GET指令的方法去调用CGI接口。如果用这种方法的话,还需要修改一下uri的最大长度,不然很容易超。
err_t httpd_post_receive_data(void *connection, struct pbuf *p)
{
*(strrchr(p->payload,'&')) = 0;
sprintf(http_uri_buf +strlen(http_uri_buf),"?%s",(char*)p->payload);
return ERR_MEM;
}
#define LWIP_HTTPD_MAX_REQUEST_URI_LEN 256
方法二:
通过形参connection,调用extract_uri_parameters将*p中的body数据分别赋给hs的params和param_vals,因为http_find_file是针对GET指令写的,如果用这种方法的话,就要修改http_find_file函数,将http_cgi_paramcount = extract_uri_parameters(hs, params);这句话删掉,不然的话会将hs中的params和param_vals清空。
err_t httpd_post_receive_data(void *connection, struct pbuf *p)
{
struct http_state *hs = (struct http_state *)connection;
*(strrchr(p->payload,'&')) = 0;
extract_uri_parameters(hs, (char*)p->payload);
return ERR_MEM;
}
httpd_post_finished在数据接收完成后执行,由httpd_handle_post_finished调用,之后就进入http_find_file去根据uri查找对应的内容,因为header和body的内容我们在前面两个函数中已经提取出来了,这个函数就先空着。但是需要改一下httpd.c中的httpd_handle_post_finished
void httpd_post_finished(void *connection, char *response_uri, u16_t response_uri_len)
{
}
static err_t
http_handle_post_finished(struct http_state *hs)
{
#if LWIP_HTTPD_POST_MANUAL_WND
/* Prevent multiple calls to httpd_post_finished, since it might have already
been called before from httpd_post_data_recved(). */
if (hs->post_finished) {
return ERR_OK;
}
hs->post_finished = 1;
#endif /* LWIP_HTTPD_POST_MANUAL_WND */
/* application error or POST finished */
/* NULL-terminate the buffer */
// http_uri_buf[0] = 0;
// httpd_post_finished(hs, http_uri_buf, LWIP_HTTPD_URI_BUF_LEN);
return http_find_file(hs, http_uri_buf, 0);
}
这三个函数有很多种写法,可以根据你的功能逻辑进行修改,也可以参考LWIP中的post_example.c中的写法。
————————————————
版权声明:本文为CSDN博主「Rose Island」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42150654/article/details/116310982
|