Flask 的 url 处理器

发布于: 2020 年 07 月 20 日
Flask 的 url 处理器

前言

关于 url 处理器这个概念早在 Flask 0.7 版本就引入进去了,这是个很实用功能,这一次我们通过一个例子,感受一下 URL Processors 的实用之处.

例子

现在对于一个网站进行 i18n 化很重要,我们现在需要根据不同的语言然后获取不同的配置参数去配置我们的网站,标题导航栏,等等。那么如果让我们对原来下面的路由进行设置,应该怎么设计呢?

/post/<post_title>
/posts
# etc ...

很容易也很简洁的一个方案就是直接在 url 上加上语言变量 lang,像这样

/<lang>/post/<post_title>
/<lang>/posts

ok,url 重新设计完成了,让我们简单写一下路由函数

from flask import Flask
app = Flask(__name__)
@app.route('/<lang>/post/<post_title>')
def index(lang,post_title):
config = get_website_config_by_lang(lang)
return jsonify({"config":g.config,"title":post_title})
@app.route('/<lang>/posts')
def posts(lang):
config = get_website_config_by_lang(lang)
return jsonify({"config":g.config,"list":[1,2,3]})
def get_website_config_by_lang(lang):
if lang == 'eng':
return {"lang":"eng","title":"Peach"}
if lang == 'zh':
return {"lang":"zh","title":"桃子"}

现在问题来了,我们现在只有两个路由,每个路由里都需要根据 url 的 lang 去获取配置,那要是 20 个或者更多呢? 都这样子写就多出来冗余代码,那么有没有更好的办法呢?答案是肯定的,解决方法就是 url_value_processor

urlvalueprocessor

urlvalueprocessor 是做什么的呢?看一下官方定义

Register a URL value preprocessor function for all view functions in the application. These functions will be called before the before_request() functions.

The function can modify the values captured from the matched url before they are passed to the view.

The function is passed the endpoint name and values dict. The return value is ignored.

如官方文档说的那样,它可以在返回视图前,修改匹配到的 url 的 value 值,具体是什么样子,让我们接着看下去

使用方法

让我们试着用 urlvalueprocessor 去改造我们的代码

from flask import Flask,g,redirect,url_for,jsonify
app = Flask(__name__)
@app.url_value_preprocessor
def pull_lang_config(endpoint,values):
lang = values.pop('lang',None)
if lang:
g.config = get_website_config_by_lang(lang)
@app.route('/<lang>/post/<post_title>')
def index(post_title):
return jsonify({"config":g.config,"title":post_title})
@app.route('/<lang>/posts')
def posts():
return jsonify({"config":g.config,"list":[1,2,3]})
@app.route('/<lang>/test') # 一个测试
def test():
return redirect(url_for('posts'))
def get_website_config_by_lang(lang):
if lang == 'eng':
return {"lang":"eng","title":"Peach"}
if lang == 'zh':
return {"lang":"zh","title":"桃子"}

运行

尝试访问一下 /zh/post/test,看一下返回结果

{
"config": {
"lang": "zh",
"title": "\u6843\u5b50"
},
"title": "test"
}

再试一下 /eng/post/test:

{
"config": {
"lang": "eng",
"title": "Peach"
},
"title": "test"
}

其中 /<lang>/posts 结果也正常输出,等等,是不是忘了什么?让我们试一试 /<lang>/test,看一下输出结果:

werkzeug.routing.BuildError: Could not build url for endpoint 'posts'. Did you forget to specify values ['lang']?

好像报错了?这是为什么?因为我们在 urlvaluepreprocessor 的函数中把 lang 给从 values 出栈了,那么有没有解决办法呢?当然有的,往下看.

url_defaults

如果说 urlvalueprocessor 是为了从 url 中取出相应的 value 值把它用到其他地方,那么 urldefaults 则是将其他地方的值重新放回到 url 中,如果不放回,一旦使用 urlfor 就会出现上面的错误。

使用

让我们尝试在此改造上面的例子

from flask import Flask,jsonify,g, redirect,url_for
app = Flask(__name__)
@app.url_defaults
def add_lang(endpoint, values):
values.setdefault("lang",g.lang)
@app.url_value_preprocessor
def pull_lang_config(endpoint,values):
g.lang = values.pop('lang')
g.config = get_website_config_by_lang(g.lang)
@app.route('/<lang>/post/<post_title>')
def index(post_title):
return jsonify({"config":g.config,"title":post_title})
@app.route('/<lang>/posts')
def about():
return jsonify({"config":g.config,"list":[1,2,3]})
@app.route('/<lang>/test') # 一个测试
def test():
return redirect(url_for('posts'))
def get_website_config_by_lang(lang):
if lang == 'eng':
return {"lang":"eng","title":"Peach"}
if lang == 'zh':
return {"lang":"zh","title":"桃子"}

运行

让我们直接测试 /<lang>/test 这个接口,看一下结果:

{
"config": {
"lang": "eng",
"title": "Peach"
},
"list": [
1,
2,
3
]
}

大功告成

参考

Using URL Processors

发布于: 2020 年 07 月 20 日 阅读数: 3
用户头像

Leetao

关注

一个喜欢瞎折腾的程序员 2018.11.09 加入

微信公众号:【桃子的学习笔记】 个人网站:https://www.leetao94.cn

评论

发布
暂无评论
Flask 的 url 处理器