后端服务器数据验证和序列化
前端浏览器传来的数据是使用json格式的字符串,而后端服务器调用函数时是需要函数参数的,那么如何将json中的数据转化成为函数调用的参数就成了一个问题。本文主要使用python
作为编程语言,aiohttp
来作为服务器框架。
基本方法
最简单的方法就是在每一个endpoint中从传来的request直接抽取参数,但是这样的话代码很麻烦,需要检查传来的参数项是否多了还是少了,类型是否正确,然后返回相应的状态码。
就像下面代码一样1:
from aiohttp import web from model.foo_model import FooModel from service.foo_service import FooService class RequestHandler(web.View): async def post(self): self.service = self.request.app[FooService] self.api_version = request.match_info.get("api_version", 1) self.name = request.match_info.get("name", None) try: body = await self.request.json() foo_model = FooModel( name=self.name, version=self.api_version, **body ) except ValueError as ex: raise web.HTTPBadRequest( reason=f"Failed to process request body." ) from ex await self.service.persist(foo_model)
使用类或wrapper
进行封装
之前在廖雪峰Python教程的服务器开发实战中无意实现了类似于Data Serialization
的功能2。他在教程中很简单地讲了一下,也就是将每个handler函数的参数名称给提取出来,然后在调用的时候从requst中自动提取数据,然后再用这些数据调用函数(他教程里的@get
和@post
也是自己实现的,新版aiohttp
已经实现了)。
@get('/api/comments') def api_comments(*, page='1'): pass
URL处理函数不一定是一个coroutine,因此我们用RequestHandler()来封装一个URL处理函数。 RequestHandler是一个类,由于定义了__call__()方法,因此可以将其实例视为函数。
RequestHandler目的就是从URL函数中分析其需要接收的参数,从request中获取必要的参数,调用URL函数,然后把结果转换为web.Response对象,这样,就完全符合aiohttp框架的要求:
class RequestHandler(object): def __init__(self, app, fn): self._app = app self._func = fn ... @asyncio.coroutine def __call__(self, request): kw = ... 获取参数 r = yield from self._func(**kw) return r
Serializing/Deserializing
将数据从request中转化为python相应类型数据叫做Serializing,将python中的数据转化为response的数据叫做Deserializing。很多服务器框架集成了相关的工作,比如Django Rest Framework。
FastAPI
使用了Pydantic
来做数据序列化和验证3,不会过度影响业务逻辑,这使得代码更加简洁高效。考虑到FastAPI
的热度,开发的便利性,并且在某些榜单上的测试性能上超过aiohttp
,目前考虑将FastAPI
纳入技术栈中。