如何创建可以接受文件/表单或JSON主体的FastAPI端点?
我想在FastAPI中创建一个端点,该端点可能会接收 multipart/form-data
或JSON主体。有什么方法可以使这样的端点接受或检测正在接收哪种类型的数据?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
我想在FastAPI中创建一个端点,该端点可能会接收 multipart/form-data
或JSON主体。有什么方法可以使这样的端点接受或检测正在接收哪种类型的数据?
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(1)
选项1
您可以拥有a 依赖项函数,您将在其中检查
content-type 请求标头和使用starlette的方法来解析身体 。请注意,仅仅因为请求的
content-type
标题说,例如,application/json
,application/x-www-form-urlencoded
或muttipart/form-data
,并不总是意味着这是真的,或传入数据是有效的JSON,或者是文件和/或form-data。因此,您应该使用try-except
块在解析身体时捕获任何潜在错误。另外,您可能需要实施各种检查,以确保您获得正确的数据类型和所需的所有字段。对于JSON主体,您可以创建一个basemodel
并使用Pydantic的parse_obj
功能验证接收的字典(类似于此答案 a>)。关于file/form-data,您可以使用starlette的
对象直接直接
,更具体地说,request.form()
method to parse the body, which will return a工作示例
选项2
另一个选项将是具有单个端点,并将您的文件定义为
可选
(请查看 this答案和 content> content-typeapplication/x-www-form-urlencoded
或muttipart/form-data
(请注意,如果您期望接收任意文件或form-data,则宁愿使用上面的选项1 )。否则,如果每个定义的参数仍然无
(这意味着客户端未在请求正文中包含其中任何一个),那么这可能是JSON请求,因此,请通过尝试通过尝试确认这一点将请求尸体解析为JSON。工作示例
选项3
另一个选项将是定义两个单独的端点。一个用于处理JSON请求,另一个用于处理文件/表单数据请求。使用 middledware ,您可以检查输入请求是否指向您希望用户的路线要发送JSON或文件/表单数据(在下面的示例
/
路由)中,如果是的,请检查content-type
与上一个选项类似并重新路由请求/submitjson
或/subtsform
endpoint,相应地(您可以通过修改path> path
属性request.scope中的
,如此答案)。这种方法的优点是,它允许您照常定义端点,而不必担心请求中缺少所需字段,或者收到的数据不以预期格式。path
属性来执行此操作。工作示例
选项4
我还建议您查看这个答案,该答案提供了有关如何发送JSON身体的解决方案以及文件/form-data在相同的请求中一起,这可能会使您对要解决的问题有不同的看法。例如,将各种端点的参数声明为
可选
并检查已接收到哪些参数,哪些没有从客户端的请求中 - 以及使用Pydantic的model_validate_json()
方法要解析以form
参数传递的JSON字符串,可能是解决问题的另一种方法。有关更多详细信息和示例,请参见上面的链接答案。测试选项1、2& 3使用python请求
test.py
Option 1
You could have a dependency function, where you would check the value of the
Content-Type
request header and parse the body using Starlette's methods, accordingly. Note that just because a request'sContent-Type
header says, for instance,application/json
,application/x-www-form-urlencoded
ormultipart/form-data
, doesn't always mean that this is true, or that the incoming data is a valid JSON, or File(s) and/or form-data. Hence, you should use atry-except
block to catch any potential errors when parsing the body. Also, you may want to implement various checks to ensure that you get the correct type of data and all the fields that you expect to be required. For JSON body, you could create aBaseModel
and use Pydantic'sparse_obj
function to validate the received dictionary (similar to Method 3 of this answer).Regarding File/Form-data, you can use Starlette's
Request
object directly, and more specifically, therequest.form()
method to parse the body, which will return aFormData
object that is an immutable multidict (i.e.,ImmutableMultiDict
) containing both file uploads and text input. When you send alist
of values for someform
input, or a list offiles
, you can use the multidict'sgetlist()
method to retrieve thelist
. In the case of files, this would return alist
ofUploadFile
objects, which you can use in the same way as this answer and this answer to loop through the files and retrieve their content. Instead of usingrequest.form()
, you could also read the request body directly from thestream
and parse it using thestreaming-form-data
library, as demonstrated in this answer.Working Example
Option 2
Another option would be to have a single endpoint, and have your File(s) and/or Form data parameters defined as
Optional
(have a look at this answer and this answer for all the available ways on how to do that). Once a client's request enters the endpoint, you could check whether the defined parameters have any values passed to them, meaning that they were included in the request body by the client and this was a request having asContent-Type
eitherapplication/x-www-form-urlencoded
ormultipart/form-data
(Note that if you expected to receive arbitrary file(s) or form-data, you should rather use Option 1 above ). Otherwise, if every defined parameter was stillNone
(meaning that the client did not include any of them in the request body), then this was likely a JSON request, and hence, proceed with confirming that by attempting to parse the request body as JSON.Working Example
Option 3
Another option would be to define two separate endpoints; one to handle JSON requests and the other for handling File/Form-data requests. Using a middleware, you could check whether the incoming request is pointing to the route you wish users to send either JSON or File/Form data (in the example below that is
/
route), and if so, check theContent-Type
similar to the previous option and reroute the request to either/submitJSON
or/submitForm
endpoint, accordingly (you could do that by modifying thepath
property inrequest.scope
, as demonstrated in this answer). The advantage of this approach is that it allows you to define your endpoints as usual, without worrying about handling errors if required fields were missing from the request, or the received data were not in the expected format.Working Example
Option 4
I would also suggest you have a look at this answer, which provides solutions on how to send both JSON body and Files/Form-data together in the same request, which might give you a different perspective on the problem you are trying to solve. For instance, declaring the various endpoint's parameters as
Optional
and checking which ones have been received and which haven't from a client's request—as well as using Pydantic'smodel_validate_json()
method to parse a JSON string passed in aForm
parameter—might be another approach to solving the problem. Please see the linked answer above for more details and examples.Testing Options 1, 2 & 3 using Python requests
test.py