《从 0 到 1 理解 HTTP:协议结构、状态码设计与 RESTful API 最佳实践》

HTTP协议

在互联⽹世界中, HTTP (HyperText Transfer Protocol,超⽂本传输协议)是⼀个⾄关重要的协议。 它定义了客⼾端(如浏览器)与服务器之间如何通信,以交换或传输超⽂本(如HTML⽂档)。
HTTP协议是客⼾端与服务器之间通信的基础。客⼾端通过HTTP协议向服务器发送请求,服务器收到 请求后处理并返回响应。HTTP协议是⼀个 ⽆连接 ⽆状态 的协议,即每次请求都需要建⽴新的连接, 且服务器不会保存客⼾端的状态信息。

认识URL

平时我们俗称的 “网址” 其实就是这里所说的URL

urlencode urldecode
/ ? : 等这样的字符, 已经被 url 当做特殊意义理解了. 因此这些字符不能随意出现.
⽐如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进⾏转义.
转义的规则如下:
将需要转码的字符转为16进制,然后从右到左,取4位(不⾜4位直接处理),每2位做⼀位,前⾯加
上%,编码成%XY格式
例如:
 
"+" 被转义成了 "%2B"
urldecode就是urlencode的逆过程;

认识HTTP请求与响应的格式

HTTP请求

⾸⾏: [⽅法] + [url] + [版本]
Header: 请求的属性, 冒号分割的键值对;每组属性之间使⽤ \r\n 分隔;遇到空⾏表⽰ Header
部分结束
Body: 空⾏后⾯的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有⼀个 Content-Length属性来标识Body的⻓度;
HTTP响应

⾸⾏: [版本号] + [状态码] + [状态码解释]
Header: 请求的属性, 冒号分割的键值对;每组属性之间使⽤\r\n分隔;遇到空⾏表⽰Header部分结
Body: 空⾏后⾯的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有⼀个 Content-Length属性来标识Body的⻓度; 如果服务器返回了⼀个html⻚⾯, 那么html⻚⾯内容就 是在body中.
基本应答格式

HTTP的⽅法

其中我们最常⽤的就是 GET ⽅法和 POST ⽅法.

GET方法(重点)

⽤途:⽤于请求URL指定的资源。
示例: ⽰例: GET /index.html HTTP/1.1
特性:指定资源经服务器端解析后返回响应内容。
注意: 要通过自己写的 http 服务器,验证 GET ⽅法 , 这⾥需要了解⼀下 FORM 表单的问题
就要引⼊ web根⽬录 ,⽂件读取的基本操作。
std::string GetFileContentHelper(const std::string &path)
{
// ⼀份简单的读取⼆进制⽂件的代码
std::ifstream in(path, std::ios::binary);
if (!in.is_open())
     return "";
in.seekg(0, in.end);

int filesize = in.tellg();
in.seekg(0, in.beg); 

std::string content;
content.resize(filesize);
in.read((char *)content.c_str(), filesize);
// std::vector<char> content(filesize);
// in.read(content.data(), filesize);
in.close();
return content;
}

POST方法(重点)

⽤途:⽤于传输实体的主体,通常⽤于提交表单数据。
⽰例: POST /submit.cgi HTTP/1.1
特性:可以发送⼤量的数据给服务器,并且数据包含在请求体中。
同样的要使用我们自己写的HTTP服务器验证POST方法,也需要用到FORM表单;

PUT方法

⽤途:⽤于传输⽂件,将请求报⽂主体中的⽂件保存到请求URL指定的位置。
⽰例: PUT /example.html HTTP/1.1
特性:不太常⽤,但在某些情况下,如RESTful API中,⽤于更新资源。

HEAD方法

⽤途:与GET⽅法类似,但不返回报⽂主体部分,仅返回响应头。
⽰例: HEAD /index.html HTTP/1.1
特性:⽤于确认URL的有效性及资源更新的⽇期时间等。

DELETE⽅法

⽤途:⽤于删除⽂件,是PUT的相反⽅法。
⽰例: DELETE /example.html HTTP/1.1
特性:按请求URL删除指定的资源。

OPTIONS⽅法

⽤途:⽤于查询针对请求URL指定的资源⽀持的⽅法。
⽰例: OPTIONS * HTTP/1.1
特性:返回允许的⽅法,如GET、POST等。

HTTP的状态码

