API Star:一个 Python 3 的 API 框架

为了在 Python 中快速构建 API,我主要依赖于Flask。最近我遇到了一个名为 “API Star” 的基于 Python 3 的新 API 框架。由于几个原因,我对它很感兴趣。首先,该框架包含 Python 新特点,如类型提示和 asyncio。而且它再进一步为开发人员提供了很棒的开发体验。我们很快就会讲到这些功能,但在我们开始之前,我首先要感谢 Tom Christie,感谢他为 Django REST Framework 和 API Star 所做的所有工作。

现在说回 API Star —— 我感觉这个框架很有成效。我可以选择基于 asyncio 编写异步代码,或者可以选择传统后端方式就像 WSGI 那样。它配备了一个命令行工具 ——apistar来帮助我们更快地完成工作。它支持 Django ORM 和 SQLAlchemy,这是可选的。它有一个出色的类型系统,使我们能够定义输入和输出的约束,API Star 可以自动生成 API 的模式(包括文档),提供验证和序列化功能等等。虽然 API Star 专注于构建 API,但你也可以非常轻松地在其上构建 Web 应用程序。在我们自己构建一些东西之前,所有这些可能都没有意义的。

开始

加群923414804免费获取数十套PDF资料,助力python学习

我们将从安装 API Star 开始。为此实验创建一个虚拟环境是一个好主意。如果你不知道如何创建一个虚拟环境,不要担心,继续往下看。

Python

1pip install apistar

(译注:上面的命令是在 Python 3 虚拟环境下使用的)

如果你没有使用虚拟环境或者你的 Python 3 的pip名为pip3,那么使用pip3 install apistar代替。

一旦我们安装了这个包,我们就应该可以使用apistar命令行工具了。我们可以用它创建一个新项目,让我们在当前目录中创建一个新项目。

Python

1apistar new .

现在我们应该创建两个文件:app.py,它包含主应用程序,然后是test.py,它用于测试。让我们来看看app.py文件:

Python

from apistar import Include, Route

from apistar.frameworks.wsgi import WSGIApp as App

from apistar.handlers import docs_urls, static_urls

def welcome(name=None):

    if name is None:

        return {'message': 'Welcome to API Star!'}

    return {'message': 'Welcome to API Star, %s!' % name}

routes = [

    Route('/', 'GET', welcome),

    Include('/docs', docs_urls),

    Include('/static', static_urls)

]

app = App(routes=routes)

if __name__ == '__main__':

    app.main()

在我们深入研究代码之前,让我们运行应用程序并查看它是否正常工作。我们在浏览器中输入http://127.0.0.1:8080/,我们将得到以下响应:

Python

1{"message": "Welcome to API Star!"}

如果我们输入:http://127.0.0.1:8080/?name=masnun

Python

1{"message": "Welcome to API Star, masnun!"}

同样的,输入http://127.0.0.1:8080/docs/,我们将看到自动生成的 API 文档。

现在让我们来看看代码。我们有一个welcome函数,它接收一个名为name的参数,其默认值为None。API Star 是一个智能的 API 框架。它将尝试在 url 路径或者查询字符串中找到name键并将其传递给我们的函数,它还基于其生成 API 文档。这真是太好了,不是吗?

然后,我们创建一个Route和Include实例的列表,并将列表传递给App实例。Route对象用于定义用户自定义路由。顾名思义,Include包含了在给定的路径下的其它 url 路径。

路由

路由很简单。当构造App实例时,我们需要传递一个列表作为routes参数,这个列表应该有我们刚才看到的Route或Include对象组成。对于Route,我们传递一个 url 路径,http 方法和可调用的请求处理程序(函数或者其他)。对于Include实例,我们传递一个 url 路径和一个Routes实例列表。

路径参数

我们可以在花括号内添加一个名称来声明 url 路径参数。例如/user/{user_id}定义了一个 url,其中user_id是路径参数,或者说是一个将被注入到处理函数(实际上是可调用的)中的变量。这有一个简单的例子:

Python


from apistar import Route

from apistar.frameworks.wsgi import WSGIApp as App

def user_profile(user_id: int):

    return {'message': 'Your profile id is: {}'.format(user_id)}

routes = [

    Route('/user/{user_id}', 'GET', user_profile),

]

app = App(routes=routes)

if __name__ == '__main__':

    app.main()

如果我们访问http://127.0.0.1:8080/user/23,我们将得到以下响应:

Python

1{"message": "Your profile id is: 23"}

