域名设置:


说明: SERVER_NAME内置属性的设置会影响全局URL,它主要有两个作用,第一个作用是在请求上下文之外生成绝对URL,当然如果设置也会影响请求上下文内的绝对URL,第二个作用是用于子域名的支持


#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# 51CTOBG: http://xmdevops.blog.51cto.com/
# Purpose:
#
"""
# 说明: 导入公共模块
from flask import Flask, url_for
# 说明: 导入其它模块
app = Flask(__name__)
# app.url_map.default_subdomain='www'
# app.config.update(SERVER_NAME='blog.51cto.com')
with app.test_request_context():
    print 'found notice: with request context full url is: %s' % (
        url_for('static', filename='css/style.css', _external=True),
    )
with app.app_context():
    print 'found notice: with app context full url is: %s' % (
        url_for('static', filename='css/style.css', _external=True),
    )
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=9000, debug=True)

说明: 如上代码在没有设置SERVER_NAME默认属性时,直接运行会报RuntimeError: Application was not able to create a URL adapter for request independent URL generation. You might be able to fix this by setting the SERVER_NAME config variable.说明url_for是依赖请求上下文的,若要生成不依赖于请求上下文(也就是说在请求上下文或程序上下文),最常见的就是异步发送邮件时url_for生成的邮件中的地址时就必须设置SERVER_NAME


子域实现:


说明: SERVER_NAME还用于子域名,因为FLASK在得知现有服务器名之前不能猜测出子域名部分,在请求上下文中如果没有设置SERVER_NAME,默认使用request.host,但是一旦设置SERVER_NAME,将全局影响全URL的主机部分,所以为了让FLASK以.分割识别子域名,强烈建议设置SERVER_NAME为指定域名,且指定名称的时候一定是SERVER_NAME='blog.51cto.com:9000'主域名加端口的形式,不然不会生效,如下就简单演示下常规项目开发中子域名的使用以及隔离资源文件及模版的方法,由于代码量有点多,就直接上传代码讲解

下载地址: http://1000eb.com/1kw8v

.
│  main.py
│
├─app
│  │  prod_config.py
│  │  test_config.py
│  │  __init__.py
│  │
│  ├─monitor
│  │  │  api.py
│  │  │  views.py
│  │  │  __init__.py
│  │  │
│  │  ├─static
│  │  │  └─js
│  │  │          main.js
│  │  │
│  │  └─templates
│  ├─static
│  └─templates
├─bin
└─doc

1. main.py为入口文件,调用app包下的create_app函数创建app实例,然后调用app.run来启动实例

2. prod_config.py中为生产环境配置,再本地测试环境尝试导入test_config.py为当前配置,SERVER_NAME就在test_config.py中声明的,发布生产环境时删掉test_config.py即可

3. monitor包中定义了monitor蓝图,创建蓝图时指定subdomain='monitor',使得我们访问monitor.blog.51cto.com时会自动访问monitor蓝图,但是需要注意的是视图函数或是模版中使用url_for()生成路径时强烈推荐加上蓝图名,如上url_for('.static', filename='js/main.js',  _external=True)/url_for('monitor.static', filename='js/main.js',  _external=True)生成的是http://monitor.blog.51cto.com/static/js/main.js,对应的文件是app/monitor/static/js/main.js文件,而url_for('static', filename='js/main.js',  _external=True)则生成的是https://blog.51cto.com/static/js/main.js,试图访问必然无法访问,一般遇到这种问题,可以用NGINX前端REWRITE重写下URL加一个默认子域名,如www即可解决

4. create_app函数主要加载prod_config.py中的配置,以及注册monitor包中的monitor蓝图



通配子域:


说明: 有时需要一个蓝图匹配多个子域名,比如51CTO给每个注册用户分配一个性域名类似http://xmdevops.blog.51cto.com/,且每个用户有自己的主页,以及不同的信息展示页面,此时就可以用动态URL


#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://xmdevops.blog.51cto.com/
# Purpose:
#
"""
# 说明: 导入公共模块
from flask import Blueprint, g
# 说明: 导入其它模块
monitor = Blueprint('monitor', __name__, subdomain='<subdomain>',
                    static_folder='static',
                    template_folder='templates')
@monitor.url_value_preprocessor
def set_site(endpoint, values):
    g.subdomain = values.pop('subdomain', None)
@monitor.url_defaults
def get_site(endpoint, values):
    values.update('subdomain', g.subdomain)
@monitor.before_request
def get_user():
    # g.user = WebUser.query.filter_by(name=g.subdomain).first_or_404()
    g.user = g.subdomain if g.subdomain else None
from . import api
from . import views

说明: 在之前的代码上仅此修改了app/monitor/__init__.py中的代码,在创建蓝图时候修改参数subdomain='<subdomain>',其实就是利用字符串转换器,我们希望输入不同的用户名子域名跳转到不同的用户主页,如上代码用到了LocalThread对象g,基于线程隔离的对象,如当访问http://user1.blog.51cto.com:9000时,由于我们DNS已经做了泛解析,指向了127.0.0.1(直接hosts文件添加模拟),所以request.host的值被置为user1.blog.51cto.com:9000,由于我们创建monitor蓝图时指定了subdomain='<subdomain>',正好匹配子域名的形式,所以将subdomain的值通过current_app.url_map.converts中的转换器转换后分别传给视图函数和内部编码后的URL,但是我们希望在传递给视图函数之前获取subdomain并存放到LocalThread g对象中以便线程隔离访问,FLASK为我们提供了monitor.url_defaults和monitor.url_value_preprocessor两个装饰器,两者都调用一个接收endpoint, values的函数,endpoint其实就是蓝图,values存储的是这个蓝图下的URL中动态名称和动态内容的映射关系(如subdomain: subdomain值),monitor.url_value_preprocessor会在转换数据后将数据从subdomain中弹出赋值给g对象,但是如果你此时直接访问的话坑定会报错说少参数,因为你把subdomain弹出了~,所以url_defaults的作用就是等URL处理完前期转换以及值处理后重新将值更新进去,最后在monitor.before_request处理请求之前将子域名换为用户对象~ 至于剩下的事情就是渲染页面了~

扩展: 其实FLASK除了支持动态子域名其实还支持动态前缀,如果要实现https://blog.51cto.com:9000/home/<subprefix>,来代替实现http://<subdomain>.blog.51cto.com:9000的效果,需要在注册蓝图时候添加url_prefix='/home/<subprefix>'然后利用@monitor.url_value_processer弹出subprefix的值到g对象中然后在@monitor.url_defaults中重新压入values,在线程隔离的g对象中存储后面处理就简单多了~