1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
| class Flask(object): """ Flask 类实现了一个 WSGI 应用,只要将包或者模块的名字传递给它 一旦创建的时候,它将首先注册视图函数、路由映射、模板配置等等几个重要的对象 传入包的名字是用于解决应用内部资源的引用,具体请查看 open_resource 函数 一般情况下,你只需要这样创建: from flask import Flask app = Flask(__name__) """
request_class = Request
response_class = Response
static_path = '/static'
secret_key = None
session_cookie_name = 'session'
jinja_options = dict( autoescape=True, extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_'] )
def __init__(self, package_name): self.debug = False
self.package_name = package_name
self.root_path = _get_package_path(self.package_name)
self.view_functions = {}
self.error_handlers = {}
self.before_request_funcs = []
self.after_request_funcs = []
self.template_context_processors = [_default_template_ctx_processor]
self.url_map = Map()
if self.static_path is not None: self.url_map.add(Rule(self.static_path + '/<filename>', build_only=True, endpoint='static')) if pkg_resources is not None: target = (self.package_name, 'static') else: target = os.path.join(self.root_path, 'static') self.wsgi_app = SharedDataMiddleware(self.wsgi_app, { self.static_path: target })
self.jinja_env = Environment(loader=self.create_jinja_loader(), **self.jinja_options) self.jinja_env.globals.update( url_for=url_for, get_flashed_messages=get_flashed_messages )
def create_jinja_loader(self): """ 加载模板目录,默认目录为 templates """ if pkg_resources is None: return FileSystemLoader(os.path.join(self.root_path, 'templates')) return PackageLoader(self.package_name)
def update_template_context(self, context): """ 为模板上下文注入几个常用的变量,比如 request, session, g context 为填充模板上下文的字典 """ reqctx = _request_ctx_stack.top for func in self.template_context_processors: context.update(func())
def run(self, host='localhost', port=5000, **options): """ 运行开发服务器 """ from werkzeug import run_simple if 'debug' in options: self.debug = options.pop('debug') options.setdefault('use_reloader', self.debug) options.setdefault('use_debugger', self.debug) return run_simple(host, port, self, **options)
def test_client(self): """ 为应用程序创建一个测试客户端 """ from werkzeug import Client return Client(self, self.response_class, use_cookies=True)
def open_resource(self, resource): """ 动态加载模块 """ if pkg_resources is None: return open(os.path.join(self.root_path, resource), 'rb') return pkg_resources.resource_stream(self.package_name, resource)
def open_session(self, request): """ 创建一个 session,secret_key 必须设置 基于 werkzeug.contrib.securecookie.SecureCookie """ key = self.secret_key if key is not None: return SecureCookie.load_cookie(request, self.session_cookie_name, secret_key=key)
def save_session(self, session, response): """ 保存 session """ if session is not None: session.save_cookie(response, self.session_cookie_name)
def add_url_rule(self, rule, endpoint, **options): """ 创建 URL 和视图函数的映射规则,等同于 route 装饰器 只是 add_url_rule 并没有为视图函数注册一个 endpoint 这一步也就是向 view_functions 字典添加 endpoint: view_func 键值对 以下: @app.route('/') def index(): pass 等同于: def index(): pass app.add_url_rule('index', '/') app.view_functions['index'] = index options: 参数选项详见 werkzeug.routing.Rule """ options['endpoint'] = endpoint options.setdefault('methods', ('GET',)) self.url_map.add(Rule(rule, **options))
def route(self, rule, **options): """ 为给定的 URL 注册一个视图函数 用法: @app.route('/') def index(): return 'Hello World'
变量部分可以用尖括号(``/user/<username>``)指定,默认接受任何不带斜杆的字符串 变量也可以指定一个转换器,以指定类型的参数: =========== =========================================== int 整数 float 浮点数 path 路径 =========== ===========================================
值得注意的是 Flask 如何处理结尾的斜杆,核心思路是保证每个 URL 唯一: 1、如果配置了一个带结尾斜杆的 URL,用户请求不带结尾斜杆的这个 URL,则跳转到带结尾斜杆的页面。 2、如果配置了一个不带结尾斜杆的 URL,用户请求带结尾斜杆的这个 URL,则触发404错误。 这和 web 服务器处理静态资源 static 的逻辑是一样的
参数: rule: URL 字符串 methods: 允许的请求方法,是个列表,默认只接受 GET 请求和隐式的 HEAD subdomain: 指定子域名 strict_slashes: 上述对结尾斜杆处理的开关 options: 参数选项详见 `werkzeug.routing.Rule` """ def decorator(f): self.add_url_rule(rule, f.__name__, **options) self.view_functions[f.__name__] = f return f return decorator
def errorhandler(self, code): """ 注册一个错误码处理函数 用法: @app.errorhandler(404) def page_not_found(): return 'This page does not exist', 404 等同于: def page_not_found(): return 'This page does not exist', 404 app.error_handlers[404] = page_not_found 参数: code: 错误码 """ def decorator(f): self.error_handlers[code] = f return f return decorator
def before_request(self, f): """注册一个预处理函数""" self.before_request_funcs.append(f) return f
def after_request(self, f): """注册一个后处理函数""" self.after_request_funcs.append(f) return f
def context_processor(self, f): """注册一个模板上下文处理函数""" self.template_context_processors.append(f) return f
def match_request(self): """ 根据当前请求的路由去和url_map匹配,拿到 enpoint 和 view_args endpoint: 端点,是 view_functions 中对应视图函数的key view_args: 视图函数的参数 """ rv = _request_ctx_stack.top.url_adapter.match() request.endpoint, request.view_args = rv return rv
def dispatch_request(self): """ 首先调用上面的 match_request 方法拿到 endpoint 和 view_args 根据 endpoint 可以从 view_functions 找到对应的视图函数 再传入视图函数的参数 view_args,并返回结果,这个结果只是函数的返回值 并没有包装成响应类 response_class,可以调用 make_response 方法生成响应 如果函数执行失败,则根据错误码调用对应的错误处理函数 """ try: endpoint, values = self.match_request() return self.view_functions[endpoint](**values) except HTTPException, e: handler = self.error_handlers.get(e.code) if handler is None: return e return handler(e) except Exception, e: handler = self.error_handlers.get(500) if self.debug or handler is None: raise return handler(e)
def make_response(self, rv): """ 将视图函数的返回值包装成一个真实的响应类 response_class 函数的返回值支持以下几种类型: ======================= =========================================== response_class: 响应类本身,原样返回 str: 字符串,创建相应类并返回 unicode: unicode 编码,utf-8编码后创建相应类并返回 tuple: 元组,解包元组传入参数创建响应类并返回 a WSGI function: WSGI 函数 ======================= =========================================== 参数: rv: 视图函数的返回值 """ if isinstance(rv, self.response_class): return rv
if isinstance(rv, basestring): return self.response_class(rv)
if isinstance(rv, tuple): return self.response_class(*rv)
return self.response_class.force_type(rv, request.environ)
def preprocess_request(self): """ 在分发请求之前执行所有的预处理函数,如果预处理函数有返回值不为 None 则返回结果并中断其余的预处理 """ for func in self.before_request_funcs: rv = func() if rv is not None: return rv
def process_response(self, response): """ 依次传入响应并执行所有的后处理函数,返回新的响应 """ session = _request_ctx_stack.top.session if session is not None: self.save_session(session, response) for handler in self.after_request_funcs: response = handler(response) return response
def wsgi_app(self, environ, start_response): """ WSGI 应用,可以用中间件包装: app.wsgi_app = MyMiddleware(app.wsgi_app) 参数: environ: WSGI 环境,是一个字典,包含了所有请求的信息 start_response: 回调函数 """ with self.request_context(environ): rv = self.preprocess_request() if rv is None: rv = self.dispatch_request() response = self.make_response(rv) response = self.process_response(response) return response(environ, start_response)
def request_context(self, environ): """ 通过给定的 WSGI 环境创建一个请求上下文,并把它绑定到当前上下文中 必须通过 with 语句使用,因为 request 对象只在请求上下文 也就是 with 语句块中起作用 用法如下: with app.request_context(environ): do_something_with(request) 参数: environ: WSGI 环境 """ return _RequestContext(self, environ)
def test_request_context(self, *args, **kwargs): """ 测试请求上下文,参数详见 werkzeug.create_environ """ return self.request_context(create_environ(*args, **kwargs))
def __call__(self, environ, start_response): """调用 wsgi_app 方法""" return self.wsgi_app(environ, start_response)
|