内容简介:在 Flask 中,蓝图(Blueprint)通常是基于路径进行分派的,因此我们看到典型的注册代码一般类似这样:相对少见的另一种用法是,Blueprint 也可以通过子域名来分派,这涉及到程序结构上会有一些改变,同时也会带来一些新的问题(当然都是可以解决的)。使用子域名是大型网站的常规做法,同时也使得 URL 路径更有针对性,比如提供一个 https://api.mydomain.com/... 比起所有页面都堆到 https://mydomain.com/ 下面,看上去也显得更专业一些。我自己也在尝试通
在 Flask 中,蓝图(Blueprint)通常是基于路径进行分派的,因此我们看到典型的注册代码一般类似这样:
app.register_blueprint(home_bp, url_prefix='...')
相对少见的另一种用法是,Blueprint 也可以通过子域名来分派,这涉及到程序结构上会有一些改变,同时也会带来一些新的问题(当然都是可以解决的)。使用子域名是大型网站的常规做法,同时也使得 URL 路径更有针对性,比如提供一个 https://api.mydomain.com/... 比起所有页面都堆到 https://mydomain.com/ 下面,看上去也显得更专业一些。我自己也在尝试通过这种方式重构自己的网站,最开始尝试的是每个域名使用一个单独的 app 去管理,但很快发现如果一些比较小的功能也做成独立的网站,会带来比较多额外的管理负担。因此,把这些功能合并到一个app,对外又能通过子域名公开,是不错的做法。因此,我对这种实现做了一些尝试,并对遇到的问题和解决办法做一个记录,以供自己和其他朋友参考。
域名管理
首先需要注意的是,由于需要区分不同的域名,以前那种在开发环境下统一用 localhost 做域名的做法现在行不通了。大型的网站可能会考虑用自定义域名解析的方式实现统一管理,我们现在的场景没那么复杂,简单在 hosts 里面加几条记录也就足够了。
127.0.0.1 yuhao.space 127.0.0.1 www.yuhao.space 127.0.0.1 blog.yuhao.space
基本应用
添加域名记录以后,我们来写一个简单的测试程序,检查一下域名分派在开发环境下是否正常。这里需要注意的几个点:
- app.config['SERVER_NAME'] 需要指向基本域名,包括端口(Flask 默认为5000);
- 创建 Blueprint 需要添加 subdomain 参数指向子域名(除主域名外)。
我们模拟两个域名来测试:一个主域名和一个博客域名(blog)。
from flask import Flask, Blueprint home_bp = Blueprint('home', __name__) blog_bp = Blueprint('blog', __name__) @home_bp.route('/') def home_index(): return 'home index' @blog_bp.route('/') def blog_index(): return 'blog index' def create_app(): def register_blueprints(app): app.register_blueprint(home_bp) app.register_blueprint(blog_bp, subdomain='blog') app = Flask(__name__) app.config['SERVER_NAME'] = 'yuhao.space:5000' register_blueprints(app) return app app = create_app() if __name__ == '__main__': app.run(debug=True)
运行程序,然后打开浏览器,分别浏览如下地址:
- http://yuhao.space:5000/
- http://blog.yuhao.space:5000/
很好,一切正常。接下来看看在生产环境下表现如何。
使用 Nginx 作为反向代理
在生产环境下,我们基本上不会把 Flask 应用直接暴露在公网上,而是使用类似 Nginx 这样的服务器作为前端代理。这种部署模式会带来一些额外的复杂性,而且容易出错(特别是配置方面),需要仔细验证。同时,正式环境一般也要在公网上启用 HTTPS,这又要求我们有一个有效的证书。因为我已经用 Let's Encrypt 申请过证书,所以这里就偷懒直接拿来用了,想自行验证的读者请在本地生成一个测试证书,不想搞太麻烦的同学就接着往下看吧。
Nginx 的配置不太相关的部分就略过了,其实和普通网站基本没有太大区别:
server { listen 443 ssl; server_name yuhao.space *.yuhao.space; ssl_certificate ...; ssl_certificate_key ...; location / { proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Scheme $scheme; proxy_pass http://localhost:5000; } }
这里我们让所有域名都通过相同的服务端口,为了识别到域名,别忘了设置 HOST。
设置完毕并重新加载 Nginx,然后用生产模式运行应用:
FLASK_ENV=production FLASK_APP=app flask run
然后打开浏览器访问...404?怎么回事?回想一下代码能猜到,通过生产环境访问时,浏览器发来的 Host 已经不带 5000 端口号了,因此我们这里要对应修改一下。为了同时保证开发环境仍然正常工作,需要判断一下环境:
app = Flask(__name__) server_name = 'yuhao.space' env = os.getenv('FLASK_ENV', 'development') if env != 'production': server_name += ':5000' app.config['SERVER_NAME'] = server_name
当然,这不是足够灵活的生产代码,但我在这里希望作为示例尽量简单明了。
再次运行程序,这次访问应该正常了。
检查反向地址
地址访问正常只是成功的一半。另一半是从客户请求中生成可访问的地址————也就是我们熟悉的 url_for()
,同样应该检查它们是否工作正常。为此,我们把视图函数稍稍修改一下,让它们返回模板内容:
@home_bp.route('/') def home_index(): return render_template('home.html')
然后写一个简单的模板:
<a href="{{ url_for('home.home_index') }}">Home Index</a> <a href="{{ url_for('blog.blog_index') }}">Blog Index</a> <br/> <a href="{{ url_for('home.home_index', _external=True) }}">Home Index</a> <a href="{{ url_for('blog.blog_index', _external=True) }}">Blog Index</a>
好消息是,内部地址是正确的; 坏消息则是外部地址(使用 _external
参数)路径虽然没错,但却返回了 http:// 的地址,这可不是我们想要的结果。
一个简单粗暴的处理办法是, 为 url_for 强制指定协议:
<a href="{{ url_for('home.home_index', _external=True, _scheme='https') }}">Home Index</a>
但可想而知,如果网站有很多链接的话,这样会增加不小的工作量。
从官方文档和代码中的备注来看,应用程序配置中有一项 PREFERED_URL_SCHEME
似乎应当与此有关,但文档对此解释不太明确,我尝试过添加该配置也不起作用。Stackoverflow 和 Github 上也有人提过类似的问题: PREFERRED_URL_SCHEME doesn't seem to work in Jinja2 Flask Template
。 官方对此也没有明确的答复。
既然没有正规途径,只要自己解决了。所幸 url_for 已经有参数可以利用,所以这也并不难。我们只要定义一个模板函数来强制指定 scheme 就好了:
def external_url_for(endpoint, **values): values.setdefault('_external', True) values.setdefault('_scheme', 'https') return url_for(endpoint, **values) ... app.add_template_global(external_url_for, 'external_url_for')
然后在模板中用 external_url_for 代替 url_for 调用即可。
结语
本文讨论了基于子域名的 Flask Blueprint 开发实践和相关问题的处理。要处理多个域名,另一种做法是通过 WSGI 接口分配多个应用,这种做法在 官方文档 Application Dispatching 部分 有所涉及。但我觉个这种方法略“重”,因此还是走了 Blueprint 的路子。如果子应用的规模很大,那么单独分配 app 或许是更灵活的做法。从实践中我们也可以体会到,尽管 Flask 在个别地方功能略显不足,但还是给我们提供了很多灵活性,值得好好去挖掘。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 「Flask笔记」 蓝图
- flask蓝图构建小项目
- 5. 使用Flask蓝图(blueprint)
- flask使用蓝图规划大型项目
- React 16.x 蓝图[双语版]
- 简单解决大型 Flask 蓝图的路由划分
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Go程序设计语言
艾伦 A. A. 多诺万 / 李道兵、高博、庞向才、金鑫鑫、林齐斌 / 机械工业出版社 / 2017-5 / 79
本书由《C程序设计语言》的作者Kernighan和谷歌公司Go团队主管Alan Donovan联袂撰写,是学习Go语言程序设计的指南。本书共13章,主要内容包括:Go的基础知识、基本结构、基本数据类型、复合数据类型、函数、方法、接口、goroutine、通道、共享变量的并发性、包、go工具、测试、反射等。 本书适合作为计算机相关专业的教材,也可供Go语言爱好者阅读。一起来看看 《Go程序设计语言》 这本书的介绍吧!