FastAPI+React全栈开发15 让我们构建一个展示API

news/2024/4/15 7:37:03

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的主要特性,特别强调了从请求中获取数据的方法,以及如何根据我们的需要设置响应。现在,让我们总结一下这一章。


http://www.ppmy.cn/news/1405275.html

相关文章

TCP Socket通信CAPL代码演示

TCP通信过程分为三个部分: 建立连接:三次握手数据传输关闭连接:四次挥手这些都可以通过socket套接字接口来实现。 Client: variables {const dword INVALID_SOCKET = 0xFFFFFFFF;dword clientSocket = INVALID_SOCKET;char …

CentOS离线安装命令

一.引言 某些CentOS安装后默认是没有部分Linux命令的,比如netstat和lsof: 一般情况下我们可以通过yum install安装这些命令。但是在CentOS无法访问公网的时候(比如CentOS服务器部署在学校、军工等无法访问外网的环境)&#xff0c…

广东开放大学2024春《互联网金融(本)》形成性考核一二三四总400题参考答案

答案:更多答案,请关注【电大搜题】微信公众号 答案:更多答案,请关注【电大搜题】微信公众号 答案:更多答案,请关注【电大搜题】微信公众号 互联网金融的本质是 A. 双边市场经济 B. 平台经济 C. 多边…

理解游戏服务器架构-部署架构

目录 前言 我所理解的服务器架构 什么是否部署架构 部署架构的职责 进程业务职责 网络链接及通讯方式 与客户端的连接方式 服务器之间连接关系 数据落地以及一致性 数据库的选择 数据访问三级缓存 数据分片 读写分离 分布式数据处理 负载均衡 热更新 配置更新 …

hadoop 高可用(HA)、HDFS HA、Yarn HA

目录 hadoop 高可用(HA) HDFS高可用 HDFS高可用架构 QJM 主备切换: Yarn高可用 hadoop 高可用(HA) HDFS高可用 HDFS高可用架构 QJM 主备切换: Yarn高可用

1,static 关键字.Java

目录 1.概述 2.定义格式和使用 2.1 静态变量及其访问 2.2 实例变量及其访问 2.3 静态方法及其访问 2.4 实例方法及其访问 3.小结 1.概述 static表示静态,是Java中的一个修饰符,可以修饰成员方法,成员变量。被static修饰后的&#xff…

LandCover数据介绍与下载

一、LC介绍 土地覆盖(Land Cover,LC)是自然营造物和人工建筑物所覆盖的地表诸多要素的综合体。土地覆盖指地表所属的植被覆盖物(森林、草原、耕作植被等)或非植被覆盖物(冰雪、建筑物等)的具体类型,侧重描述地球表面的自然属性&a…

zabbix主动发现,注册及分布式监控

主动发现 结果 主动注册 结果 分布式监控 服务机:132 代理机:133 客户端:135 代理机 数据库赋权: 代理机配置 网页上配置代理 客户端配置 网页上配置主机 重启代理机服务 网页效果

【机器学习】数据探索(Data Exploration)---数据质量和数据特征分析

一、引言 在机器学习项目中,数据探索是至关重要的一步。它不仅是模型构建的基础,还是确保模型性能稳定、预测准确的关键。数据探索的过程中,数据质量和数据特征分析占据了核心地位。数据质量直接关系到模型能否从数据中提取有效信息&#xff…

C#多页面共用一个实例

C#多页面共用一个实例 案例&#xff1a; C#与硬件设备交互&#xff0c;交互类里面有打开设备、数据接发等1操作&#xff0c;在其他许多地方需要调用该设备兼顾各代码的耦合度 采用单例模式&#xff0c;eg.CAN设备&#xff1a; private CANClass(){}/// <summary>/// 获…

Apache ECharts-数据统计(详解、入门案例)

简介&#xff1a;Apache ECharts 是一款基于 Javascript 的数据可视化图表库&#xff0c;提供直观&#xff0c;生动&#xff0c;可交互&#xff0c;可个性化定制的数据可视化图表。 1、介绍 图 1.1 Apache ECharts 功能、运行环境 功能&#xff1a; ECharts&#xff…

基本电路理论-电流和电压的参考方向

&#x1f308;个人主页&#xff1a;会编程的果子君 &#x1f4ab;个人格言:“成为自己未来的主人~” 电流及参考方向 电流&#xff1a;带电粒子有规则的定向移动 电流强度&#xff1a;单位时间内通过导体横截面的电荷量&#xff0c;即&#xff1a;idq/dt 单位&#xff1a…

2013年认证杯SPSSPRO杯数学建模C题(第二阶段)公路运输业对于国内生产总值的影响分析全过程文档及程序

2013年认证杯SPSSPRO杯数学建模 C题 公路运输业对于国内生产总值的影响分析 原题再现&#xff1a; 交通运输作为国民经济的载体&#xff0c;沟通生产和消费&#xff0c;在经济发展中扮演着极其重要的角色。纵观几百年来交通运输与经济发展的相互关系&#xff0c;生产水平越高…

力扣贪心算法--第一天

前言 今天是贪心算法的第一天&#xff0c;算法之路重新开始&#xff01; 内容 之前没了解过贪心算法。 什么是贪心 贪心的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。难点就是如何通过局部最优&#xff0c;推出整体最优。 一、455.分发饼干 假设你是一…

源浩流体设备与您相约2024年第13届生物发酵展

参展企业介绍 温州源浩流体设备科技有限公司是一家集设计、开发、制造、销售、服务于一体的高科技企业&#xff0c;公司主要生产各种不锈钢阀门、管件、卫生级流体设备(卫生级换向阀,卫生级减压阀,卫生级罐底阀)等。现为温州市泵阀协会会员&#xff0c;ISO9000 2008版质量质量…

单链表求集合的交集

#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> typedef int ElemType; typedef struct LinkNode {ElemType data;LinkNode* next; }LinkNode, * LinkList; //尾插法建立单链表 void creatLinkList(LinkList& L) {L (LinkNode*)mallo…

QT - 日志:qDebug/qInfo/qWarning/qCritical

篇一、日志打印函数 头文件&#xff1a; #include <QDebug> 代码&#xff1a;qDebug()<<"hello world!"; 其他打印级别&#xff1a; qInfo(): 普通信息 qDebug(): 调试信息 qWarning(): 警告信息 qCritical(): 严重错误 qFatal(): 致命错误 1. qDebug…

Verilog语法回顾--用户定义原语

目录 用户定义原语 UDP定义 UDP状态表 状态表符号 组合UDP 电平敏感UDP 沿敏感时序UDP 参考《Verilog 编程艺术》魏家明著 用户定义原语 用户定义原语&#xff08;User-defined primitive&#xff0c;UDP&#xff09;是一种模拟硬件技术&#xff0c;可以通过设计新的原…

EFPN代码解读

论文 Extended Feature Pyramid Network for Small Object Detection python3 D:/Project/EFPN-detectron2-master/tools/train_net.py --config-file configs/InstanceSegmentation/pointrend_rcnn_R_50_FPN_1x_coco.yaml --num-gpus 1 训练脚本 cfg 中的配置 先获取配置…

Python(乱学)

字典在转化为其他类型时&#xff0c;会出现是否舍弃value的操作&#xff0c;只有在转化为字符串的时候才不会舍弃value 注释的快捷键是ctrl/ 字符串无法与整数&#xff0c;浮点数&#xff0c;等用加号完成拼接 5不入&#xff1f;&#xff1f;&#xff1f; 还有一种格式化的方法…
最新文章