news 2026/7/11 19:44:06

这次把 GET、POST、文件上传到响应处理的坑给你一次填平

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
这次把 GET、POST、文件上传到响应处理的坑给你一次填平

如果你也在技术这条路上向前走,关注我,愿我们能彼此陪伴,一起成为更好的自己 🌱

🗺️ 核心脉络
🔹 万物皆始于一个客户端实例

🔹 GET 请求:参数拼接的艺术

🔹 POST 四大金刚:表单、JSON、文件、原始数据

🔹 Header 与 Cookie 的正确注入方式

🔹 返回值处理:从入门到高手的飞跃

🔹 流式请求:大文件下载再也不怕

🔹 那些悄悄崩溃的细节与避坑指南

🏗️ 第一部分:万物始于客户端,但别乱来
前面咱们说过了,全局一个 AsyncClient 服用。这里再明确一下,任何请求都要从它发出。

import httpx

async with httpx.AsyncClient(base_url=“https://httpbin.org”, timeout=10.0) as client:

# 里面可以疯狂发请求

你会问:“不是说要全局单例吗,怎么又 async with 了?”
全局单例是在 FastAPI 里处理时,通过 lifespan 和 Depends 管理的。

这里为了演示方便,下面的很多例子我是用的 async with 写临时上下文,
但你心里要时刻清楚:生产环境保持单例,别模仿我这种临时创建。

🔗 第二部分:GET 请求——参数别只会拼在 URL 里
最基础,也是最常用的,发送 GET 请求:

resp = await client.get(“/get”)

重点在于 传参怎么传。如果你还这么写:

client.get(f"/get?name=张三&age=18") # 这要给个差评了

为什么?一是中文编码问题,二是拼接时很容易出错。
正确的姿势应该是用 params 字典来处理参数对象:

params = {“name”: “张三”, “age”: 18}

resp = await client.get(“/get”, params=params)

httpx 会自动帮你编码成 /get?name=%E5%BC%A0%E4%B8%89&age=18

而且还有个骚操作,就是你想同一个 key 传多个值?也是可以的,直接用列表:

params = {“tag”: [“python”, “fastapi”]}

结果: /get?tag=python&tag=fastapi

但这里要说个容易翻车的点:
params 里如果某个key的值是 None,默认会被忽略掉。如果你想要保留空字段,可以在传参时手动转成字符串来处理。

📬 第三部分:POST 请求——四大金刚怎么选
POST 请求的请求体类型有很多种,如果选错了,可能就被下游一巴掌呼回来了。

🔹 1. 表单数据(application/x-www-form-urlencoded)
如果要模拟页面表单的提交,这时候要用 data 参数来传字典,前提是你的参数是 key-value 类型的:

data = {“username”: “admin”, “password”: “123456”}

resp = await client.post(“/login”, data=data)

🔹 2. JSON 数据(application/json)
这也是最常见的 API 交互方式,用 json 参数直接传 Python 对象,特别是参数存在复杂类型时,httpx 会自动序列化并设好 Content-Type头信息。

payload = {“user_id”: 1, “items”: [“铅笔”, “橡皮”]}

resp = await client.post(“/order”, json=payload)

🔹 3. 文件上传(multipart/form-data)
传文件是个高频需求,但也是容易踩坑的重灾区。这时候要用 files 参数,它的值可以是文件对象或元组。

简单上传

files = {“avatar”: open(“photo.jpg”, “rb”)}

resp = await client.post(“/upload”, files=files)

自定义文件名和 MIME 类型

files = {

"report": ("2025年报.pdf", open("report.pdf", "rb"), "application/pdf")

}

resp = await client.post(“/upload”, files=files)

这里要注意:files 和 data 可以同时使用,但别和 json 混用,否则会冲突。

🔹 4. 原始二进制或纯文本
如果你是用 content 参数传入 bytes 或 str,httpx 会原样塞进请求体里。

raw_data = b’\x00\x01\x02’

resp = await client.post(“/binary”, content=raw_data)

🎩 第四部分:Header 与 Cookie,细节决定成败
如果要自定义 Header,可以直接传字典:

headers = {“X-API-Key”: “secret”, “User-Agent”: “MyApp/1.0”}

resp = await client.get(“/data”, headers=headers)

但要特别注意:通过 client.headers 设置的全局头,会和单次请求的 headers 合并,同名键单次请求的会覆盖全局的。
不得不说,这个设计还是很贴心的,自定义和默认项都能实现了。

Cookie 管理同样简单:

cookies = {“session_id”: “abc123”}

resp = await client.get(“/dashboard”, cookies=cookies)

但如果你需要持久化 Cookie(比如登录后维持会话),这时候可以用 http.CookieJar 绑定到客户端,我这里就不展开了,但你知道它有这个能力就行,需要的时候再深入研究。

📦 第五部分:返回值处理——别只看 resp.json()
拿到响应对象后,你应该根据 Content-Type 值选择合适的解析方法,可别二话不说全部 .json() 梭哈。

下面解释下常见的返回类型解析方式,以及应对哪种类型的返回值:

🔹 resp.status_code:检查状态码,好习惯是调用 resp.raise_for_status() 让 4xx/5xx 立即抛异常。

🔹 resp.text:返回解码后的字符串(默认用响应头里的编码,智能)。

🔹 resp.json():解析 JSON,返回字典或列表。如果响应体不是合法 JSON 格式,可能会爆 JSONDecodeError 错误,要小心。

🔹 resp.content:获取原始字节流,下载图片、视频等。

🔹 resp.headers:查看响应头,比如获取 Content-Type 的值等。

下面来个实战:检查状态码后再解析返回值。

resp = await client.get(“/user/1”)

if resp.status_code == 200:

user = resp.json()

else:

print(f"错误:{resp.status_code} - {resp.text}")

上面这些算是常用的功能都有了,但我想说,它的能力还不止哦!还有更优雅的流式读取,接着看。

🌊 第六部分:流式响应——大文件别想一次性吞进内存
下载大文件时,如果你直接傻傻地 content = resp.content,那几百兆或者上G的数据能直接撑爆内存。
这时候就必须用流式响应了,然后分片保存到本地文件中。

async with client.stream(“GET”, “/bigfile.zip”) as resp:

resp.raise_for_status() with open("download.zip", "wb") as f: async for chunk in resp.aiter_bytes(): f.write(chunk)

client.stream() 返回的响应对象不会立即读取所有数据,而是通过 aiter_bytes() 逐块读取,内存占用是恒定的一片的大小,这才是工业级的优雅。

同理,流式读取文本行,可以处理大型 JSON Lines,比如:日志文件

async with client.stream(“GET”, “/logs.txt”) as resp:

async for line in resp.aiter_lines(): print(line)

🚨 第七部分:要特别注意的那些悄悄崩溃的细节
🔹 超时一定要设死
前面强调过,再强调一次。客户端级别设:

client = httpx.AsyncClient(timeout=10.0) # 10秒总超时

你也可以精细控制连接、读取和写入的不同超时定义:

timeout = httpx.Timeout(5.0, connect=3.0, read=5.0, write=2.0)

🔹 代理配置不迷路
如果公司网络需要代理,那就直接传代理地址给它:

proxies = “http://proxy.com:8080”

client = httpx.AsyncClient(proxies=proxies)

🔹 重试不要靠手写
网络波动是永远不可避免的,但这时别急着手动写 try/except 循环。

httpx 可以结合 tenacity 库做自动重试,但更建议在 AsyncClient 的 transport 层加 httpx.HTTPTransport(retries=3),这是同步用法。
异步重试通常用中间件或依赖注入实现,这些先作个了解即可。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/11 19:44:05

3分钟掌握GBFR Logs伤害统计:新手必看的实时DPS监控完整指南

3分钟掌握GBFR Logs伤害统计:新手必看的实时DPS监控完整指南 【免费下载链接】gbfr-logs GBFR Logs lets you track damage statistics with a nice overlay DPS meter for Granblue Fantasy: Relink. 项目地址: https://gitcode.com/gh_mirrors/gb/gbfr-logs …

作者头像 李华
网站建设 2026/7/11 19:42:40

我的编程学习之旅:从零开始的探索与规划

一、自我介绍今天是我学习编程的第一天,我是一个对技术充满好奇与热情的探索者,也是准大一工科生。我可能没有丰富的项目经验,但我拥有强烈的求知欲和解决问题的决心。我相信,每一行代码都是通往新世界的桥梁,每一次调…

作者头像 李华
网站建设 2026/7/11 19:42:08

TAS5414C-Q1与STM32L041C6芯片对比:汽车音频与物联网应用

1. 两款芯片的基本定位与核心差异TAS5414C-Q1和STM32L041C6虽然都是嵌入式系统中常见的芯片,但它们的定位和功能差异极大。TAS5414C-Q1是德州仪器(TI)推出的一款专为汽车音响系统设计的4通道D类音频功率放大器,而STM32L041C6则是意法半导体(ST)的超低功耗…

作者头像 李华
网站建设 2026/7/11 19:42:06

PDF补丁丁:免费开源的一站式PDF工具箱,轻松解决文档处理难题

PDF补丁丁:免费开源的一站式PDF工具箱,轻松解决文档处理难题 【免费下载链接】PDFPatcher PDF补丁丁——PDF工具箱,可以编辑书签、剪裁旋转页面、解除限制、提取或合并文档,探查文档结构,提取图片、转成图片等等 项目…

作者头像 李华
网站建设 2026/7/11 19:39:34

MAX77654与STM32F410RB的低功耗电源管理方案

1. 项目背景与核心器件选型在嵌入式系统开发中,电源管理一直是决定产品可靠性和续航能力的关键因素。我们这次要实现的方案,采用了MAX77654电源管理IC与STM32F410RB微控制器的组合,这个搭配在低功耗物联网设备、便携式医疗仪器等领域有着广泛…

作者头像 李华