Sanic 路由進階

veelion發表於2019-03-26

上一節我們學習了基本的Sanic 路由知識,本節我們深入Sanic路由,這包括:url_for方法,WebSocket路由,使用者定義的路由名稱和為靜態檔案(Sanic static)建立URL。

Sanic route路由進階

url_for方法建立URL

Sanic提供了一個url_for方法,來基於處理函式名生成URL。它可以用於避免在程式碼中硬編碼url路徑,取而代之的是僅僅用處理函式名。比如:

from sanic.response import redirect

@app.route('/')
async def index(request):
    # 引用處理函式名稱`user_handler`生成一個URL
    url = app.url_for('user_handler', user_id=5)
    # 生成的url是:`/users/5`, redirect to it
    return redirect(url)

@app.route('/users/<user_id>')
async def user_handler(request, user_id):
    return text('User - {}'.format(user_id))

使用url_for需要注意:

(1)傳遞給url_for的關鍵詞引數如果不是請求引數就會被加入到URL的查詢字串(query string),例如:

url = app.url_for('user_handler', user_id=5, arg_one='one', arg_two='two')
# /users/5?arg_one=one&arg_two=two

(2)多值引數可以傳遞給url_for,比如:

url = app.url_for('user_handler', user_id=5, arg_one=['one', 'two'])
# /users/5?arg_one=one&arg_one=two

(3)有些特殊引數(_anchor, _external, _scheme, _server)傳遞給url_for會建立特殊的url(其中,_method目前尚未支援)。例如:

url = app.url_for('user_handler', user_id=5, arg_one='one', _anchor='anchor')
# /users/5?arg_one=one#anchor

url = app.url_for('user_handler', user_id=5, arg_one='one', _external=True)
# //server/users/5?arg_one=one
# _external requires you to pass an argument _server or set SERVER_NAME in app.config if not url will be same as no _external

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

# you can pass all special arguments at once
url = app.url_for('user_handler', user_id=5, arg_one=['one', 'two'], arg_two=2, _anchor='anchor', _scheme='http', _external=True, _server='another_server:8888')
# http://another_server:8888/users/5?arg_one=one&arg_one=two&arg_two=2#anchor

(4)所有有效的請求引數必須傳遞給url_for來建立URL。如果有一個請求引數沒有提供,或者一個引數的型別不匹配,就會產生URLBuildError異常。

Websocket 路由

Websocket協議的路由可以通過@app.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方法可以取代裝飾器:

async def feed(request, ws):
    pass

app.add_websocket_route(my_websocket_handler, '/feed')

WebSocket路由的處理函式別呼叫時,第一個引數是request(跟http路由一樣),第二個引數是WebSocket協議物件,它有sendrecv兩個方法分別用以傳送和接收資料。

strict_slashes引數

我們可以決定路由是否嚴格匹配末尾的斜槓,通過引數strict_slashes進行設定:

# 設定整個app所有路由的預設strict_slashes值
app = Sanic('strict_slash', strict_slashes=True)

# 可以為個別路由重新設定strict_slashes 值
# 因為strict_slashes=False,訪問路徑`/get`和`/get/`都可以得到`OK`
@app.get('/get', strict_slashes=False)
def handler(request):
    return text('OK')

# 該引數同樣適用於blueprints
bp = Blueprint('bp_strict_slash', strict_slashes=True)

@bp.get('/bp/get', strict_slashes=False)
def handler(request):
    return text('OK')

app.blueprint(bp)

strict_slashes=True時,路由設定的路徑必須和訪問的URL路徑完全一致才行,也就是路由設定的路徑以斜槓/結尾則訪問的URL也必須以斜槓/結尾;

strict_slashes=False時,訪問URL後面可加可不加斜槓/,比如上例中訪問路徑/get/get/都可以得到OK

使用者定義的路由名稱

通過傳遞引數name給路由裝飾器就可以自定義路由名稱,該引數會覆蓋預設使用hanler.__name__屬性的路由名稱。這個路由名稱是給url_for使用的。

app = Sanic('test_named_route')

@app.get('/get', name='get_handler')
def handler(request):
    return text('OK')

# 當為上面的路由使用`url_for`時,
# 應該使用 `app.url_for('get_handler')`
# 而不是`app.url_for('handler')`


# 同樣適用於blueprints
bp = Blueprint('test_named_bp')

@bp.get('/bp/get', name='get_handler')
def handler(request):
    return text('OK')

app.blueprint(bp)

# 應該使用 `app.url_for('test_named_bp.get_handler')`
# 而不是 `app.url_for('test_named_bp.handler')`


# 同一個url的不同方法可以使用不同的名字,但是對應`url_for`來說,它們都是對應同一個url:

@app.get('/test', name='route_test')
def handler(request):
    return text('OK')

@app.post('/test', name='route_post')
def handler2(request):
    return text('OK POST')

@app.put('/test', name='route_put')
def handler3(request):
    return text('OK PUT')

# 下面生成的url都相同,可以使用它們中的任何一個:
# '/test'
app.url_for('route_test')
# app.url_for('route_post')
# app.url_for('route_put')

# 同一個處理函式名對應不同方法時,
# 就需要知道`name` (為了`url_for`)
@app.get('/get')
def handler(request):
    return text('OK')

@app.post('/post', name='post_handler')
def handler(request):
    return text('OK')

# 因此:
# app.url_for('handler') == '/get'
# app.url_for('post_handler') == '/post'

為靜態檔案建立URL

Sanic 支援使用url_for方法建立靜態檔案url。如果靜態url指向一個資料夾,url_for方法的filename引數可以忽略。

app = Sanic('test_static')
app.static('/static', './static')
app.static('/uploads', './uploads', name='uploads')
app.static('/the_best.png', '/home/ubuntu/test.png', name='best_png')

bp = Blueprint('bp', url_prefix='bp')
bp.static('/static', './static')
bp.static('/uploads', './uploads', name='uploads')
bp.static('/the_best.png', '/home/ubuntu/test.png', name='best_png')
app.blueprint(bp)

# then build the url
app.url_for('static', filename='file.txt') == '/static/file.txt'
app.url_for('static', name='static', filename='file.txt') == '/static/file.txt'
app.url_for('static', name='uploads', filename='file.txt') == '/uploads/file.txt'
app.url_for('static', name='best_png') == '/the_best.png'

# blueprint url building
app.url_for('static', name='bp.static', filename='file.txt') == '/bp/static/file.txt'
app.url_for('static', name='bp.uploads', filename='file.txt') == '/bp/uploads/file.txt'
app.url_for('static', name='bp.best_png') == '/bp/static/the_best.png'

猿人學banner宣傳圖

我的公眾號:猿人學 Python 上會分享更多心得體會,敬請關注。

***版權申明:若沒有特殊說明,文章皆是猿人學 yuanrenxue.com 原創,沒有猿人學授權,請勿以任何形式轉載。***

相關文章