发送 Index 请求
让我们添加索引图书的 API,然后放入一些测试数据供后续使用。
移动 domain/model 到 books/domain/model。
创建 books/domain/gateway/book_manager.py:
from abc import ABC, abstractmethod
from ..model import Book
class BookManager(ABC):
@abstractmethod
def index_book(self, b: Book) -> str:
pass
此处也是在使用4层架构。项目越大,该架构的好处越明显。阅读更多。
添加 books/domain/gateway/__init__.py:
from .book_manager import BookManager
创建 books/infrastructure/search/es.py:
from dataclasses import asdict
from elasticsearch import Elasticsearch
from ...domain.gateway import BookManager
from ...domain.model import Book
INDEX_BOOK = "book_idx"
class ElasticSearchEngine(BookManager):
def __init__(self, address: str, page_size: int):
self.page_size = page_size
self.client = Elasticsearch(address)
def index_book(self, b: Book) -> str:
result = self.client.index(index=INDEX_BOOK, document=asdict(b))
return result['_id']
默认情况下,Elasticsearch 允许你将文档索引到尚不存在的索引中。 当你将文档索引到不存在的索引时,Elasticsearch 将会使用默认设置动态地创建索引。这在开发者不想显式地创建索引时会很方便。
安装 yaml
依赖:
pip3 install PyYAML
创建 books/infrastructure/config/config.py:
from dataclasses import dataclass
import yaml
@dataclass
class SearchConfig:
address: str
@dataclass
class ApplicationConfig:
page_size: int
@dataclass
class Config:
app: ApplicationConfig
search: SearchConfig
def parseConfig(filename: str) -> Config:
with open(filename, 'r') as f:
data = yaml.safe_load(f)
return Config(
ApplicationConfig(**data['app']),
SearchConfig(**data['search'])
)
添加 books/infrastructure/config/__init__.py:
from .config import Config, parseConfig
创建 config.yml:
app:
page_size: 10
search:
address: "http://localhost:9200"
警醒:
不要直接 git 提交 config.yml。可能会导致敏感数据泄露。如果非要提交的话,建议只提交配置格式模板。
比如:app: page_size: 10 search: address: ""
创建 books/application/executor/book_operator.py:
from ...domain.model import Book
from ...domain.gateway import BookManager
class BookOperator():
def __init__(self, book_manager: BookManager):
self.book_manager = book_manager
def create_book(self, b: Book) -> str:
return self.book_manager.index_book(b)
记得给所有相关子目录创建 __init__.py 文件。
创建 books/application/wire_helper.py:
from ..domain.gateway import BookManager
from ..infrastructure.config import Config
from ..infrastructure.search import ElasticSearchEngine
class WireHelper:
def __init__(self, engine: ElasticSearchEngine):
self.engine = engine
@classmethod
def new(cls, c: Config):
es = ElasticSearchEngine(c.search.address, c.app.page_size)
return cls(es)
def book_manager(self) -> BookManager:
return self.engine
创建 books/adapter/router.py:
import logging
from fastapi import FastAPI, HTTPException
from ..application.executor import BookOperator
from ..application import WireHelper
from ..domain.model import Book
class RestHandler:
def __init__(self, logger: logging.Logger, book_operator: BookOperator):
self._logger = logger
self.book_operator = book_operator
def create_book(self, b: Book):
try:
return self.book_operator.create_book(b)
except Exception as e:
self._logger.error(f"Failed to create: {e}")
raise HTTPException(status_code=400, detail="Failed to create")
def make_router(app: FastAPI, wire_helper: WireHelper):
rest_handler = RestHandler(
logging.getLogger("lr-full-text"),
BookOperator(wire_helper.book_manager())
)
@app.get("/")
async def welcome():
return {"status": "ok"}
@app.post("/books")
async def create_book(b: Book):
book_id = rest_handler.create_book(b)
return {"id": book_id}
用以下代码替换 main.py 中内容:
from fastapi import FastAPI
from books.adapter import make_router
from books.application import WireHelper
from books.infrastructure.config import parseConfig
CONFIG_FILENAME = "config.yml"
c = parseConfig(CONFIG_FILENAME)
wire_helper = WireHelper.new(c)
app = FastAPI()
make_router(app, wire_helper)
再次运行 server,然后使用 curl
进行测试:
uvicorn web.main:app --reload
样例请求:
curl -X POST \
-H "Content-Type: application/json" \
-d '{"title":"The Da Vinci Code","author":"Dan Brown","published_at":"2003-03-18","content":"In the Louvre, a curator is found dead. Next to his body, an enigmatic message. It is the beginning of a race to discover the truth about the Holy Grail."}' \
http://localhost:8000/books
样例响应:
{"id":"Te9HCo8BPyexJHELQDO_"}
放入测试数据
curl -X POST \
-H "Content-Type: application/json" \
-d '{"title":"Harry Potter and the Philosopher\u0027s Stone","author":"J.K. Rowling","published_at":"1997-06-26","content":"A young boy discovers he is a wizard and begins his education at Hogwarts School of Witchcraft and Wizardry, where he uncovers the mystery of the Philosopher‘s Stone."}' \
http://localhost:8000/books
curl -X POST \
-H "Content-Type: application/json" \
-d '{"title":"To Kill a Mockingbird","author":"Harper Lee","published_at":"1960-07-11","content":"Set in the American South during the Great Depression, the novel explores themes of racial injustice and moral growth through the eyes of young Scout Finch."}' \
http://localhost:8000/books
curl -X POST \
-H "Content-Type: application/json" \
-d '{"title":"The Lord of the Rings","author":"J.R.R. Tolkien","published_at":"1954-07-29","content":"A hobbit named Frodo Baggins embarks on a perilous journey to destroy a powerful ring and save Middle-earth from the Dark Lord Sauron."}' \
http://localhost:8000/books
curl -X POST \
-H "Content-Type: application/json" \
-d '{"title":"The Catcher in the Rye","author":"J.D. Salinger","published_at":"1951-07-16","content":"Holden Caulfield narrates his experiences in New York City after being expelled from prep school, grappling with themes of alienation, identity, and innocence."}' \
http://localhost:8000/books
curl -X POST \
-H "Content-Type: application/json" \
-d '{"title":"The Alchemist","author":"Paulo Coelho","published_at":"1988-01-01","content":"Santiago, a shepherd boy, travels from Spain to Egypt in search of a treasure buried near the Pyramids. Along the way, he learns about the importance of following one‘s dreams."}' \
http://localhost:8000/books
curl -X POST \
-H "Content-Type: application/json" \
-d '{"title":"The Hunger Games","author":"Suzanne Collins","published_at":"2008-09-14","content":"In a dystopian future, teenagers are forced to participate in a televised death match called the Hunger Games. Katniss Everdeen volunteers to take her sister‘s place and becomes a symbol of rebellion."}' \
http://localhost:8000/books
curl -X POST \
-H "Content-Type: application/json" \
-d '{"title":"1984","author":"George Orwell","published_at":"1949-06-08","content":"Winston Smith lives in a totalitarian society ruled by the Party led by Big Brother. He rebels against the oppressive regime but ultimately succumbs to its control."}' \
http://localhost:8000/books
curl -X POST \
-H "Content-Type: application/json" \
-d '{"title":"The Girl with the Dragon Tattoo","author":"Stieg Larsson","published_at":"2005-08-01","content":"Journalist Mikael Blomkvist and hacker Lisbeth Salander investigate the disappearance of a young woman from a wealthy family, uncovering dark secrets and corruption."}' \
http://localhost:8000/books
curl -X POST \
-H "Content-Type: application/json" \
-d '{"title":"Gone Girl","author":"Gillian Flynn","published_at":"2012-06-05","content":"On their fifth wedding anniversary, Nick Dunne‘s wife, Amy, disappears. As the media circus ensues and suspicions mount, Nick finds himself in a whirlwind of deception and betrayal."}' \
http://localhost:8000/books