使用 Python FastAPI 构建 Web 服务
FastAPI
![](/data/attachment/album/202006/04/105930wr333sx3erz3k2ur.jpg)


[FastAPI](https://fastapi.tiangolo.com/) 是一个使用 Python 编写的 Web 框架，还应用了 Python asyncio 库中最新的优化。本文将会介绍如何搭建基于容器的开发环境，还会展示如何使用 FastAPI 实现一个小型 Web 服务。


### 起步


我们将使用 Fedora 作为基础镜像来搭建开发环境，并使用 Dockerfile 为镜像注入 FastAPI、[Uvicorn](https://www.uvicorn.org/) 和 [aiofiles](https://github.com/Tinche/aiofiles) 这几个包。



```
FROM fedora:32
RUN dnf install -y python-pip \
    && dnf clean all \
    && pip install fastapi uvicorn aiofiles
WORKDIR /srv
CMD ["uvicorn", "main:app", "--reload"]
```

在工作目录下保存 `Dockerfile` 之后，执行 `podman` 命令构建容器镜像。



```
$ podman build -t fastapi .
$ podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/fastapi latest 01e974cabe8b 18 seconds ago 326 MB
```

下面我们可以开始创建一个简单的 FastAPI 应用程序，并通过容器镜像运行。



```
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello Fedora Magazine!"}
```

将上面的代码保存到 `main.py` 文件中，然后执行以下命令开始运行：



```
$ podman run --rm -v $PWD:/srv:z -p 8000:8000 --name fastapi -d fastapi
$ curl http://127.0.0.1:8000
{"message":"Hello Fedora Magazine!"
```

这样，一个基于 FastAPI 的 Web 服务就跑起来了。由于指定了 `--reload` 参数，一旦 `main.py` 文件发生了改变，整个应用都会自动重新加载。你可以尝试将返回信息 `"Hello Fedora Magazine!"` 修改为其它内容，然后观察效果。


可以使用以下命令停止应用程序：



```
$ podman stop fastapi
```

### 构建一个小型 Web 服务


接下来我们会构建一个需要 I/O 操作的应用程序，通过这个应用程序，我们可以看到 FastAPI 自身的特点，以及它在性能上有什么优势（可以在[这里](https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=composite&l=z8kflr-v&a=2&f=jz8cg-0-3s-0-3k-6bo-0-0-18y74-8s5c-0)参考 FastAPI 和其它 Python Web 框架的对比）。为简单起见，我们直接使用 `dnf history` 命令的输出来作为这个应用程序使用的数据。


首先将 `dnf history` 命令的输出保存到文件。



```
$ dnf history | tail --lines=+3 > history.txt
```

在上面的命令中，我们使用 `tail` 去除了 `dnf history` 输出内容中无用的表头信息。剩余的每一条 `dnf` 事务都包括了以下信息：


* `id`：事务编号（每次运行一条新事务时该编号都会递增）
* `command`：事务中运行的 `dnf` 命令
* `date`：执行事务的日期和时间


然后修改 `main.py` 文件将相关的数据结构添加进去。



```
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class DnfTransaction(BaseModel):
    id: int
    command: str
    date: str
```

FastAPI 自带的 [pydantic](https://pydantic-docs.helpmanual.io/) 库让你可以轻松定义一个数据类，其中的类型注释对数据的验证也提供了方便。


再增加一个函数，用于从 `history.txt` 文件中读取数据。



```
import aiofiles

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class DnfTransaction(BaseModel):
    id: int
    command: str
    date: str


async def read_history():
    transactions = []
    async with aiofiles.open("history.txt") as f:
        async for line in f:
            transactions.append(DnfTransaction(
                id=line.split("|")[0].strip(" "),
                command=line.split("|")[1].strip(" "),
                date=line.split("|")[2].strip(" ")))
    return transactions
```

这个函数中使用了 `aiofiles` 库，这个库提供了一个异步 API 来处理 Python 中的文件，因此打开文件或读取文件的时候不会阻塞其它对服务器的请求。


最后，修改 `root` 函数，让它返回事务列表中的数据。



```
@app.get("/")
async def read_root():
    return await read_history()
```

执行以下命令就可以看到应用程序的输出内容了。



```
$ curl http://127.0.0.1:8000 | python -m json.tool
[
{
"id": 103,
"command": "update",
"date": "2020-05-25 08:35"
},
{
"id": 102,
"command": "update",
"date": "2020-05-23 15:46"
},
{
"id": 101,
"command": "update",
"date": "2020-05-22 11:32"
},
....
]
```

### 总结


FastAPI 提供了一种使用 asyncio 构建 Web 服务的简单方法，因此它在 Python Web 框架的生态中日趋流行。要了解 FastAPI 的更多信息，欢迎查阅 [FastAPI 文档](https://fastapi.tiangolo.com/)。


本文中的代码可以在 [GitHub](https://github.com/cverna/fastapi_app) 上找到。




---


via: <https://fedoramagazine.org/use-fastapi-to-build-web-services-in-python/>


作者：[Clément Verna](https://fedoramagazine.org/author/cverna/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[HankChow](https://github.com/HankChow) 校对：[wxy](https://github.com/wxy)


本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


OK
* FastAPI* is a modern Python web framework that leverage the latest Python improvement in asyncio. In this article you will see how to set up a container based development environment and implement a small web service with FastAPI.

## Getting Started

The development environment can be set up using the Fedora container image. The following Dockerfile prepares the container image with FastAPI, [Uvicorn](https://www.uvicorn.org/) and [aiofiles](https://github.com/Tinche/aiofiles).

FROM fedora:32 RUN dnf install -y python-pip \ && dnf clean all \ && pip install fastapi uvicorn aiofiles WORKDIR /srv CMD ["uvicorn", "main:app", "--reload"]

After saving this Dockerfile in your working directory, build the container image using podman.

$ podman build -t fastapi . $ podman images REPOSITORY TAG IMAGE ID CREATED SIZE localhost/fastapi latest 01e974cabe8b 18 seconds ago 326 MB

Now let’s create a basic FastAPI program and run it using that container image.

from fastapi import FastAPI app = FastAPI() @app.get("/") async def root(): return {"message": "Hello Fedora Magazine!"}

Save that source code in a *main.py* file and then run the following command to execute it:

$ podman run --rm -v $PWD:/srv:z -p 8000:8000 --name fastapi -d fastapi $ curl http://127.0.0.1:8000 {"message":"Hello Fedora Magazine!"

You now have a running web service using FastAPI. Any changes to *main.py* will be automatically reloaded. For example, try changing the “Hello Fedora Magazine!” message.

To stop the application, run the following command.

$ podman stop fastapi

## Building a small web service

To really see the benefits of FastAPI and the performance improvement it brings ([see comparison](https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=composite&l=z8kflr-v&a=2&f=jz8cg-0-3s-0-3k-6bo-0-0-18y74-8s5c-0) with other Python web frameworks), let’s build an application that manipulates some I/O. You can use the output of the *dnf history* command as data for that application.

First, save the output of that command in a file.

$ dnf history | tail --lines=+3 > history.txt

The command is using *tail* to remove the headers of *dnf history* which are not needed by the application. Each dnf transaction can be represented with the following information:

- id : number of the transaction (increments every time a new transaction is run)
- command : the dnf command run during the transaction
- date: the date and time the transaction happened

Next, modify the *main.py* file to add that data structure to the application.

from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class DnfTransaction(BaseModel): id: int command: str date: str

FastAPI comes with the [pydantic](https://pydantic-docs.helpmanual.io/) library which allow you to easily build data classes and benefit from type annotation to validate your data.

Now, continue building the application by adding a function that will read the data from the *history.txt * file.

import aiofiles from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class DnfTransaction(BaseModel): id: int command: str date: str async def read_history(): transactions = [] async with aiofiles.open("history.txt") as f: async for line in f: transactions.append(DnfTransaction( id=line.split("|")[0].strip(" "), command=line.split("|")[1].strip(" "), date=line.split("|")[2].strip(" "))) return transactions

This function makes use of the * aiofiles* library which provides an asyncio API to manipulate files in Python. This means that opening and reading the file will not block other requests made to the server.

Finally, change the root function to return the data stored in the transactions list.

@app.get("/") async def read_root(): return await read_history()

To see the output of the application, run the following command

$ curl http://127.0.0.1:8000 | python -m json.tool [ { "id": 103, "command": "update", "date": "2020-05-25 08:35" }, { "id": 102, "command": "update", "date": "2020-05-23 15:46" }, { "id": 101, "command": "update", "date": "2020-05-22 11:32" }, .... ]

## Conclusion

*FastAPI* is gaining a lot a popularity in the Python web framework ecosystem because it offers a simple way to build web services using asyncio. You can find more information about *FastAPI* in the [documentation](https://fastapi.tiangolo.com/).

The code of this article is available in [this GitHub repository](https://github.com/cverna/fastapi_app).

*Photo by Jan Kubita on Unsplash.*

## Mehdi

Does not work on local machine.

#ERROR: Error loading ASGI app. Could not import module “main”.

Command used is:

uvicorn main:app –reload

And I have installed everything:

pip3 install uvicorn fastapi aiofiles

## Mehdi

Turns out the file name has to be called main.py as mentioned in the post. I was using a different name. Solved.

## Eric L.

You probably haven’t created the

in the current directory.

## baoboa

Very useful, thanks for this post.

## pr0PM

Hi,

Got everything working until

which gives

## Vaclav

Awesome post! Thanks. It works fine without podman /docker container only.

Podman build fails at RUN:

“Error: error building at STEP “RUN dnf install -y python pip && dnf clean all && pip install fastapi uvicorn aiofiles”: error while running runtime: exit status 127

”

There could be a typo – check: dnf search python-pip

I suggest “python pip”

## Clément Verna

Yeah it looks like you are missing “-” in the RUN command which should be “RUN dnf install -y python-pip ….”

## tgfr

better way are using H2o https://h2o.examp1e.net/

## jan

Thanks for the introduction.

Today I was playing with this example on OSX and docker and I figured that one needs to rewrite the last line in the Dockerfile to make uvicorn accessible outside of the container, otherwise it’s only bounded to localhost inside the container and one can only curl within the container.

CMD [“uvicorn”, “main:app”, “–host”, “0.0.0.0”, “–reload”]

I thought this must be the same for podman but it’s not.

Any ideas why that is?