但如果我们尝试访问http://127.0.0.1:8080/user/some_string,它将无法匹配。因为我们定义了user_profile函数,且为user_id参数添加了一个类型提示。如果它不是整数,则路径不匹配。但是如果我们继续删除类型提示,只使用user_profile(user_id),它将匹配此 url。这也展示了 API Star 的智能之处和利用类型和好处。

包含/分组路由

有时候将某些 url 组合在一起是有意义的。假设我们有一个处理用户相关功能的user模块,将所有与用户相关的 url 分组在/user路径下可能会更好。例如/user/new、/user/1、/user/1/update等等。我们可以轻松地在单独的模块或包中创建我们的处理程序和路由,然后将它们包含在我们自己的路由中。

让我们创建一个名为user的新模块,文件名为user.py。我们将以下代码放入这个文件:

Python

from apistar import Route

def user_new():

    return {"message": "Create a new user"}

def user_update(user_id: int):

    return {"message": "Update user #{}".format(user_id)}

def user_profile(user_id: int):

    return {"message": "User Profile for: {}".format(user_id)}

user_routes = [

    Route("/new", "GET", user_new),

    Route("/{user_id}/update", "GET", user_update),

    Route("/{user_id}/profile", "GET", user_profile),

]

现在我们可以从 app 主文件中导入user_routes,并像这样使用它:

Python

from apistar import Include

from apistar.frameworks.wsgi import WSGIApp as App

from user import user_routes

routes = [

    Include("/user", user_routes)

]

app = App(routes=routes)

if __name__ == '__main__':

    app.main()

现在/user/new将委托给user_new函数。

访问查询字符串/查询参数

查询参数中传递的任何参数都可以直接注入到处理函数中。比如 url/call?phone=1234,处理函数可以定义一个phone参数,它将从查询字符串/查询参数中接收值。如果 url 查询字符串不包含phone的值,那么它将得到None。我们还可以为参数设置一个默认值,如下所示:

Python


def welcome(name=None):

    if name is None:

        return {'message': 'Welcome to API Star!'}

    return {'message': 'Welcome to API Star, %s!' % name}

在上面的例子中,我们为name设置了一个默认值None。

注入对象

通过给一个请求程序添加类型提示,我们可以将不同的对象注入到视图中。注入请求相关的对象有助于处理程序直接从内部访问它们。API Star 内置的http包中有几个内置对象。我们也可以使用它的类型系统来创建我们自己的自定义对象并将它们注入到我们的函数中。API Star 还根据指定的约束进行数据验证。

让我们定义自己的User类型,并将其注入到我们的请求处理程序中:

Python


from apistar import Include, Route

from apistar.frameworks.wsgi import WSGIApp as App

from apistar import typesystem

class User(typesystem.Object):

    properties = {

    'name': typesystem.string(max_length=100),

    'email': typesystem.string(max_length=100),

    'age': typesystem.integer(maximum=100, minimum=18)

    }

    required = ["name", "age", "email"]

def new_user(user: User):

    return user

routes = [

    Route('/', 'POST', new_user),

]

app = App(routes=routes)

if __name__ == '__main__':

    app.main()

现在如果我们发送这样的请求:

Python


curl -X POST \

  http://127.0.0.1:8080/ \

  -H 'Cache-Control: no-cache' \

  -H 'Content-Type: application/json' \

  -d '{"name": "masnun", "email": "masnun@gmail.com", "age": 12}'

猜猜发生了什么?我们得到一个错误,说年龄必须等于或大于 18。类型系允许我们进行智能数据验证。如果我们启用了docsurl,我们还将自动记录这些参数。

发送响应

如果你已经注意到,到目前为止,我们只可以传递一个字典,它将被转换为 JSON 并作为默认返回。但是,我们可以使用apistar中的Response类来设置状态码和其它任意响应头。这有一个简单的例子:

Python


from apistar import Route, Response

from apistar.frameworks.wsgi import WSGIApp as App

def hello():

    return Response(

    content="Hello".encode("utf-8"),

    status=200,

    headers={"X-API-Framework": "API Star"},

    content_type="text/plain"

    )

routes = [

    Route('/', 'GET', hello),

]

app = App(routes=routes)

if __name__ == '__main__':

    app.main()

它应该返回纯文本响应和一个自定义标响应头。请注意,content应该是字节,而不是字符串。这就是我编码它的原因。

继续

我刚刚介绍了 API Star 的一些特性,API Star 中还有许多非常酷的东西,我建议通过Github Readme文件来了解这个优秀框架所提供的不同功能的更多信息。我还将尝试在未来几天内介绍关于 API Star 的更多简短的,集中的教程。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容