FastAPI+React全栈开发15 让我们构建一个展示API
Chapter03 Getting Started with FastAPI
15 Let’s Build a showcase API
FastAPI+React全栈开发15 让我们构建一个展示API
REST APIs are all about cycles of HTTP requests and responses, it is the engine that powers the web and is implemented in every web framework, speaking the language of the web, the HTTP protocol. I feel that the best way to showcase FastAPI’s capabilities is to dive right in and create simple endpoints and focus on specific parts of code that achieve the desired functionalities. Rather than the usual CRUD operations that we will implement in the forthcoming chapters, I want to focus on the process of retrieving and setting request and response elements.
REST api都是关于HTTP请求和响应的循环,它是为web提供动力的引擎,并且在每个web框架中实现,使用web的语言,HTTP协议。我觉得展示FastAPI功能的最好方法是直接进入并创建简单的端点,并专注于实现所需功能的代码的特定部分。与我们将在接下来的章节中实现的常规CRUD操作不同,我想重点关注检索和设置请求和响应元素的过程。
Retrieving path and query parameters
The first endpoint will be for retrieving a car by its unique ID:
第一个端点将用于通过其唯一ID检索汽车:
main.py
from fastapi import FastAPIapp = FastAPI()@app.get("/car/{id}")
async def get_car(id: str):return {"id": id}if __name__ == '__main__':import uvicornuvicorn.run(app)
The first line of the preceding snippet defines a dynamic path, the static part is defined with car/, while {id} is a standard Python string formatted dynamic parameter in the sense that it can be anything, a string or a number.
前面代码片段的第一行定义了一个动态路径,静态部分用car/定义,而{id}是一个标准的Python字符串格式的动态参数,它可以是任何东西,字符串或数字。
Let’s try it out and test the endpoint with an ID equal to 1:
让我们尝试一下,并测试ID等于1的端点:
http localhost:8000/car/1
HTTP/1.1 200 OK
content-length: 10
content-type: application/json
date: Wed, 27 Mar 2024 09:48:46 GMT
server: uvicorn{"id": "1"
}
We got our JSON response back, but here, 1 in the response is a string. You can try this same route with an ID equal to a string.
我们得到了JSON响应,但这里,响应中的1是字符串。你可以用一个字符串ID来尝试相同的路由。
http localhost:8000/car/abc
HTTP/1.1 200 OK
content-length: 12
content-type: application/json
date: Wed, 27 Mar 2024 09:50:34 GMT
server: uvicorn{"id": "abc"
}
FastAPI doesn’t complain and returns our string, which was provided as part of the dynamic parameter, but this is where Python’s newer features come into play. Enter type hinting.
FastAPI没有抱怨,并返回我们的字符串,它是作为动态参数的一部分提供的,但这是Python的新特性发挥作用的地方。输入类型提示。
Returning to our FastAPI route (or endpoint), to make the car ID become an integer, it is enough to hint at the type of the variable parameter. The endpoint will look like this.
回到我们的FastAPI路由(或端点),要使汽车ID变成整数,只要提示变量参数的类型就足够了。端点是这样的。
from fastapi import FastAPIapp = FastAPI()@app.get("/car/{id}")
async def get_car(id: int):return {"id": id}if __name__ == '__main__':import uvicornuvicorn.run(app)
I have given it a new path: /car/{id} . Apart from the name of the function, the only difference is in the argument, the semicolon followed by int means that we expect aninteger, but FastAPI takes this very seriously.
我给了它一个新的路径:/car/{id}。除了函数的名称之外,唯一的区别在于参数,分号后面跟着int意味着我们期望一个整数,但是FastAPI非常重视这一点。
If we take a look at the interactive documention at http://llocalhost:8000/docs, and try to insert a string in the id field for the /car endpoint, we will get an error and will not be able to proceed.
如果我们查看http://llocalhost:8000/docs上的交互式文档,并尝试在/car端点的id字段中插入一个字符串,我们将得到一个错误,并且无法继续。
http localhost:8000/car/abc
HTTP/1.1 422 Unprocessable Entity
content-length: 201
content-type: application/json
date: Wed, 27 Mar 2024 09:58:10 GMT
server: uvicorn{"detail": [{"input": "abc","loc": ["path","id"],"msg": "Input should be a valid integer, unable to parse string as an integer","type": "int_parsing","url": "https://errors.pydantic.dev/2.5/v/int_parsing"}]
}
If we try it out in our REST client and test the /car route by passing it a string, we will see that FastAPI is yelling at us, but this is for our own good! We got several useful messages. First, FastAPI set the status code for us correctly, that is 422 Unprocessable Entity, and in the body of the response, it pointed out what the problem was, the value is not valid integer. It also gives us the location where the error occurred, in the path, that is, the id part. This is a trivial example, but imagine that you are sending a complex request with a complicated path, several query strings, and maybe additional information in the header. Using type hinting quickly solves these problems.
如果我们在REST客户端中尝试它,并通过传递一个字符串来测试/car路由,我们将看到FastAPI对我们大喊大叫,但这是为了我们自己好!我们收到了几条有用的信息。首先,FastAPI为我们正确地设置了状态码,即422 Unprocessable Entity,并且在响应的主体中指出了问题所在,值不是有效的整数。它还为我们提供了错误发生的位置,在路径中,即id部分。这是一个简单的例子,但是想象一下,您正在发送一个复杂的请求,该请求具有复杂的路径、几个查询字符串,并且头中可能还有其他信息。使用类型提示可以快速解决这些问题。
If you try to access the endpoint without specifying any ID, you will get yet another error.
如果您尝试在不指定任何ID的情况下访问端点,您将得到另一个错误。
http get localhost:8000/car
HTTP/1.1 404 Not Found
content-length: 22
content-type: application/json
date: Wed, 27 Mar 2024 11:42:03 GMT
server: uvicorn{"detail": "Not Found"
}
FastAPI has, again, correctly set the status code giving us a nice 404 Not Found error, and repeated this message in the body. The endpoint that we hit does not exist, we need to pecify a value after the slash.
FastAPI再次正确地设置了状态码,给我们一个很好的404 Not Found错误,并在body中重复此消息。我们命中的端点不存在,我们需要在斜杠后面指定一个值。
Situations may arise where you have similar paths, but one of them is dynamic, while the other one is static. A typical case could be an application that has numerous users, hitting the API at the URL defined by /users/id would give you some information about the user with the selected ID, while /users/me would typically be an endpoint that displays your information and allows you to modify it in some way.
可能会出现这样的情况:您有相似的路径,但其中一个是动态的,而另一个是静态的。典型的情况可能是,一个应用程序有许多用户,在/users/id定义的URL上点击API,您将获得关于具有所选id的用户的一些信息,而/users/me通常是一个端点,它显示您的信息并允许您以某种方式修改它。
In these situations, it is important to remember that, like in other web frameworks, order matters.
在这些情况下,重要的是要记住,就像在其他web框架中一样,顺序很重要。
The following piece of code will not yield the desired results.
下面的代码段不会产生期望的结果。
from fastapi import FastAPIapp = FastAPI()@app.get("/car/{id}")
async def get_car(id: str):return {"id": id}@app.get("/car/yellow")
async def get_car():return {"abc": "yellow"}if __name__ == '__main__':import uvicornuvicorn.run(app)
http get localhost:8000/car/333
HTTP/1.1 200 OK
content-length: 12
content-type: application/json
date: Wed, 27 Mar 2024 11:48:27 GMT
server: uvicorn{"id": "333"
}
http get localhost:8000/car/yellow
这里的路由被 /car/{id} 捕获了,没有走 /car/yellow 的逻辑。
HTTP/1.1 200 OK
content-length: 15
content-type: application/json
date: Wed, 27 Mar 2024 11:48:52 GMT
server: uvicorn{"id": "yellow"
}
FastAPI allows us to solve this at the path level by letting us create a class based on Enum for the account type. This class defines all the possible values for the account variable. In our case, there are just two, free and pro.
FastAPI允许我们在路径级别解决这个问题,它允许我们为account类型创建一个基于Enum的类。这个类为帐户变量定义了所有可能的值。在我们的例子中,只有两个,免费和专业。
from fastapi import FastAPI
from enum import Enumapp = FastAPI()class AccountEnum(str, Enum):FREE = "free"PRO = "pro"
Finally, in the actual endpoint, we combine this class with the utilities from the Path function (do not forget to import it along with FastAPI from fastapi!).
最后,在实际的端点中,我们将这个类与Path函数中的实用程序结合起来(不要忘记将它与FastAPI一起从FastAPI中导入!)
from fastapi import FastAPI, Path
from enum import Enumapp = FastAPI()class AccountEnum(str, Enum):FREE = "free"PRO = "pro"@app.get("/account/{acc_type}/{months}")
async def get_account_enum(acc_type: AccountEnum, months: int = Path(..., ge=3, le=12)):return {"message": "Account Enum","acc_type": acc_type,"months": months,}if __name__ == '__main__':import uvicornuvicorn.run(app)
FastAPI was able to pack a lot of punch in the preceding code: by setting the type of the acc_type part of the path to our previously defined class, we ensured that only the free or pro value can be passed. The months variable, however, is handled by the Path utility function.
FastAPI能够在前面的代码中添加大量的内容:通过将路径的acc_type部分的类型设置为我们之前定义的类,我们确保只能传递free或pro值。然而,months变量是由Path实用函数处理的。
As for other topics in this part, I strongly advise you to head over to the excellent documentation site and see what other options are avvailable, in this case, the Path function received three parameters. The three dots mean that the value is required and that no default value has been provided, get=3 means that the value can be greater or equal to 3, while le=12 means that it can be smaller or equal to 12.
至于本部分中的其他主题,我强烈建议您前往优秀的文档站点,查看是否有其他可用的选项,在本例中,Path函数接收了三个参数。三个点表示该值是必需的,并且没有提供默认值,get=3表示该值可以大于或等于3,le=12表示该值可以小于或等于12。
With that, we’ve learned how to validate, restrict, and order our path parameters. Now, let’s look at the query parameters. Query parameters are added at the end of the URL by using the ?min_price=2000&max_price=4000 format.
至此,我们已经学习了如何验证、限制和排序路径参数。现在,让我们看一下查询参数。通过使用?min_price=2000&max_price=4000格式在URL的末尾添加查询参数。
The question mark in the previous expression is a separator that tells us where the query string begins, while the ampersands, &, allow us to add more than one assignment (the equals signs, =).
前一个表达式中的问号是一个分隔符,告诉我们查询字符串从哪里开始,而&号允许我们添加多个赋值(等号=)。
Query parameters are usually used to apply filters, sort, order, or limit query sets, apply paginations to a long list of results, and similar tasks. FastAPI treats them similarly to path parameters. They will be, so to say, automatically picked up by FastAPI and available for processing in our endpoint functions.
查询参数通常用于应用筛选器、排序、排序或限制查询集、对长结果列表应用分页以及类似的任务。FastAPI将它们视为路径参数。可以说,它们将被FastAPI自动拾取,并可在端点函数中进行处理。
Let’s create a simple endpoint that accepts two query parameters for the minimum and the maximum prices of the car.
让我们创建一个简单的端点,它接受两个查询参数,分别表示汽车的最低价格和最高价格。
from fastapi import FastAPIapp = FastAPI()@app.get("/cars/price")
async def get_cars(min_price: int = 0, max_price: int = 100000):return {"message": "get_cars","min_price": min_price,"max_price": max_price,}if __name__ == '__main__':import uvicornuvicorn.run(app)
Let’s test this endpoint with HTTPie.
让我们用HTTPie测试这个端点。
http "localhost:8000/cars/price?min_price=2000&max_price=4000"
HTTP/1.1 200 OK
content-length: 56
content-type: application/json
date: Wed, 27 Mar 2024 15:06:12 GMT
server: uvicorn{"max_price": 4000,"message": "get_cars","min_price": 2000
}
Of course, this particular solution is not very good, we do not ensure the basic condition that the minimum price should be lower than the maximum price, but that can easily be handled by Pydantic object-level validation.
当然,这种特殊的解决方案不是很好,我们不保证最低价格应该低于最高价格的基本条件,但这可以很容易地通过Pydantic对象级验证来处理。
FastAPI picked up our query parameters and performed the same parsing and validation checks it did previously. It is worth mentioning that FastAPI provides the Query function, which is very similar to the Path function that we used previously, we can use the greater than, less than, or equal conditions, as well as set default values.
FastAPI获取我们的查询参数,并执行与之前相同的解析和验证检查。值得一提的是,FastAPI提供了Query函数,它与我们之前使用的Path函数非常相似,我们可以使用大于、小于或等于条件,也可以设置默认值。
With that, we’ve seen how FastAPI enables us to work with data that is passed through the path and query parameters, as well as the tools it uses under the hood to perform parsing and validation as soon as possible. Now, let’s examine the main data vehicle of REST APIs: the request body.
至此,我们已经看到了FastAPI如何使我们能够处理通过路径和查询参数传递的数据,以及它在底层使用的工具,以便尽快执行解析和验证。现在,让我们检查一下REST api的主要数据载体:请求体。
The request body - the bulk of the data
REST APIs enable two-way communicaton between a client, usually a web browser or similar device and an API server. The bulk of this data is carried over in the request and response body. A request body consists of the data that’s sent from the client to our API, if there is such data, while the response body is the data sent from the API server to our client(s). This data can be encoded in various ways (XML was quite popular 15 years ago, for example) but in this book, we will consider exclusively JavaScript Object Notation (JSON) since it is used everywhere and it plays exceptionlly nicely with our database solution of choice, MongoDB.
REST API支持客户端(通常是web浏览器或类似设备)与API服务器之间的双向通信。这些数据的大部分在请求和响应体中传递。请求体包含从客户端发送到我们的API的数据(如果有这样的数据),而响应体是从API服务器发送到我们的客户端的数据。这些数据可以以各种方式编码(例如,XML在15年前非常流行),但在本书中,我们将只考虑JavaScript对象表示法(JSON),因为它无处不在,而且它与我们选择的数据库解决方案MongoDB配合得非常好。
When sending data, we should always use POST requests to create new resources, PUT and PATCH to update resources, and the DELETE method to delete. Since the body of a request can and will contain raw data, in our case, MongoDB documents or arrays of documents, we will see how we can leverage the power of Pydantic models to our benefit. But first, let’s see how the mechanism works, without any validation or modeling.
在发送数据时,我们应该总是使用POST请求来创建新的资源,使用PUT和PATCH来更新资源,使用DELETE方法来删除资源。由于请求的主体可以并且将包含原始数据,在本例中是MongoDB文档或文档数组,因此我们将看到如何利用Pydantic模型的强大功能。但首先,让我们看看机制是如何工作的,没有任何验证或建模。
In the following snippet for a hypothetical endpoint that would be used to insert new cars in our future database, we pass just the generic request body as the data. We expect it to be a dictionary.
在下面的代码片段中,假设端点将用于在未来的数据库中插入新车,我们只传递通用请求体作为数据。我们希望它是一本字典。
from typing import Dictfrom fastapi import FastAPI, Bodyapp = FastAPI()@app.post("/cars")
async def new_car(data: Dict = Body(...)):return dataif __name__ == '__main__':import uvicornuvicorn.run(app)
Intuitively, you may have guessed that the Body function is somewhat similar to the previously introduced Path and Query functions, yet there is a difference, when working with the request body, this function is mandatory.
直观地,您可能已经猜到Body函数与前面介绍的Path和Query函数有些相似,但是有一个区别,在处理请求体时,这个函数是强制性的。
The three dots indicate that the body is required (you must send something), but this is the only requirement. Let’s try to insert a car (a Fiat 500, made in 2015).
这三个点表示正文是必需的(你必须发送一些东西),但这是唯一的要求。让我们试着插入一辆汽车(菲亚特500,2015年生产)。
http POST "localhost:8000/cars" brand="FIAT" model="500" year=2015
HTTP/1.1 200 OK
content-length: 44
content-type: application/json
date: Wed, 27 Mar 2024 15:22:26 GMT
server: uvicorn{"brand": "FIAT","model": "500","year": "2015"
}
Again, FastAPI functions do the heavy lifting for us, we were able to retrieve all the data that was passed to the request body and make it available to our function for further processing, database insertion, optional preprocessing, and so on. On the other hand, we could have passed just about any key-value pairs to the body. For example, we could set the number of legs to 4(cars do not have legs, yet), and it would make its way into the request body, disregarding modern car engineering.
同样,FastAPI函数为我们完成了繁重的工作,我们能够检索传递给请求体的所有数据,并使其可用于我们的函数进行进一步处理、数据库插入、可选预处理等。另一方面,我们可以将任何键值对传递给主体。例如,我们可以将腿的数量设置为4(汽车还没有腿),并且它将进入请求体,而不考虑现代汽车工程。
Keen observers may have notice that while all went well, FastAPI sent us a 200 response status again, even through a 201 Resource Created error may have been more appropriate and, well, exact. We could have had some MongoDB insertion at the end of the function, after all. Do not worry, we will see how easy it is to modify the response body as well, but for now, let’s see why Pydantic shines when it comes to request bodies.
敏锐的观察者可能已经注意到,虽然一切顺利,FastAPI再次发送给我们一个200响应状态,即使201资源创建错误可能更合适,更准确。毕竟,我们可以在函数的末尾插入一些MongoDB。不要担心,我们将看到修改响应体是多么容易,但是现在,让我们看看Pydantic在请求体方面为什么如此出色。
Let’s say that we wawnt to enforce a certain structure for our request body. After all, we cannot allow users to send arbitrary fields and data and bomb our precious POST endpoint. To create new car entries, we only want the brand, model, and production year fields.
假设我们想要为请求体强制执行某种结构。毕竟,我们不能允许用户发送任意字段和数据,从而破坏我们宝贵的POST端点。要创建新的汽车条目,我们只需要品牌、型号和生产年份字段。
We will create a simple Pydantic model with the desired types.
我们将使用所需的类型创建一个简单的Pydantic模型。
from fastapi import FastAPI, Body
from pydantic import BaseModelclass InsertCar(BaseModel):brand: strmodel: stryear: intapp = FastAPI()@app.post("/cars")
async def new_car(data: InsertCar = Body(...)):return dataif __name__ == '__main__':import uvicornuvicorn.run(app)
By now, you already know that the first two parameters are expected to be strings, while the year must be an integer, all of them are required.
到目前为止,您已经知道前两个参数应该是字符串,而年份必须是整数,所有参数都是必需的。
Now, if we try to post the same data that we did previously but with additional fields, we will only get these three fields back. Also, these fields will go through Pydantic parsing and validation and throw meaningful error messages if something does not conform to the data specification.
现在,如果我们尝试发布与之前相同的数据,但添加了额外的字段,我们将只得到这三个字段。此外,这些字段将经过Pydantic解析和验证,如果不符合数据规范,则抛出有意义的错误消息。
I encourage you to play with this endpoint and try different post data combinations. The following is an example.
我鼓励您使用这个端点并尝试不同的post数据组合。示例如下:
http POST "localhost:8000/cars" brand="FIAT" model="500" year=2015
HTTP/1.1 200 OK
content-length: 42
content-type: application/json
date: Wed, 27 Mar 2024 15:34:34 GMT
server: uvicorn{"brand": "FIAT","model": "500","year": 2015
}
This combination of Pydantic model validation and the Body function provides all the necessary flexibility when working with request data. This is because you can combine them and pass different pieces of information using the same request bus ride, so to speak.
Pydantic模型验证和Body函数的这种组合在处理请求数据时提供了所有必要的灵活性。这是因为可以将它们组合起来,并使用相同的请求总线传递不同的信息。
If we wanted to pass a user with a promo code along with the new car data, we could try to define a Pydantic model for the user and extract the promo code with the Body function. First, let’s define a minimal user model.
如果我们希望向用户传递带有促销码的新车数据,我们可以尝试为用户定义Pydantic模型,并使用Body函数提取促销码。首先,让我们定义一个最小用户模型。
class InsertUser(BaseModel):username: strname: str
Now, we can create a more complex function that will process two Pydantic models and an optional user promo code, we have set the default value to None.
现在,我们可以创建一个更复杂的函数,它将处理两个Pydantic模型和一个可选的用户促销码,我们将默认值设置为None。
from fastapi import FastAPI, Body
from pydantic import BaseModelclass InsertCar(BaseModel):brand: strmodel: stryear: intclass InsertUser(BaseModel):username: strname: strapp = FastAPI()@app.post("/cars")
async def new_car(user: InsertUser,car: InsertCar,code: int = Body(None)
):return {"user": user,"car": car,"code": code,}if __name__ == '__main__':import uvicornuvicorn.run(app)
For this request, which contains a full fledged JSON object with two nested objects and some code, I opted to use Insomnia since I find it easier than typing JSON in the command propt or resorting to piping. I guess it is a matter of preference, but I believe that when developing and testing REST APIs, it is useful to have a GUI tool such as Insomnia or Postman and a command-line client (such as cURL or httpie). This is what Insomnia looks like when testing this particular endpoint.
对于这个包含两个嵌套对象和一些代码的完整JSON对象的请求,我选择使用Insomnia,因为我发现它比在命令提示符中输入JSON或使用管道更容易。我想这是一个偏好问题,但我相信,在开发和测试REST api时,有一个GUI工具(如Insomnia或Postman)和一个命令行客户端(如cURL或httpie)是很有用的。这就是Insomnia 在测试这个特定端点时的样子。
POST: http://localhost:8000/cars
{"car":{"brand": "Renault","model": "Twingo","year": 2017},"user": {"username": "zhangdapeng","name": "dapeng"},"code": 10000
}
After playing around with the combination of request bodies and Pydantic models, we have seen that we can control the inflow of the data and be confident that the data that’s available to our API endpoint will be what we want it and expect it to be. Sometimes, however, we may want to go to the bare metal and work with the raw request object. FastAPI covers that case too.
在尝试了请求体和Pydantic模型的组合之后,我们已经看到我们可以控制数据的流入,并确信API端点可用的数据将是我们想要的和期望的。然而,有时我们可能想要直接使用原始请求对象。FastAPI也涵盖了这种情况。
The request object
I have mentioned several times that FastAPI is built on the Starlette web framework and that it uses numberous Starlette features. The raw request object in FastAPI is Starlette’s request object and it can be accessed in our functions once it’s been imported from FastAPI directly. Bear in mind that by using the request object directly, you are missing out on FastAPI’s most important features, Pydantic parsing and validation, as well as self documentation! However, there might be situations in which you need to have the raw requests.
我已经多次提到FastAPI是建立在Starlette web框架上的,它使用了许多Starlette的特性。FastAPI中的原始请求对象是Starlette的请求对象,一旦从FastAPI直接导入,就可以在我们的函数中访问它。请记住,通过直接使用请求对象,您将错过FastAPI最重要的特性,Pydantic解析和验证,以及自我文档!但是,在某些情况下,您可能需要原始请求。
from fastapi import FastAPI, Requestapp = FastAPI()@app.post("/cars")
async def new_car(req: Request):return {"base_url": req.base_url,}if __name__ == '__main__':import uvicornuvicorn.run(app)
In the preceding code, we created a minimal FastAPI app, imported the Request class, and used it in the only endpoint. If you test this endpoint with your REST client, you will only get the base URL as the message, while the all part lists all the methods and properties of the Request object so that you have an idea of what is available.
在前面的代码中,我们创建了一个最小的FastAPI应用程序,导入了Request类,并在唯一的端点中使用它。如果您用REST客户机测试这个端点,您将只获得作为消息的基本URL,而all部分列出了Request对象的所有方法和属性,以便您了解可用的内容。
All of these methods and properties are available for you to use in your application.
您可以在应用程序中使用所有这些方法和属性。
With that, we’ve seen how FastAPI facilitates our work with the main HTTP transport mechanisms request bodies, query strings, and paths. Now, we will cover other, equally important aspects of any web framework solution.
至此,我们已经看到了FastAPI如何简化我们使用主要HTTP传输机制(请求体、查询字符串和路径)的工作。现在,我们将介绍任何web框架解决方案的其他同等重要的方面。
Cookies and headers, form data, and files
When speaking of the ways our web framework ingests data, any discussion would be incomplete without including topics such as handling form data, handling files, and manipulating cookies and headers. This section will provide simple examples of how FastAPI handles these tasks.
当谈到我们的web框架摄取数据的方式时,如果不包括处理表单数据、处理文件、操作cookie和header等主题,任何讨论都是不完整的。本节将提供FastAPI如何处理这些任务的简单示例。
Headers
Header parameters are handled in a similar way to query and path parameters and, as we will see later, cookies. We can collect them, so to speak, using the Header function. Headers are essential in topics such as authentication and authorization as they often carry JSON Web Tokens (JWTs), which are used for identifying users and their permissions.
处理报头参数的方式与处理查询和路径参数以及cookie的方式类似。可以这么说,我们可以使用Header函数收集它们。标头在身份验证和授权等主题中是必不可少的,因为它们通常携带JSON Web令牌(jwt),用于标识用户及其权限。
Let’s try to read the user agent by using the Header function.
让我们尝试使用Header函数来读取用户代理。
from fastapi import FastAPI, Headerapp = FastAPI()@app.post("/cars")
async def new_car(user_agent: str | None = Header(None)):return {"User-Agent": user_agent}if __name__ == '__main__':import uvicornuvicorn.run(app)
使用httpie发送请求:
http POST localhost:8000/cars
响应结果:
HTTP/1.1 200 OK
content-length: 29
content-type: application/json
date: Wed, 27 Mar 2024 23:27:57 GMT
server: uvicorn{"User-Agent": "HTTPie/3.2.2"
}
You can extract all the headers in this way and FastAPI is nice enough to provide further assistance: it will convert names into lowercase, convert the keys into snake case, and so on.
您可以用这种方式提取所有的头文件,FastAPI很好地提供了进一步的帮助:它将名称转换为小写字母,将键转换为蛇形字母,等等。
Cookies
Cookies work in a very similar way and although they can be extracted manually from the Cookies header, the framework offers a utility function, conveniently named Cookie, that does all the work in a way similar to Query, Path, and Header.
Cookie以非常相似的方式工作,尽管可以从Cookie头中手动提取它们,但框架提供了一个实用程序函数,方便地命名为Cookie,它以类似于查询、路径和头的方式完成所有工作。
Forms (and files)
So far, we have only dealt with JSON data and that is alright, after all, it is the ubiquitous language of the web and our main vehicle for moving data back and forth. There are cases, however, that require a different data encoding, forms might be processed directly by your API, with data encoded as multipart/form-data or form-urlencoded.
到目前为止,我们只处理了JSON数据,这没关系,毕竟,它是web中无处不在的语言,也是我们来回移动数据的主要工具。但是,在某些情况下,需要不同的数据编码,您的API可以直接处理表单,将数据编码为multipart/form-data或form-urlencoded。
Important Node: Notice that although we can have multiple Form parameters in a path operation, we cannot declare Body fields that we expect to be in JSON. The HTTP request will have the body encoded using only application/x-www-for,-urlencoded instead of application/json. This limitation is part of the HTTP protocol and has nothing todo with FastAPI itself.
重要提示:请注意,尽管我们可以在一个路径操作中使用多个Form参数,但我们不能声明我们期望在JSON中的Body字段。HTTP请求的正文将只使用application/x-www-for,-urlencoded而不是application/json进行编码。这个限制是HTTP协议的一部分,与FastAPI本身无关。
The simplest way to cover both form cases, with and without including files for upload, is to start by installing python-multipart, a streaming multipart parser for Python. Stop your server and use pip to install it.
覆盖这两种表单情况(包括或不包括用于上传的文件)的最简单方法是从安装Python -multipart开始,这是一个用于Python的流多部分解析器。停止服务器并使用pip安装它。
pip install python-multipart
The Form function works similarly to the previously examined utility functions, but with the difference that it looks for form-encoded parameters. Let’s look at a simple example in which we wish to upload a car image and a couple of form fields, such as the brand and the model. I will use a photo that I found on Pexels (photo by Yogesh Yadav: https://www.pexels.com/photo/white-vintage-car-parked-on-green-grass-8746207/).
Form函数的工作方式类似于前面讨论的实用程序函数,但不同之处在于它查找表单编码的参数。让我们看一个简单的示例,在这个示例中,我们希望上传一个汽车图像和两个表单字段,比如品牌和型号。我将使用我在pixel上找到的一张照片(照片由Yogesh Yadav提供:https://www.pexels.com/photo/white-vintage-car-parked-on-green-grass-8746207/)。
from fastapi import FastAPI, Form, File, UploadFileapp = FastAPI()@app.post("/upload")
async def upload(file: UploadFile = File(...),brand: str = Form(...),model: str = Form(...),
):return {"brand": brand,"model": model,"file": file.filename,}if __name__ == '__main__':import uvicornuvicorn.run(app)
The preceding code handles the form parameters via the Form function and the uploaded file by using the UploadFile utility class.
前面的代码通过form函数处理表单参数,并使用UploadFile实用程序类处理上传的文件。
The preceding code handles the form parameters via the Form function and the uploaded file by using the UploadFile utility class.
前面的代码通过form函数处理表单参数,并使用UploadFile实用程序类处理上传的文件。
To save the image to a disk, we need to copy the buffer to an actual file on the disk. The following code achieves this.
为了将映像保存到磁盘,我们需要将缓冲区复制到磁盘上的实际文件中。下面的代码实现了这一点。
import shutil
from fastapi import FastAPI, Form, File, UploadFileapp = FastAPI()@app.post("/upload")
async def upload(file: UploadFile = File(...),brand: str = Form(...),model: str = Form(...),
):# 保存图片with open("test.jpg", "wb") as f:shutil.copyfileobj(file.file, f)return {"brand": brand,"model": model,"file": file.filename,}if __name__ == '__main__':import uvicornuvicorn.run(app)
The open block opens a file on the disk using a specified filename and copies the FastAPI file that’s sent through the form. I have hardcoded the filename, do any new upload will simply overwirte the existing file, but you could use some randomly generated filename while using the UUID library, for example.
open块使用指定的文件名在磁盘上打开一个文件,并复制通过表单发送的FastAPI文件。我已经硬编码了文件名,任何新的上传都会简单地覆盖现有文件,但是您可以在使用UUID库时使用一些随机生成的文件名,例如。
File uploading is an operation that you probably won’t be doing this way, file uploads can be handled by the Python async file library known as aifiles or as a background task, which is another feature of FastAPI. However, I wanted to provide a rather complete picture of how you can handle everyday web tasks with the framework.
文件上传是一个你可能不会这样做的操作,文件上传可以由称为aifiles的Python异步文件库处理,或者作为后台任务处理,这是FastAPI的另一个功能。然而,我想提供一个相当完整的画面,告诉你如何使用这个框架处理日常的web任务。
FastAPI response customization
In the previous sections, we looked at numberous small examples of FastAPI requests, saw how we can reach every corner of the request, the path, the query string, the request body, headers, and cookies, and saw how to work with form encoded requests. Now, let’s take a closer look at FastAPI’s response objects. In all the cases that we have seen so far, we returned a Python dictionary that was then serialized into JSON correctly by FastAPI. The framework enables use, the developers, to customize the response in a very granular way, as we will see in the next few sections.
在前面的小节中,我们查看了许多FastAPI请求的小示例,了解了如何到达请求的每个角落、路径、查询字符串、请求体、报头和cookie,并了解了如何处理表单编码的请求。现在,让我们仔细看看FastAPI的响应对象。到目前为止,在我们看到的所有情况下,我们都返回了一个Python字典,然后由FastAPI正确地序列化为JSON。该框架使使用者(即开发人员)能够以非常细粒度的方式定制响应,我们将在接下来的几节中看到这一点。
The first thing that you may want to change in an HTTP response is going to be the status code. You may also want to provide some meaningful errors when things do not go as planned. FastAPI conveniently raises classic Python exceptions when HTTP errors are present. FastAPI puts a lot of emphasis on using standar, compliant meaningful response codes that minimize the need to create custom payload messages. For instance, you don’t want to send a 200 OK status code for everything and then notify users of errors by using the payload, FastAPI encourages good practices.
您可能希望在HTTP响应中更改的第一件事是状态码。当事情没有按计划进行时,您可能还希望提供一些有意义的错误。当存在HTTP错误时,FastAPI可以方便地引发经典的Python异常。FastAPI非常强调使用标准的、兼容的、有意义的响应代码,从而最大限度地减少了创建自定义有效负载消息的需要。例如,您不希望为所有内容发送200 OK状态码,然后通过使用有效负载通知用户错误,FastAPI鼓励良好的实践。
Setting status codes
HTTP status codes indicate if an operation was successful or if there was an error. These codes also provide information about the type of operation, and they can be divided into several groups: infomational, successful, client errors, server errors, and so on. It isn’t necessary to memorize the status codes, although you probably know what a 404 or a 500 code is, unfortunately.
HTTP状态码指示操作是否成功或是否存在错误。这些代码还提供有关操作类型的信息,它们可以分为几组:信息、成功、客户端错误、服务器错误等等。没有必要记住状态码,尽管很不幸,您可能知道404或500代码是什么。
FastAPI makes it incredibly easy to set a status code, it is enough to just pass the desired status_code variable to the decorator. Here, we are using the 208 status code for a simple endpoint.
FastAPI使设置状态码变得非常容易,只需将所需的status_code变量传递给装饰器就足够了。在这里,我们对一个简单的端点使用208状态码。
from fastapi import FastAPI, statusapp = FastAPI()@app.get("/", status_code=status.HTTP_208_ALREADY_REPORTED)
async def hello():return {"message": "Hello World"}if __name__ == '__main__':import uvicornuvicorn.run(app)
Testing the root route in httpie yields the following output.
在httpie中测试根路由会得到以下输出。
http localhost:8000
HTTP/1.1 208 Already Reported
content-length: 25
content-type: application/json
date: Thu, 28 Mar 2024 01:14:43 GMT
server: uvicorn{"message": "Hello World"
}
Similarly, we can set status codes for the delete, update, or create operations.
类似地,我们可以为删除、更新或创建操作设置状态码。
FastAPI sets the 200 status by default if it doesn’t encounter exceptions, so it is up to us to set the correct codes for the various API operations, such as 204 No Content for deleteing, 201 for creating, and so on. It is a good practice that is particularly encouraged.
如果没有遇到异常,FastAPI默认设置200状态,因此由我们来为各种API操作设置正确的代码,例如用于删除的204 No Content,用于创建的201,等等。这是一种特别鼓励的良好做法。
Pydantic can be used for response modeling as well, we can limit or otherwise modify the fields that should appear in the response and perform similar checks that it ddoes for the request body by using the response_model argument.
Pydantic也可以用于响应建模,我们可以限制或修改响应中应该出现的字段,并通过使用response_model参数对请求体执行类似的检查。
HTTP errors
Errors are bound to happen, no matter how meticulously you design your backend, for example, users somehow find a way to send the wrong parameters to a query, the frontend sends the wrong request body, or the database goes offline (although that shouldn’t happen since we will be using MongoDB!), anything can happen. It is of paramount importance to detect these errors as soon as possible (this is a leitmotiv in FastAPI) and send clear and complete messages to the frontend, as well as the user. We can do this by raising exceptions.
无论您如何精心设计后端,错误都必然会发生,例如,用户不知何故找到了向查询发送错误参数的方法,前端发送错误的请求体,或者数据库脱机(尽管这不应该发生,因为我们将使用MongoDB!),任何事情都可能发生。尽快检测这些错误(这是FastAPI的主要目的)并向前端和用户发送清晰完整的消息是至关重要的。我们可以通过引发异常来做到这一点。
FastAPI heavily relies on web standards and tries to enforce good practices in every facet of the development process, so it puts a lot of emphasis on using HTTP status codes. These codes provide a clear indication of the type of problem that has arisen, while the payload can be used to further clarify the cause of the problem.
FastAPI在很大程度上依赖于web标准,并试图在开发过程的各个方面实施良好的实践,因此它非常强调使用HTTP状态码。这些代码提供了已出现问题类型的明确指示,而有效载荷可用于进一步澄清问题的原因。
FastAPI uses a Python exception, aptly called HTTPException, to raise HTTP errors. This class allows us to set a status code and set an error message.
FastAPI使用一个Python异常(恰当地称为HTTPException)来引发HTTP错误。这个类允许我们设置状态码和错误消息。
Returning to our example of inserting new cars into the database, we could set a custom exception like this.
回到我们在数据库中插入新车的例子,我们可以像这样设置一个自定义异常。
from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModelclass InsertCar(BaseModel):brand: strmodel: stryear: intapp = FastAPI()@app.post("/cars")
async def insert_car(car: InsertCar):if car.year > 2022:raise HTTPException(status_code=status.HTTP_406_NOT_ACCEPTABLE,detail="The car doesn't exist yet!",)return {"message": car}if __name__ == '__main__':import uvicornuvicorn.run(app)
When trying to insert a car that hasn’t been built yet, the response is as follows.
当试图插入一辆尚未建成的汽车时,响应如下。
http POST localhost:8000/cars brand="fiat" model="500L" year=2023
HTTP/1.1 406 Not Acceptable
content-length: 39
content-type: application/json
date: Thu, 28 Mar 2024 01:29:35 GMT
server: uvicorn{"detail": "The car doesn't exist yet!"
}
This is a pretty contrived example as I do not expect you to make custom exceptions for any possible problem that might arise, but I believe that this gives a goo idea of what is possible and the flexibility that FastAPI gives you.
这是一个非常做作的例子,因为我不希望您为任何可能出现的问题定制异常,但我相信这给了一个很好的想法,什么是可能的,以及FastAPI为您提供的灵活性。
We just had a pretty fast ride through the main features of FastAPI, with particular emphasis on ways to get data out of the request and how to set the response according to our needs. Now, let’s summarize this chapter.
我们刚刚快速地了解了FastAPI的主要特性,特别强调了从请求中获取数据的方法,以及如何根据我们的需要设置响应。现在,让我们总结一下这一章。
相关文章:
FastAPI+React全栈开发15 让我们构建一个展示API
Chapter03 Getting Started with FastAPI 15 Let’s Build a showcase API FastAPIReact全栈开发15 让我们构建一个展示API REST APIs are all about cycles of HTTP requests and responses, it is the engine that powers the web and is implemented in every web framew…...
list(链表)容器(二)
一、list 插入和删除 函数原型: push_back(elem);//在容器尾部加入一个元素 pop_back();//删除容器中最后一个元素 push_front(elem);//在容器开头插入一个元素 pop_front();//从容器开头移除第一个元素 insert(pos,elem);//在pos位置插elem元素的拷贝,…...
世优科技上榜2024年度《中国虚拟数字人影响力指数报告》
日前,第三期《中国虚拟数字人影响力指数报告》在中国网络视听大会上正式发布。本期《报告》由中国传媒大学媒体融合与传播国家重点实验室(以下简称“国重实验室”)、中国传媒大学数字人研究院编制,中国网络视听协会、人民日报智慧…...
【调试方法】C代码中dump中间数据的方法
一,简介 本文主要介绍,如何在C语言代码中将音频流数据进行写入文件,方便调试定位问题: 二,函数实现 按int8_t写入 #include <stdio.h>int32_t write_int8_t_data(int8_t *name, int8_t *buffer, int32_t dat…...
【BUG】vue中@change时间传值丢失问题
项目场景: 在修改项目bug时,发现后端响应到前端的值,通过change事件调用方法,在方法中拿到值时,有部分数据丢失。 问题描述 后端传到前端的值为:字符串类型的"00000089",change调用…...
Linux提权!!!
上一篇文章讲了Windows的提权,那么这篇文章就来讲一下Linux的提权 1.SUID提权 suid权限 作用:让普通用户临时拥有该文件的属主的执行权限,suid权限只能应用在二进制可执行文件(命令)上,而且suid权限只能设置…...
Android Studio学习7——常用控件view
Android控件 双击shift键——>搜索想要找的文件 Ctrlshift回车——>补全“;”号 CtrlX——>删除一行,只需把鼠标放在那一行 windows自带字体...
Springboot3 集成knife4j(swagger)
knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案,前身是swagger-bootstrap-ui,取名kni4j是希望它能像一把匕首一样小巧,轻量,并且功能强悍! 官网地址: Knife4j 集Swagger2及OpenAPI3为一体的增强解决方案. | Knife4j 本文以Springboot3版本集成kn…...
深信服:借助观测云实现全链路可观测性
导读 深信服科技股份有限公司 简称「深信服」( Sangfor Technologies Inc. ),是一家领先的网络安全和云计算解决方案提供商,致力于为全球客户提供高效、智能、安全的网络和云服务。随着公司业务的不断扩展,也面临着监…...
详解Qt中使用线程
详解Qt中使用线程 Qt中的线程相关知识涵盖了线程创建、管理、通信以及线程安全等方面。下面将详细讲解这些知识点,并提供对应的示例代码。 线程创建与管理 QThread类 Qt通过QThread类来创建和管理线程。要创建一个新的工作线程,通常有两种方法&#…...
在.Net6中用gdal实现第一个功能
目录 一、创建.NET6的控制台应用程序 二、加载Gdal插件 三、编写程序 一、创建.NET6的控制台应用程序 二、加载Gdal插件 Gdal的资源可以经过NuGet包引入。右键单击项目名称,然后选择 "Manage NuGet Packages"(管理 NuGet 包)。N…...
采用大语言模型进行查询重写——Query Rewriting via Large Language Models
文章:Query Rewriting via Large Language Models,https://arxiv.org/abs/2403.09060 摘要 查询重写是在将查询传递给查询优化器之前处理编写不良的查询的最有效技术之一。 手动重写不可扩展,因为它容易出错并且需要深厚的专业知识。 类似地…...
使用Vue实现CSS过渡和动画
01-初识动画和过渡 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>使用vue实现css过渡和动画&l…...
一家购物商场的数据运营挑战
✅作者简介:《数据运营:数据分析模型撬动新零售实战》作者、《数据实践之美》作者、数据科技公司创始人、多次参加国家级大数据行业标准研讨及制定、高端企培合作讲师。 🌸公众号:风姑娘的数字视角,免费分享数据应用相…...
React Native框架开发APP,安装免费的图标库(react-native-vector-icons)并使用详解
一、安装图标库 要使用免费的图标库,你可以使用 React Native Vector Icons 库。 首先,确保你已经安装了 react-native-vector-icons: npm install --save react-native-vector-iconsnpm install --save-dev types/react-native-vector-ic…...
idea端口占用
报错:Verify the connector‘s configuration, identify and stop any process that‘s listening on port XXXX 翻译: 原因: 解决: 一、重启大法 二、手动关闭 启动spring项目是控制台报错,详细信息如下ÿ…...
MQ消息队列详解以及MQ重复消费问题
MQ消息队列详解以及MQ重复消费问题 1、解耦2、异步调用3、流量削峰4、MQ重复消费问题,以及怎么解决?4.1、重复消费产生4.2、解决方法: https://blog.csdn.net/qq_44240587/article/details/104630567 核心的就是:解耦、异步、削锋…...
系统IO函数接口
目录 前言 一. man手册 1.1 man手册如何查询 1.2 man手册基础 二.系统IO函数接口 三.open打开文件夹 3.1 例1 open打开文件 3.2 open打开文件代码 3.3 例2 创建文件 四.write写文件 4.1 write写文件 五. read读文件 5.1 read读文件与偏移 5.2 偏移细节 5.3 read读文件代码 六.复…...
06 监听器
文章目录 SessionAttListenerDemo.javaSessionListenerDemo.javaProductController.java SessionAttListenerDemo.java package com.aistart.listener;import javax.servlet.ServletContext; import javax.servlet.annotation.WebListener; import javax.servlet.http.HttpSess…...
C语言第三十九弹---预处理(上)
✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】 预处理 1、预定义符号 2、#define定义常量 3、#define定义宏 4、带有副作用的宏参数 5、宏替换的规则 6、宏和函数的对比 总结 在C语言中,预处…...
计算机视觉无人驾驶技术:入门指南
I. 引言: 计算机视觉无人驾驶技术是一种基于计算机视觉和机器学习技术的自动化驾驶技术。它可以通过搭载各种传感器和摄像机,让车辆自主感知周围环境,实现尽可能自动化的驾驶操作。 这种技术具有重要性和优势,包括: …...
Golang和Java对比
其实我是Javaer转的Golang,我谈谈自己对Java和Golang的体会 我先讲讲我认为Golang的优点 1、Golang是一门新语言,相比于Java,他的生态要小很多,优点很明显,自由度高,学习成本低,能快速拉起一个…...
2024.2.29力扣每日一题——统计可能的树根数目
2024.2.29 题目来源我的题解方法一 深度搜索(暴力) 超时方法二 树形动态规划 题目来源 力扣每日一题;题序:2581 我的题解 方法一 深度搜索(暴力) 超时 以每个节点node为跟进行深度搜索,并在搜…...
同一个主机配置多个SSH key
使用git时,我们可能一个git客户端使用多个git服务器,比如github,自建gitlab,gitee,为了防止提交混乱,所以需要一一对应生成公私钥。 第一步: 使用ssh-keygen生成多对密钥对,比如&…...
SAP系统财务模块简介:实现财务管理的卓越之道
作为全球领先的企业管理软件提供商,SAP公司开发了一系列强大而全面的财务模块,帮助企业实现财务管理的高效运作与优化。SAP系统的财务模块涵盖了财务核算、成本管理、资金管理、资产会计等多个方面,为企业提供了完整的财务管理解决方案。本文…...
【pytest】功能特性及常用插件
pytest是一个功能强大的Python测试框架,它的语法简洁明了,易于学习和使用。同时,它提供了丰富的功能和插件,使得测试过程更加灵活和高效。 功能特性 pytest的主要功能特性包括: 参数化测试:允许使用不同…...
基于SpringBoot和Vue的房产销售系统的设计与实现
今天要和大家聊的是一款基于SpringBoot和Vue的房产销售系统的设计与实现 !!! 有需要的小伙伴可以通过文章末尾名片咨询我哦!!! 💕💕作者:李同学 💕…...
ROS2从入门到精通1-2:详解ROS2服务通信机制与自定义服务
目录 0 专栏介绍1 服务通信模型2 服务模型实现(C)3 服务模型实现(Python)4 自定义服务5 话题、服务通信的异同 0 专栏介绍 本专栏旨在通过对ROS2的系统学习,掌握ROS2底层基本分布式原理,并具有机器人建模和应用ROS2进行实际项目的开发和调试的工程能力。…...
vue两个特性和什么是MVVM
一、什么是vue 1.构建用户界面 用vue往html页面中填充数据,非常的方便 2.框架 框架是一套线成的解决方案 vue的指令、组件(是对ui结构的复用)、路由、vuex 二、vue的特性 1.数据驱动视图 2.双向数据绑定 1.数据驱动视图 数据的变化会驱动…...
CAD Plant3D 2023 下载地址及安装教程
CAD Plant3D是一款专业的三维工厂设计软件,用于在工业设备和管道设计领域进行建模和绘图。它是Autodesk公司旗下的AutoCAD系列产品之一,专门针对工艺、石油、化工、电力等行业的设计和工程项目。 CAD Plant3D提供了一套丰富的工具和功能,帮助…...
wordpress动漫图片主题/做seo网页价格
最近有朋友在咨询天兴工作室zblogphp调用某个栏目内的文章怎么调用?调用后想第一篇文章和后面的用不同的显示界面怎么搞?本文就来上示例代码并尝试解释下。先上代码: {foreach Getlist(10,2,null,null,null,null,array(has_subcate>true))…...
选择合肥网站建设/网站注册信息查询
文章目录简介nvue 和 vue 相互通讯方式:nvue注意事项:简介 uni-app是逻辑渲染分离的,渲染层在app端提供了两套排版引擎, 小程序方式的webview渲染和weex方式的原生渲染,两种渲染引入可以自己根据需要选。 vue文件走的…...
坪山网站建设基本流程/新媒体运营培训课程
从1.5开始,JQuery引入了Deferred对象,应用这个对象,针对一个行为可以注册多个回调函数,并能将行为的调用结果进行传递。以下用一些例子来说明这个对象的强大功能。 楔子: 以下的代码是用来获取一个文件的内容,获取完毕…...
个人怎么申请注册商标/seo资料
var utils{}; /*** 获取时区方法* returns {number} 8代表东8 -8西8*/utils.getLocalTime function () {var date new Date();return date.getTimezoneOffset() / -60;};/*** 获取当前时间方法* returns {string}对应格式的当前时间*/utils.getCurrentTime function (st…...
iis7搭建网站织梦/宁波seo推广联系方法
省市级联//定义一个保存所有城市的数组var arys [["北京","天津","重庆","上海"],["长沙","株洲","湘潭","娄底","岳阳"],["东莞","佛山","中山",…...
网站建站手机/淘宝如何提升关键词排名
文章目录1.一层感知机结构输出数学推导代码测试2.多层感知机结构输出数学推导代码测试3.反向传播过程数学推导函数优化示例1.一层感知机结构输出 这个结构的含义是,n个x的输入,经过n个w的权值,进行一个线性的叠加求和∑XW得到一个输出结果x1…...