FastAPI+Jinja2开发Web应用如何遵循DRY原则避免重复编码
FastAPI 接口与前端路由逻辑复用优化方案
方案1:抽离公共业务处理函数(最易落地,零架构调整)
你当前重复的逻辑是「参数接收→过滤器构造→调用CRUD拿数据」这一段,完全可以把这部分从两个路由里抽出来,放到crud/article.py或者单独的services/article.py里作为公共方法。
举个例子,你可以在crud/article.py里新增公共方法:
# crud/article.py 新增代码 async def fetch_articles_by_filter(tag: str, db: AsyncIOMotorClient) -> DisplayArticles: filters = ArticleFilters(tag=tag) dbarticles = await get_articles_with_filters(db, filters) return DisplayArticles(articles=dbarticles, articles_count=len(dbarticles))
之后API路由可以简化为:
# api/api_v1/endpoints/article.py @router.get("/articles", response_model=DisplayArticles, tags=["articles"]) async def get_articles( tag: str = "", db: AsyncIOMotorClient = Depends(get_database), ): article_data = await fetch_articles_by_filter(tag, db) return create_aliased_response(article_data)
对应的webapp路由直接复用同一个公共方法即可:
# webapp/article/article.py @router.get("/articles", tags=["webapp_articles"]) async def webapp_get_articles( tag: str = "", db: AsyncIOMotorClient = Depends(get_database), request: Request = Depends(), ): article_data = await fetch_articles_by_filter(tag, db) return templates.TemplateResponse( "article_list.html", context={"request": request, "articles": article_data.articles, "count": article_data.articles_count} )
这个方案不需要调整现有架构,只要把重复的业务逻辑抽成公共方法就行,后续加新接口只要先写公共方法,再分别写API和webapp的路由返回逻辑,完全不会出现代码重复。
方案2:同一路由兼容多返回类型(适合小项目快速迭代)
如果你不想维护两套路由,可以直接在同一个路由里根据请求头的Accept字段判断返回JSON还是HTML,不需要写两个不同的路由函数:
# 可以放在共享路由文件,或者直接放在API层 @router.get("/articles", tags=["articles"]) async def get_articles( tag: str = "", db: AsyncIOMotorClient = Depends(get_database), request: Request = Depends(), ): # 公共业务逻辑和之前一致 filters = ArticleFilters(tag=tag) dbarticles = await get_articles_with_filters(db, filters) article_data = DisplayArticles(articles=dbarticles, articles_count=len(dbarticles)) # 根据请求类型返回对应格式 accept = request.headers.get("Accept", "") if "text/html" in accept: # 前端页面请求返回模板 return templates.TemplateResponse( "article_list.html", context={"request": request, **article_data.dict()} ) # 默认返回JSON格式给API调用 return create_aliased_response(article_data)
这个方案的好处是你只要维护一套路由,后续加参数、改逻辑只要改一次就行,缺点是如果接口和前端页面的参数差异变大,后期维护成本会升高,适合小项目或者逻辑高度一致的场景。
可选架构优化建议
如果后续项目要长期扩展,可以新增一层services目录专门放业务逻辑,把CRUD层只保留纯数据库读写操作,所有业务组装、参数校验逻辑都放到service层,不管是API路由还是webapp路由都只调用service层的方法,完全避免逻辑重复,调整后的结构参考:
app/ - services/ -- article.py # 存放文章相关的业务逻辑,比如拼接过滤条件、关联查询标签、计算统计数据等 -- tag.py
这样架构分层更清晰,不管后续是加小程序接口、管理后台接口还是其他端的入口,都直接复用service层的逻辑,不会出现重复代码。
内容的提问来源于stack exchange,提问作者jonbon
相关产品推荐
相关产品推荐

