Sanic框架实战

       

1. 入门

Sanic 是一款类似Flask的Web服务器,它运行在Python 3.5+上。

除了与Flask功能类似之外,它还支持异步请求处理,这意味着你可以使用Python3.5 中新的异步/等待语法,使你的程序运行更加快速。

1.1 简单起步

from sanic import Sanic
from sanic.response import json

app = Sanic()

@app.route("/")
async def test(request):
    return json({"hello": "world"})

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000)
  1. 保存到main.py文件,运行文件python3 main.py
  2. 打开URLhttp://0.0.0.0:8000,可以看到网页显示 Hello World信息。

2. 路由

路由允许用户为不同的URL地址指定处理的函数。

一个基本的路由就像下面的例子,而app就是Sanic类的一个实例。

from sanic.response import json

@app.route("/")
async def test(request):
    return json({ "hello": "world" })

当地址http://server.url/被访问时(服务的基础地址),根地址/就会被路由匹配一个定义了返回JSON对象的test函数。

必须使用async def语法定义函数,来保证其可以进行异步处理。

2.1 请求参数

Sanic的基础路由支持请求参数的操作。

如果需要指定参数,请使用尖括号<PARAM>将指定参数括起来。请求参数将作为路由函数的关键字参数。

from sanic.response import text

@app.route('/tag/<tag>')
async def tag_handler(request, tag):
    return text('Tag - {}'.format(tag))

如果需要指定添加的参数的类型,则要在参数名字后面添加:type指定参数类型。如果参数与指定的参数类型不匹配,则Sanic会抛出NotFound的异常,从而导致页面出现404: Page not found的错误。

from sanic.response import text

@app.route('/number/<integer_arg:int>')
async def integer_handler(request, integer_arg):
    return text('Integer - {}'.format(integer_arg))

@app.route('/number/<number_arg:number>')
async def number_handler(request, number_arg):
    return text('Number - {}'.format(number_arg))

@app.route('/person/<name:[A-z]>')
async def person_handler(request, name):
    return text('Person - {}'.format(name))

@app.route('/folder/<folder_id:[A-z0-9]{0,4}>')
async def folder_handler(request, folder_id):
    return text('Folder - {}'.format(folder_id))

2.2 HTTP 请求类型

默认情况下,一个路由会定义一个仅仅适用于URL的GET请求。然而,@app.route装饰器接受一个可选的参数methods,它允许定义的函数使用列表中任何一个的HTTP方法。

from sanic.response import text

@app.route('/post', methods=['POST'])
async def post_handler(request):
    return text('POST request - {}'.format(request.json))

@app.route('/get', methods=['GET'])
async def get_handler(request):
    return text('GET request - {}'.format(request.args))

这里还有一个可选的host参数(列表或是字符串)。它限制了给主机的路由。如果存在一个没有主机的路由,它将是一个默认值。

@app.route('/get', methods=['GET'], host='example.com')
async def get_handler(request):
    return text('GET request - {}'.format(request.args))

# if the host header doesn't match example.com, this route will be used
@app.route('/get', methods=['GET'])
async def get_handler(request):
    return text('GET request in default - {}'.format(request.args))

这里还有一种快速使用装饰器的方法:

from sanic.response import text

@app.post('/post')
async def post_handler(request):
    return text('POST request - {}'.format(request.json))

@app.get('/get')
async def get_handler(request):
    return text('GET request - {}'.format(request.args))

2.3 add_route方法

就像上文提到的,路由通常使用@app.route装饰器进行添加的。但是,这个装饰器只是app.add_route方法的一个封装。它看起来像下面这样:

from sanic.response import text

# Define the handler functions
async def handler1(request):
    return text('OK')

async def handler2(request, name):
    return text('Folder - {}'.format(name))

async def person_handler2(request, name):
    return text('Person - {}'.format(name))

# Add each handler function as a route
app.add_route(handler1, '/test')
app.add_route(handler2, '/folder/<name>')
app.add_route(person_handler2, '/person/<name:[A-z]>', methods=['GET'])

2.4 利用url_for生成URL

Sanic提供了一个根据处理函数名字生成URL的方法url_for。在应用中,它使用处理程序的名字来有效避免使用实际的网络路径。

@app.route('/')
async def index(request):
    # generate a URL for the endpoint `post_handler`
    url = app.url_for('post_handler', post_id=5)
    # the URL is `/posts/5`, redirect to it
    return redirect(url)


@app.route('/posts/<post_id>')
async def post_handler(request, post_id):
    return text('Post - {}'.format(post_id))

