参考链接FastAPI-HTTPException网络协议-七层、五层、四层协议概念HTTP协议入门-阮一峰一、OSI七层网络模型标准OSI七层网络模型应用层Application、表示层Presentation、会话层Session、传输层Transport、网络层Network、数据链路层Data Link、物理层Physical。而实际使用提及的 TCP/IP 四层模型只不过是只使用了5 6 7层中的部分层1234层而已。下图可见HTTP协议是基于 TCP 的应用层协议。HTTP负责定义传输内容的格式和规则规定了请求和响应的结构以及各种请求方法的含义如POSTGET这样客户端和服务器就按照定好的规则组织和解析数据HTTP 协议生成的请求和响应数据会被封装到TCP数据包中进行传输TCP为HTTP的传输提供传输保障提供面向连接的可靠字节流服务确保数据完整有序到达对端。二、HTTP/1.1 协议a. 什么是 HTTPHTTPHyperText Transfer Protocol是Web浏览器和服务器间通信协议当浏览器输入网址调用API本质上是在发送HTTP请求服务器返回HTTP响应。HTTP/1.1 协议1997年发布虽然在2015年发布了 HTTP/2老设备不支持二进制格式抓包工具需要解码但是 HTTP/1.1 仍然是最流行的版本文本格式开发者友好抓包工具直接可读。b. HTTP/1.1 协议原始报文格式详解HTTP协议在应用层所有HTTP报文就是字符串使用 Postman 开源软件可以很方便的查看发送的HTTP报文长啥样用curl命令怎么发返回的响应长什么样子。[!NOTE]每个HTTP请求都会收到HTTP响应吗协议语义上每个成功被服务器接收并处理的请求都应返回一个响应至少有状态行/状态码与响应头特殊情况如HEAD、204 No Content、304 Not Modified没有响应体但仍有响应。实际网络层面若在服务器处理前或处理过程中发生中间设备断开超时崩溃都可能导致客户端拿不到响应HTTP 协议未规定响应的时间限制只要连接保持打开协议允许服务器过很久才返回响应。实际应用中超时来自各层的实现。所以客户端应该做好异常处理超时控制自己定超时时间。1请求格式Request包括请求行请求头请求体。GET /index.html HTTP/1.1 # ① 请求行格式为 METHOD SP REQUEST-TARGET SP HTTP-VERSION CRLF [必须是 ASCII 文本可视为字符串] Host: example.com # ② 请求头指定服务器域名 [必须是 ASCII 文本可视为字符串] User-Agent: curl/7.29.0 Accept: */* nameLiliage25 # ③ 可选请求体Request Body仅在 POST/PUT 中出现。[可以是任意二进制数据][!NOTE]一、请求行常见 METHODGET取资源POST提交数据PUT/DELETEREST 常用HEAD只请求头部OPTIONS查询服务器支持的方法二、请求头Host 必须存在HTTP/1.1 新要求键值对格式不区分大小写存在如下请求头可选键Header用途Host必填用于指定服务器域名有了Host字段就可以将请求发往同一台服务器上的不同网站为虚拟主机的兴起打下了基础。User-Agent浏览器/客户端标识Accept可接受返回类型Content-Type请求体类型AuthorizationToken、Basic Auth 等Connection: close客户端在最后一个请求时发送Connection: close明确要求服务器关闭TCP连接。三、请求体body可选请求体Request Body仅在 POST/PUT 方法出现GET 请求通常没有请求体。2响应格式Response包括响应行响应头响应体。HTTP/1.1 200 OK # ① 响应行含状态码 Content-Type: text/html; charsetUTF-8 # ② 响应头也是键值对格式 Content-Length: 1256 Connection: keep-alive html.../html # ③ 响应体任意数据HTML、JSON、图片等[!NOTE]一、响应行响应行中有如下状态码类别范围含义常见例子1xx100-199信息性很少用100 Continue2xx200-299成功200 OK,201 Created3xx300-399重定向301 Moved Permanently,302 Found4xx400-499客户端 Client 错误你搞错了400 Bad Request,401 Unauthorized,403 Forbidden,404 Not Found5xx500-599服务器 Server 错误我搞错了500 Internal Server Error,502 Bad Gateway其中 FastAPI 默认如果在方法中通过return正常返回则响应的状态 码为200。但是你可以在想要返回异常时显式改变状态码HTTPException(status_code403, ...)。二、响应头Headers 是键值对形式的元信息。用于告诉客户端如何处理响应。常见响应头Header作用Content-Type来告诉接收方“如何解释 body”)如 字符串JSON / HTML / XML和非字符串如 图片PNG/JPG文件PDF/ZIP视频/音频流。Content-Type的格式和值不是随便写的而是全球统一的标准命名体系。Content-Length响应体的字节长度Cache-Control是否缓存、缓存多久Set-Cookie服务端设置 CookieServer服务端类型Connectionkeep-alive / closeContent-Type的常见标准 MIME 类型写法有类别MIME 类型说明是否文本JSONapplication/jsonJSON 数据✅HTMLtext/htmlHTML 文档✅PNG 图片image/pngPNG 格式图片❌二进制流application/octet-stream通用二进制未知类型❌FastAPI 会自动设置合适的 Headers。比如当你返回 JSON 时它会自动加Content-Type: application/json。当你抛出HTTPExceptionFastAPI 也会自动设置正确的Content-Type和状态码。三、响应体body符合响应头中Content-Type的类型的数据客户端应当根据该类型解析。❗️HTTP/1.1 连接特点非常关键默认是 Keep-Alive 长连接一个 TCP 连接可发送多个请求。但不能多路复用必须按顺序返回队头阻塞 Head-of-line blocking三、Python 实现 HTTP clinet 与 FastAPI server 通信FastAPI server 一侧有 unvicorn 服务器实现回复几乎不涉及HTTP的代码。约定请求和响应JSON格式如下class RunScriptRequest(BaseModel): script_name: str Field(..., description逻辑脚本名) args: list[str] Field(default_factorylist) timeout: int | None Field(None, description脚本超时时间秒)class RunScriptResponse(BaseModel): status: str exit_code: int stdout: str stderr: str duration_ms: int使用 Python requests 包的最小实现发送 HTTP POST 请求只用requests发送 JSON、拿到响应、做基本校验与拆包在拆包时用 Pydantic 严格校验响应结构importrequestsfrompydanticimportBaseModel,Field,ValidationErrorfromtypingimportList,Optional# ---- 契约模型与你给出的一致 ----classRunScriptRequest(BaseModel):script_name:strField(...,description逻辑脚本名)args:List[str]Field(default_factorylist)timeout:Optional[int]Field(None,description脚本超时时间秒)classRunScriptResponse(BaseModel):status:strexit_code:intstdout:strstderr:strduration_ms:intdefrun_script_with_validation(script_name:str,args:list[str]|NoneNone,timeout_seconds:int|NoneNone,request_timeout:float5.0,)-RunScriptResponse: 发送请求并用 Pydantic 严格校验响应。返回 RunScriptResponse 实例。 reqRunScriptRequest(script_namescript_name,argsargsor[],timeouttimeout_seconds)try:resprequests.post(urlhttps://your.api.host/run-script,# TODO: 替换为真实接口地址jsonrequest_data,# 要发送的数据自动将 Python 字典转换为 JSON 格式自动设置Content-Type 为 application/josntimeouttimeout#超时设置秒)resp.raise_for_status()# 非 2xx 会抛 HTTPErrorexceptrequests.exceptions.Timeout:raiseRuntimeError(请求超时)exceptrequests.exceptions.ConnectionError:print(连接错误)exceptrequests.exceptions.RequestExceptionase:raiseRuntimeError(f请求失败{e})# 解析 JSONtry:dataresp.json()# 将JSON格式转换为Python数据结构exceptValueError:raiseRuntimeError(响应内容不是合法 JSON)try:returnRunScriptResponse.model_validate(data)# 验证格式是否符合预期exceptValidationErrorasve:# 打印/记录详细校验错误raiseRuntimeError(f响应结构不符合契约{ve})if__name____main__:resultrun_script_with_validation(daily_job,args[--dry-run],timeout_seconds60)# 直接面向字段有类型提示print(状态,result.status)print(退出码,result.exit_code)print(耗时(ms),result.duration_ms)print(标准输出\n,result.stdout)print(标准错误\n,result.stderr)[!NOTE]requests基于urllib3这个强大而灵活的客户端库自动完成TCP建立DNS解析TLS握手与整数校验无需手写无需自己写 socket/TCP但如果希望复用TCP 连接以提升性能可使用requests.Session()上面的requests.post中的jsonrequest_data为自动请求头的方法何以替换成自动方法如下importjson headers{Content-Type:application/json}requests.post(url,datajson.dumps(data),headersheaders)
HTTP协议介绍
参考链接FastAPI-HTTPException网络协议-七层、五层、四层协议概念HTTP协议入门-阮一峰一、OSI七层网络模型标准OSI七层网络模型应用层Application、表示层Presentation、会话层Session、传输层Transport、网络层Network、数据链路层Data Link、物理层Physical。而实际使用提及的 TCP/IP 四层模型只不过是只使用了5 6 7层中的部分层1234层而已。下图可见HTTP协议是基于 TCP 的应用层协议。HTTP负责定义传输内容的格式和规则规定了请求和响应的结构以及各种请求方法的含义如POSTGET这样客户端和服务器就按照定好的规则组织和解析数据HTTP 协议生成的请求和响应数据会被封装到TCP数据包中进行传输TCP为HTTP的传输提供传输保障提供面向连接的可靠字节流服务确保数据完整有序到达对端。二、HTTP/1.1 协议a. 什么是 HTTPHTTPHyperText Transfer Protocol是Web浏览器和服务器间通信协议当浏览器输入网址调用API本质上是在发送HTTP请求服务器返回HTTP响应。HTTP/1.1 协议1997年发布虽然在2015年发布了 HTTP/2老设备不支持二进制格式抓包工具需要解码但是 HTTP/1.1 仍然是最流行的版本文本格式开发者友好抓包工具直接可读。b. HTTP/1.1 协议原始报文格式详解HTTP协议在应用层所有HTTP报文就是字符串使用 Postman 开源软件可以很方便的查看发送的HTTP报文长啥样用curl命令怎么发返回的响应长什么样子。[!NOTE]每个HTTP请求都会收到HTTP响应吗协议语义上每个成功被服务器接收并处理的请求都应返回一个响应至少有状态行/状态码与响应头特殊情况如HEAD、204 No Content、304 Not Modified没有响应体但仍有响应。实际网络层面若在服务器处理前或处理过程中发生中间设备断开超时崩溃都可能导致客户端拿不到响应HTTP 协议未规定响应的时间限制只要连接保持打开协议允许服务器过很久才返回响应。实际应用中超时来自各层的实现。所以客户端应该做好异常处理超时控制自己定超时时间。1请求格式Request包括请求行请求头请求体。GET /index.html HTTP/1.1 # ① 请求行格式为 METHOD SP REQUEST-TARGET SP HTTP-VERSION CRLF [必须是 ASCII 文本可视为字符串] Host: example.com # ② 请求头指定服务器域名 [必须是 ASCII 文本可视为字符串] User-Agent: curl/7.29.0 Accept: */* nameLiliage25 # ③ 可选请求体Request Body仅在 POST/PUT 中出现。[可以是任意二进制数据][!NOTE]一、请求行常见 METHODGET取资源POST提交数据PUT/DELETEREST 常用HEAD只请求头部OPTIONS查询服务器支持的方法二、请求头Host 必须存在HTTP/1.1 新要求键值对格式不区分大小写存在如下请求头可选键Header用途Host必填用于指定服务器域名有了Host字段就可以将请求发往同一台服务器上的不同网站为虚拟主机的兴起打下了基础。User-Agent浏览器/客户端标识Accept可接受返回类型Content-Type请求体类型AuthorizationToken、Basic Auth 等Connection: close客户端在最后一个请求时发送Connection: close明确要求服务器关闭TCP连接。三、请求体body可选请求体Request Body仅在 POST/PUT 方法出现GET 请求通常没有请求体。2响应格式Response包括响应行响应头响应体。HTTP/1.1 200 OK # ① 响应行含状态码 Content-Type: text/html; charsetUTF-8 # ② 响应头也是键值对格式 Content-Length: 1256 Connection: keep-alive html.../html # ③ 响应体任意数据HTML、JSON、图片等[!NOTE]一、响应行响应行中有如下状态码类别范围含义常见例子1xx100-199信息性很少用100 Continue2xx200-299成功200 OK,201 Created3xx300-399重定向301 Moved Permanently,302 Found4xx400-499客户端 Client 错误你搞错了400 Bad Request,401 Unauthorized,403 Forbidden,404 Not Found5xx500-599服务器 Server 错误我搞错了500 Internal Server Error,502 Bad Gateway其中 FastAPI 默认如果在方法中通过return正常返回则响应的状态 码为200。但是你可以在想要返回异常时显式改变状态码HTTPException(status_code403, ...)。二、响应头Headers 是键值对形式的元信息。用于告诉客户端如何处理响应。常见响应头Header作用Content-Type来告诉接收方“如何解释 body”)如 字符串JSON / HTML / XML和非字符串如 图片PNG/JPG文件PDF/ZIP视频/音频流。Content-Type的格式和值不是随便写的而是全球统一的标准命名体系。Content-Length响应体的字节长度Cache-Control是否缓存、缓存多久Set-Cookie服务端设置 CookieServer服务端类型Connectionkeep-alive / closeContent-Type的常见标准 MIME 类型写法有类别MIME 类型说明是否文本JSONapplication/jsonJSON 数据✅HTMLtext/htmlHTML 文档✅PNG 图片image/pngPNG 格式图片❌二进制流application/octet-stream通用二进制未知类型❌FastAPI 会自动设置合适的 Headers。比如当你返回 JSON 时它会自动加Content-Type: application/json。当你抛出HTTPExceptionFastAPI 也会自动设置正确的Content-Type和状态码。三、响应体body符合响应头中Content-Type的类型的数据客户端应当根据该类型解析。❗️HTTP/1.1 连接特点非常关键默认是 Keep-Alive 长连接一个 TCP 连接可发送多个请求。但不能多路复用必须按顺序返回队头阻塞 Head-of-line blocking三、Python 实现 HTTP clinet 与 FastAPI server 通信FastAPI server 一侧有 unvicorn 服务器实现回复几乎不涉及HTTP的代码。约定请求和响应JSON格式如下class RunScriptRequest(BaseModel): script_name: str Field(..., description逻辑脚本名) args: list[str] Field(default_factorylist) timeout: int | None Field(None, description脚本超时时间秒)class RunScriptResponse(BaseModel): status: str exit_code: int stdout: str stderr: str duration_ms: int使用 Python requests 包的最小实现发送 HTTP POST 请求只用requests发送 JSON、拿到响应、做基本校验与拆包在拆包时用 Pydantic 严格校验响应结构importrequestsfrompydanticimportBaseModel,Field,ValidationErrorfromtypingimportList,Optional# ---- 契约模型与你给出的一致 ----classRunScriptRequest(BaseModel):script_name:strField(...,description逻辑脚本名)args:List[str]Field(default_factorylist)timeout:Optional[int]Field(None,description脚本超时时间秒)classRunScriptResponse(BaseModel):status:strexit_code:intstdout:strstderr:strduration_ms:intdefrun_script_with_validation(script_name:str,args:list[str]|NoneNone,timeout_seconds:int|NoneNone,request_timeout:float5.0,)-RunScriptResponse: 发送请求并用 Pydantic 严格校验响应。返回 RunScriptResponse 实例。 reqRunScriptRequest(script_namescript_name,argsargsor[],timeouttimeout_seconds)try:resprequests.post(urlhttps://your.api.host/run-script,# TODO: 替换为真实接口地址jsonrequest_data,# 要发送的数据自动将 Python 字典转换为 JSON 格式自动设置Content-Type 为 application/josntimeouttimeout#超时设置秒)resp.raise_for_status()# 非 2xx 会抛 HTTPErrorexceptrequests.exceptions.Timeout:raiseRuntimeError(请求超时)exceptrequests.exceptions.ConnectionError:print(连接错误)exceptrequests.exceptions.RequestExceptionase:raiseRuntimeError(f请求失败{e})# 解析 JSONtry:dataresp.json()# 将JSON格式转换为Python数据结构exceptValueError:raiseRuntimeError(响应内容不是合法 JSON)try:returnRunScriptResponse.model_validate(data)# 验证格式是否符合预期exceptValidationErrorasve:# 打印/记录详细校验错误raiseRuntimeError(f响应结构不符合契约{ve})if__name____main__:resultrun_script_with_validation(daily_job,args[--dry-run],timeout_seconds60)# 直接面向字段有类型提示print(状态,result.status)print(退出码,result.exit_code)print(耗时(ms),result.duration_ms)print(标准输出\n,result.stdout)print(标准错误\n,result.stderr)[!NOTE]requests基于urllib3这个强大而灵活的客户端库自动完成TCP建立DNS解析TLS握手与整数校验无需手写无需自己写 socket/TCP但如果希望复用TCP 连接以提升性能可使用requests.Session()上面的requests.post中的jsonrequest_data为自动请求头的方法何以替换成自动方法如下importjson headers{Content-Type:application/json}requests.post(url,datajson.dumps(data),headersheaders)