最常⻅的状态码, ⽐如 200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad
Gateway)
状态码含义应用样例
100Continue
上传⼤⽂件时,服务器告诉客⼾端可以继续上传
200Ok
访问⽹站⾸⻚,服务器返回⽹⻚内容
201Created
发布新⽂章,服务器返回⽂章创建成功的信息
301
Moved Permanently
⽹站换域名后,⾃动跳转到新域名;搜索引擎更新⽹站
链接时使⽤
302
Found 或 See Other
⽤⼾登录成功后,重定向到⽤⼾⾸⻚
304
Not Modified
浏览器缓存机制,对未修改的资源返回304状态码
400
Bad Request
填写表单时,格式不正确导致提交失败
401
Unauthorized
访问需要登录的⻚⾯时,未登录或认证失败
403
Forbidden
尝试访问你没有权限查看的⻚⾯
404
Not Found
访问不存在的⽹⻚链接
500
Internal Server Error
服务器崩溃或数据库错误导致⻚⾯⽆法加载
502
Bad Gateway
使⽤代理服务器时,代理服务器⽆法从上游服务器获取
有效响应
503
Service Unavailable
服务器维护或过载,暂时⽆法处理请求
重定向 相关状态码的表格:
状态码含义是否为临时重定向应用样例
301
Moved Permanently
否(永久重定向)
⽹站换域名后,⾃动跳转
到新域名;搜索引擎更新
⽹站链接时使⽤
302
Found 或 See Other
是(临时重定向)
⽤⼾登录成功后,重定向
到⽤⼾⾸⻚
307
Temporary Redirect
是(临时重定向)
临时重定向资源到新的位
置(较少使⽤)
308
Permanent Redirect
否(永久重定向)
永久重定向资源到新的位
置(较少使⽤)
关于重定向的验证,以301为代表
HTTP状态码301( 永久重定向 )和302( 临时重定向 )都依赖Location选项。以下是关于两者依赖
Location选项的详细说明:
HTTP状态码 301 (永久重定向)
当服务器返回HTTP 301状态码时,表⽰请求的资源已经被永久移动到新的位置。
在这种情况下,服务器会在响应中添加⼀个Location头部,⽤于指定资源的新位置。这个Location 头部包含了新的URL地址,浏览器会⾃动重定向到该地址。
例如,在HTTP响应中,可能会看到类似于以下的头部信息:
HTTP/1.1 301 Moved Permanently\r\n
Location: https://www.new-url.com\r\n
HTTP状态码 302 (临时重定向)
当服务器返回HTTP 302状态码时,表⽰请求的资源临时被移动到新的位置。
同样地,服务器也会在响应中添加⼀个Location头部来指定资源的新位置。浏览器会暂时使⽤新的 URL进⾏后续的请求,但不会缓存这个重定向。
例如,在HTTP响应中,可能会看到类似于以下的头部信息:
HTTP/1.1 302 Found\r\n
Location: https://www.new-url.com\r\n
总结:⽆论是HTTP 301还是HTTP 302重定向,都需要依赖Location选项来指定资源的新位置。这个 Location选项 是⼀个标准的HTTP响应头部,⽤于告诉浏览器应该将请求重定向到哪个新的URL地址。

HTTP常⻅Header

Content-Type: 数据类型(text/html等)
Content-Length: Body的⻓度
Host: 客⼾端告知服务器, 所请求的资源是在哪个主机的哪个端⼝上;
User-Agent: 声明⽤⼾的操作系统和浏览器版本信息;
Referer: 当前⻚⾯是从哪个⻚⾯跳转过来的;
Location: 搭配3xx状态码使⽤, 告诉客⼾端接下来要去哪⾥访问;
Cookie: ⽤于在客⼾端存储少量信息. 通常⽤于实现会话(session)的功能;
connection报头
HTTP中的 Connection 字段是HTTP报⽂头的⼀部分,它主要⽤于控制和管理客⼾端与服务器之间
的连接状态
核⼼作⽤
管理持久连接 : Connection 字段还⽤于管理持久连接(也称为⻓连接)。持久连接允许客⼾端
和服务器在请求/响应完成后不⽴即关闭TCP连接,以便在同⼀个连接上发送多个请求和接收多个响 应。
持久连接(⻓连接)
HTTP/1.1:在HTTP/1.1协议中,默认使⽤持久连接。当客⼾端和服务器都不明确指定关闭连接
时,连接将保持打开状态,以便后续的请求和响应可以复⽤同⼀个连接。
HTTP/1.0:在HTTP/1.0协议中,默认连接是⾮持久的。如果希望在HTTP/1.0上实现持久连接,需要在请求头中显式设置 Connection: keep-alive
语法格式
Connection: keep-alive :表⽰希望保持连接以复⽤TCP连接。
Connection: close :表⽰请求/响应完成后,应该关闭TCP连接。

简易HTTP服务器

实现⼀个最简单的HTTP服务器, 只在⽹⻚上输出 "hello world"; 只要我们按照HTTP协议的要求构造数据,就可以得到。
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void Usage() {
   printf("usage: ./server [ip] [port]\n");
  }
int main(int argc, char* argv[]) {
     if (argc != 3) {
        Usage();
       return 1;
    }
int fd = socket(AF_INET, SOCK_STREAM, 0);
   if (fd < 0) {
     perror("socket");
    return 1;
  }
  struct sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = inet_addr(argv[1]);
  addr.sin_port = htons(atoi(argv[2]));
int ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
if (ret < 0) {
 perror("bind");
return 1;
}
ret = listen(fd, 10);
 if (ret < 0) {
    perror("listen");
return 1;
}
 for (;;) {
 struct sockaddr_in client_addr;
   socklen_t len;
   int client_fd = accept(fd, (struct sockaddr*)&client_addr, &len);
if (client_fd < 0) {
     perror("accept");
    continue;
}
   char input_buf[1024 * 10] = {0}; // ⽤⼀个⾜够⼤的缓冲区直接把数据读完.
   ssize_t read_size = read(client_fd, input_buf, sizeof(input_buf) - 1);
   if (read_size < 0) {
    return 1;
}
     printf("[Request] %s", input_buf);
      char buf[1024] = {0};
       const char* hello = "<h1>hello world</h1>";
       sprintf(buf, "HTTP/1.0 200 OK\nContent-Length:%lu\n\n%s", strlen(hello),
hello);
    write(client_fd, buf, strlen(buf));
  }
    return 0;
}
编译, 启动服务. 在浏览器中输⼊ http://[ip]:[port], 就能看到显⽰的结果 "Hello World"
今天的内容分享就到这里,欢迎各位大佬来评论区指点。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值