使用url_for需要注意的是:

  • 传递给url_for的关键字如果不是请求参数,将包含在URL 的查询字符串中。
url = app.url_for('post_handler', post_id=5, arg_one='one', arg_two='two')
# /posts/5?arg_one=one&arg_two=two
  • 可以传递多个参数给url_for函数。
url = app.url_for('post_handler', post_id=5, arg_one=['one', 'two'])
# /posts/5?arg_one=one&arg_one=two
  • 还可以传递一些特殊的参数给url_for方法来构造一些特殊的URL,诸如:_anchor,_external,_scheme,_server
url = app.url_for('post_handler', post_id=5, arg_one='one', _anchor='anchor')
# /posts/5?arg_one=one#anchor

url = app.url_for('post_handler', post_id=5, arg_one='one', _external=True)
# //server/posts/5?arg_one=one
# _external requires passed argument _server or SERVER_NAME in app.config or url will be same as no _external

url = app.url_for('post_handler', post_id=5, arg_one='one', _scheme='http', _external=True)
# http://server/posts/5?arg_one=one
# when specifying _scheme, _external must be True

# you can pass all special arguments one time
url = app.url_for('post_handler', post_id=5, arg_one=['one', 'two'], arg_two=2, _anchor='anchor', _scheme='http', _external=True, _server='another_server:8888')
# http://another_server:8888/posts/5?arg_one=one&arg_one=two&arg_two=2#anchor
  • 所有的参数都必须正确地传递给url_for方法来构造URL。如果未提供参数或指定参数不匹配,将抛出URLBuildError的错误。

2.5 WebSocket 路由

使用@app.websocket装饰器定义WebSocket协议的路由。

@app.websocket('/feed')
async def feed(request, ws):
    while True:
        data = 'hello!'
        print('Sending: ' + data)
        await ws.send(data)
        data = await ws.recv()
        print('Received: ' + data)

或者,使用app.add_websocket_route方法来代替@app.websocket装饰器。

async def feed(request, ws):
    pass

app.add_websocket_route(my_websocket_handler, '/feed')

WebSocket路由的处理程序将请求作为第一个参数传递,并将WebSocket协议对象作为第二个参数传递。而协议对象具有sendrecv两个方法来进行数据的传送和接收。

3. 请求数据

当接收端接收到一个HTTP请求的时候,路由函数就会传递一个Request对象。

以下的变量可以作为Request对象的属性进行访问。

  • json(任何类型)-JSON格式的数据
from sanic.response import json

@app.route("/json")
def post_json(request):
    return json({ "received": True, "message": request.json })
  • arg(dict类型)-查询字符串变量。一个查询的字符串是部分的URL,类似于?key1=value1&key2=value2,如果要解析这个URL,那么arg字典看起来就像{'key1': ['value1'], 'key2': ['value2']},这个请求将使用query_string变量来保存未解析的字符串的值。
from sanic.response import json

@app.route("/query_string")
def query_string(request):
    return json({ "parsed": True, "args": request.args, "url": request.url, "query_string": request.query_string })
  • raw_args(dict类型)-在许多情况下,你只需要访问一段很小的url参数。对于前文的URL的?key1=value1&key2=value2raw_args字典看起来就像这样{'key1': 'value1', 'key2': 'value2'}

  • files(File对象的字典)-具有名称,正文和类型的文件列表。

from sanic.response import json

@app.route("/files")
def post_json(request):
    test_file = request.files.get('test')

    file_parameters = {
        'body': test_file.body,
        'name': test_file.name,
        'type': test_file.type,
    }

    return json({ "received": True, "file_names": request.files.keys(), "test_file_parameters": file_parameters })
  • form(dict类型)-发布表单数据
from sanic.response import json

@app.route("/form")
def post_json(request):
    return json({ "received": True, "form_data": request.form, "test": request.form.get('test') })
  • body(字节类型)-发布正文。这个属性允许检索请求的原始数据,而无需理会数据的类型。
from sanic.response import text

@app.route("/users", methods=["POST",])
def create_user(request):
    return text("You are trying to create a user with the following POST: %s" % request.body)
  • ip(str类型)-请求者的IP地址

  • app-对正在处理此请求的Sanic应用程序对象的引用。在无法访问全局app对象或其它处理程序的时候很有用。

from sanic.response import json
from sanic import Blueprint

bp = Blueprint('my_blueprint')

@bp.route('/')
async def bp_root(request):
    if request.app.config['DEBU
  • 7
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值