146 lines
3.5 KiB
Markdown
146 lines
3.5 KiB
Markdown
# py_server
|
||
|
||
这是一个 FastAPI 异步 API 项目骨架,分层方式参考了 Hyperf 项目里的 `server/app`。
|
||
|
||
## 分层对应
|
||
|
||
| Hyperf | FastAPI 当前项目 | 作用 |
|
||
| --- | --- | --- |
|
||
| `Controller/Admin` | `app/controller/admin` | 接收请求,调用 service |
|
||
| `Service/Admin` | `app/service/admin` | 业务逻辑 |
|
||
| `Common/Repository` | `app/common/repository` | 数据访问 |
|
||
| `Model` | `app/model` | 数据模型 |
|
||
| `Middleware/Admin` | `app/middleware/admin` | admin token 和权限校验 |
|
||
| `Lib/Jwt` | `app/lib/jwt` | JWT 签发、解析、黑名单 |
|
||
| `Request/Admin` | `app/request/admin` | 请求参数校验 |
|
||
| `Constants` | `app/constants` | 状态码和业务常量 |
|
||
| `Lib/Return` | `app/lib/response` | 统一返回结构 |
|
||
|
||
> Python 里 `return` 是关键字,所以参考 Hyperf 的 `Lib/Return` 在这里命名为 `lib/response`。
|
||
|
||
## 启动
|
||
|
||
```bash
|
||
.venv/bin/uvicorn app.main:app --reload
|
||
```
|
||
|
||
默认启动时会自动创建 SQLite 开发库,并创建一个 admin 用户:
|
||
|
||
- username: `admin`
|
||
- password: `admin`
|
||
|
||
本地配置可以复制:
|
||
|
||
```bash
|
||
cp .env.example .env
|
||
```
|
||
|
||
## Admin 登录接口
|
||
|
||
### 登录
|
||
|
||
```http
|
||
POST /admin/login/login
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"username": "admin",
|
||
"password": "admin"
|
||
}
|
||
```
|
||
|
||
返回:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "success",
|
||
"data": {
|
||
"access_token": "...",
|
||
"refresh_token": "...",
|
||
"expire_at": 3600
|
||
}
|
||
}
|
||
```
|
||
|
||
### 刷新 token
|
||
|
||
```http
|
||
POST /admin/login/refresh
|
||
Authorization: Bearer <refresh_token>
|
||
```
|
||
|
||
刷新成功后会签发新的 `access_token` 和 `refresh_token`,并把旧的 `refresh_token` 加入黑名单,防止重复刷新。
|
||
|
||
### 当前 admin 用户
|
||
|
||
```http
|
||
GET /admin/profile/current
|
||
Authorization: Bearer <access_token>
|
||
```
|
||
|
||
## JWT 逻辑
|
||
|
||
当前实现和参考 Hyperf 项目保持同样思路:
|
||
|
||
1. 登录成功后同时签发 `access_token` 和 `refresh_token`。
|
||
2. 普通 admin 接口只接受 `access_token`。
|
||
3. `/admin/login/refresh` 只接受 `refresh_token`。
|
||
4. refresh 成功后,把旧 refresh token 加入黑名单。
|
||
5. token 会校验签名、过期时间、issuer 和 token 类型。
|
||
|
||
token 读取顺序和 Hyperf 中间件一致:
|
||
|
||
1. `Authorization: Bearer <token>`
|
||
2. `token` header
|
||
3. query string: `?token=...`
|
||
|
||
## FastAPI 依赖注入
|
||
|
||
Controller 里这段:
|
||
|
||
```python
|
||
service: LoginService = Depends(get_login_service)
|
||
```
|
||
|
||
可以理解为 FastAPI 版的 Hyperf 容器注入。
|
||
|
||
请求进来时,FastAPI 会先调用 `get_login_service()`,把创建好的 `LoginService` 传给 `service` 参数。
|
||
|
||
`get_login_service()` 内部会继续组装:
|
||
|
||
- `AdminUserRepository`
|
||
- `BaseTokenService`
|
||
- `AdminReturn`
|
||
|
||
所以 controller 只负责接收请求和调用 service。
|
||
|
||
## `@lru_cache` 的作用
|
||
|
||
`app/core/dependencies.py` 里的:
|
||
|
||
```python
|
||
@lru_cache
|
||
def get_database() -> Database:
|
||
return Database(get_settings().database_path)
|
||
```
|
||
|
||
表示第一次调用时创建对象,后面再次调用时直接复用第一次创建的对象。
|
||
|
||
在这个项目里,它的作用类似 Hyperf 容器里的共享服务/单例服务。比如 JWT 黑名单必须复用同一个对象,否则旧 refresh token 加入黑名单后,下一次请求就查不到了。
|
||
|
||
## 测试
|
||
|
||
```bash
|
||
.venv/bin/python -m compileall app tests
|
||
.venv/bin/python -m unittest tests.test_admin_login_flow
|
||
```
|
||
|
||
测试覆盖:
|
||
|
||
- admin 登录成功
|
||
- access token 访问当前用户成功
|
||
- refresh token 换新 token 成功
|
||
- 旧 refresh token 复用失败
|
||
- access token 调用 refresh 接口失败
|