1. 초기화
이 장에서는 Flask 애플리케이션의 다양한 부분을 배우게 됩니다. 동시에 첫 번째 Flask 웹 애플리케이션을 작성하고 실행하게 됩니다.
모든 Flask 애플리케이션은 애플리케이션 인스턴스를 생성해야 합니다. 클라이언트로부터 받은 모든 요청은 웹 서버 게이트웨이 인터페이스 프로토콜을 사용하여 처리하기 위해 이 개체로 전달됩니다. 이 애플리케이션 인스턴스는 일반적으로 다음 메소드를 사용하여 생성되는 Flask 클래스의 객체입니다.
from flask import Flask app = Flask(__name__)
Flask에 필요한 유일한 것 클래스 생성자 매개변수는 애플리케이션의 기본 모듈 또는 패키지입니다. 대부분의 애플리케이션에서는 Python의 __name__ 변수가 전달해야 하는 올바른 값입니다.
참고: Flask 개발자의 경우 Flask 애플리케이션 생성자에 전달된 name 매개변수가 혼란스러울 수 있습니다. Flask는 이 매개변수를 사용하여 나중에 이 경로를 기준으로 리소스 파일을 찾을 수 있도록 애플리케이션의 루트 디렉터리를 결정합니다.
나중에 더 복잡한 애플리케이션 인스턴스 초기화를 볼 수 있지만 간단한 애플리케이션의 경우 이것만으로도 충분합니다.
2. 라우팅 및 뷰 기능
웹 브라우저 등의 클라이언트는 웹 서비스에 요청을 보낸 후 이를 Flask 애플리케이션 인스턴스에 보냅니다. 애플리케이션 인스턴스는 각 URL 요청에 대해 어떤 코드를 실행해야 하는지 알아야 하므로 Python 함수에 대한 URL 맵을 설정합니다. URL과 기능 간의 연결을 설정하는 이러한 작업을 라우팅이라고 합니다.
Flask 애플리케이션에서 경로를 정의하는 가장 편리한 방법은 애플리케이션 인스턴스에서 app.route 데코레이터를 명시적으로 정의하여 데코레이팅된 함수를 경로로 등록하는 것입니다. 다음 예에서는 데코레이터를 사용하여 경로를 선언하는 방법을 보여줍니다.
@app.route('/') def index(): return '<h1>Hello World!</h1>'
참고: 데코레이터는 Python 언어의 표준 기능입니다. 다양한 방식으로 함수의 동작을 변경할 수 있습니다. 일반적인 패턴은 데코레이터를 사용하여 함수를 이벤트 핸들러로 등록하는 것입니다.
이전 예에서는 index() 함수를 애플리케이션의 루트 URL에 대한 이벤트 핸들러로 등록했습니다. 이 애플리케이션이 서버에 배포되고 www.example.com 도메인 이름에 바인딩된 경우 브라우저 주소 표시줄에 http://www.php.cn/을 입력하면 index()가 실행되어 서비스가 실행됩니다. 클라이언트가 수신한 이 함수의 반환 값을 응답이라고 합니다. 클라이언트가 웹 브라우저인 경우 응답은 사용자에게 표시되는 문서입니다.
index()와 유사한 함수를 뷰 함수라고 합니다. 뷰에서 반환되는 응답은 간단한 HTML 콘텐츠 문자열일 수도 있지만 앞으로 보게 될 것처럼 더 복잡한 형식을 취할 수도 있습니다.
참고: 응답 문자열은 Python 코드에 포함되어 있어 코드를 제어하기 어렵습니다. 여기서는 응답의 개념만 소개합니다. 3장에서 응답을 생성하는 올바른 방법을 배우게 됩니다.
우리가 매일 사용하는 웹사이트 URL 중 일부가 어떻게 구성되어 있는지 살펴보면 변수가 많다는 것을 알 수 있습니다. 예를 들어, Facebook 프로필 페이지의 URL은 http://www.php.cn/;username>이므로 사용자 이름이 그 일부입니다. Flask는 경로 데코레이터의 특수 구문을 사용하여 이러한 유형의 URL을 지원합니다. 다음 예에서는 동적 이름 구성 요소가 있는 경로를 정의합니다.
@app.route('/user/<name>') def user(name): return '<h1>Hello, %s!</h1>' % name
꺾쇠 괄호로 묶인 부분은 동적 부분입니다. 정적 부분과 일치하는 URL이 이 경로에 매핑됩니다. 뷰 함수가 호출되면 Flask는 동적 구성 요소를 매개 변수로 보냅니다. 이전 예의 보기 기능에서 이 매개변수는 응답으로 개인화된 인사말을 생성하는 데 사용됩니다.
라우팅의 동적 구성 요소는 기본적으로 문자열로 설정되지만 다른 유형으로 정의할 수도 있습니다. 예를 들어 /user/
3. 서비스 시작
애플리케이션 인스턴스에는 Flask 통합 웹 서비스를 시작하기 위한 run 메소드가 있습니다:
if __name__ == '__main__': app.run(debug=True)
__name__ == 여기서는 스크립트가 즉시 실행될 때 웹 서비스가 시작되었는지 확인하기 위해 '__main__'을 사용합니다. 다른 스크립트에서 스크립트를 가져오면 상위 스크립트로 처리되어 다른 서비스를 시작하므로 app.run() 호출을 건너뜁니다.
서비스가 시작되면 요청을 기다리는 루프에 들어가서 서비스를 제공합니다. 이 루프는 예를 들어 Ctrl-C를 눌러 애플리케이션이 중지될 때까지 계속됩니다.
app.run()에 대한 웹 서비스의 작동 모드를 구성하는 데 사용할 수 있는 여러 옵션 매개변수가 있습니다. 개발 중에 디버거와 리로더를 활성화하는 디버그 모드를 쉽게 켤 수 있습니다. 디버그를 True로 전달하면 됩니다.
참고: Flask에서 제공하는 웹 서비스는 프로덕션 환경에서 사용되지 않습니다. 17장에서는 프로덕션 환경의 웹 서비스에 대해 배웁니다.
4. 완전한 애플리케이션
이전 섹션에서는 Flask 웹 애플리케이션의 다양한 부분에 대해 배웠으니 이제 하나 작성해 보겠습니다. 전체 hello.py 애플리케이션 스크립트는 앞서 설명한 세 부분을 하나의 파일로 결합한 것에 지나지 않습니다. 응용예 2-1을 나타냅니다.
hello.py 예: 완전한 Flask 애플리케이션
from flask import Flask app = Flask(__name__) @app.route('/') def index(): return 'Hello World!
' if __name__ == '__main__': app.run(debug=True)
建议:如果你有克隆在GitHub上的应用程序,你现在可以运行git checkout 2a来切换到这个版本的应用程序。
运行应用程序之前,请确保你在之前创建的虚拟环境已经是激活状态且已安装Flask。现在打开你的web浏览器并在地址栏输入 http://www.php.cn/:5000/ 。下图显示连接到应用程序后的web浏览器。
然后输入以下命令启动应用程序:
(venv) $ python hello.py * Running on http://127.0.0.1:5000/ * Restarting with reloader
如果你输入任何其他URL,应用程序将不知道如何操作它并且将返回错误代码404给浏览器——当你访问一个不存在的网页也会得到该错误。
下面所示应用程序的增强版添加了第二个动态路由。当你访问这个URI,你应该可以看到一个个性的问候。
示例hello.py:带有动态路由的Flask应用程序
from flask import Flask app = Flask(__name__) @app.route('/') def index(): return 'Hello World!
' @app.route('/user/') def user(name): return ' Hello, %s!
' % name if __name__ == '__main__': app.run(debug=True)
建议:如果你有克隆在GitHub上的应用程序,你现在可以运行git checkout 2b来切换到这个版本的应用程序。
测试动态路由,确保服务正在运行随后访问 http://localhost:5000/user/Dave 。生成的应用程序会使用动态参数名响应一个定制的问候。尝试不同的名称,看看视图函数总是生成响应基于给定的名称。
5.1、应用程序Context和请求Context
当Flask从客户端收到一个请求,它需要提供几个可用对象给视图函数处理。request对象是个不错的例子,它封装了客户端发送的HTTP请求。
Flask视图函数访问request对象的最好方式,就是作为一个参数发送它,但这需要每个单一视图函数在应用程序中有一个额外的参数。考虑一下,如果request对象不是唯一一个视图函数需要访问完成请求的对象,事情将会变得更加复杂。
为了避免弄乱视图函数那些可能需要或不需要的参数,Flask使用context来临时确定可访问的全局对象。也多亏了context,视图函数可以写成下面这样:
from flask import request @app.route('/') def index(): user_agent = request.headers.get('User-Agent') return ' <p>Your browser is %s</p> ' % user_agent
注意,在这个视图函数中,request是如何被作为一个全局变量来使用的。现实中,request是不能作为全局变量的,如果是多线程服务器,同一时间线程作用于不同客户端的不同请求,所以每一个线程需要看到request中的不同对象。contexts使得Flask确定可访问的全局变量而不干扰其他线程。
注:线程是可以独立管理的最小指令序列。一个进程中有多个活动的线程是非常常见的,有时分享内存或文件句柄资源。多线程web服务器会启动一个线程池并从池中选择一个线程来处理每个传入的请求。
Flask有两类context:应用级context 和 请求级context。表2-1展示了这些context提供的变量。
Flask激活(或压栈)应用级context和请求级context在调度请求之前,然后删除他们当请求被处理后。当应用程序context被压入栈,线程中current_app和g变量变得可用;同样的,当请求级context被压入栈,request和session变量也同样变得可用。如果这些变量中的任何一个不是由激活的应用级或请求级context访问,会产生错误。在后面的章节会详细讨论四个context变量,所以不要担心你不理解它们的用处。
下面的Python shell会话演示了应用级context是如何工作的:
>>> from hello import app >>> from flask import current_app >>> current_app.name Traceback (most recent call last): ... RuntimeError: working outside of the application context >>> app_ctx = app.app_context() >>> app_ctx.push() >>> current_app.name 'hello' >>> app_ctx.pop()
在这个示例中,当应用级context没有激活,但是却作为有效的context被压入栈中,current_app.name报错。注意在应用程序实例中一个应用级context是如何通过调用app.app_context()来获得的。
5.2、请求调度
当一个应用程序收到客户端的请求,它需要找到响应的视图函数为之服务。对于这个任务,Flask会在应用程序的URL映射中查找请求的URL,该映射包含URLs和操作它们的视图函数。Flask通过app.route装饰器或非装饰器版本app.add_url_rule()来建立这个映射。
看一下Flask应用程序中URL映射是怎样的,你可以在Python shell中检查hello.py创建的映射。测试中,请确保你的虚拟环境是激活状态:
(venv) % python >>> from hello import app >>> app.url_map Map([<Rule '/' (HEAD, OPTIONS, GET) -> index>, <Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>, <Rule '/user/<name>' (HEAD, OPTIONS, GET) -> user>])
URL映射中所示的HEAD、OPTIONS、GET元素为request方法,由路由处理。Flask连接方法到每个路由,这样不同的请求方法发送到相同的URL可以被不同的视图函数处理。HEAD和OPTIONS方法由Flask自动管理,所以实际上可以说,在这个应用程序中URL映射的三个路由都连接到GET方法了。在第四章你将学习为路由指定不同的请求方法。
5.3、请求Hooks
有些时候在每个请求处理之前或之后执行代码是非常有用的。例如,在开始每一个请求前可能有必要创建数据库连接,或对用户请求进行验证。为了避免复制处理这些操作的代码到每一个视图函数中,Flask给你选择注册相同函数来调用,在请求被分配给视图函数之前或之后。
请求hooks由装饰器实现。下面是四个Flask支持的hooks:
(1)before_first_request:在第一个请求被处理前注册一个函数运行。
(2)before_request:在每一个请求前注册一个函数运行。
(3)after_request:如果没有未处理的异常发生,在每一个请求后注册一个函数运行。
(4)teardown_request:即使未处理的异常发生,在每一个请求后注册一个函数运行。
在请求hook函数和视图函数之间共享数据的惯用方法就是使用g全局context。例如,before_request处理程序可以从数据库加载已登录的用户并保存在g.user中。之后,当视图函数被调用,可以从那访问用户。
请求hooks的示例会在未来的章节中展示给大家,所以不用担心,
5.4、响应
当Flask调用一个视图函数,并期望它的返回值去响应该请求。大多数的响应是将简单字符串构成的HTML页面发回给客户端。
但是HTTP协议需要比字符串更多的信息作为请求的响应。一个HTTP响应中非常重要的部分是状态码,Flask默认设置200来指示请求已经成功处理。
当视图函数需要用不同的状态码响应,可以在响应文本后添加数字码作为第二个返回值。例如,下面的视图函数返回400错误状态码的请求:
@app.route('/') def index(): return '<h1>Bad Request</h1>', 400
视图函数返回的响应还可以携带第三个参数,添加一个头部字典给HTTP响应。通常很少用到,但是你可以在第十四章看到示例。
除了返回一个、两个或三个值的元组,Flask视图函数可以选择返回response对象。make_response()函数可携带一个、两个或三个参数,和视图函数返回的值一样,并返回一个response对象。有时候在视图函数中执行这个转换是非常有用的,然后使用response对象中的方法进一步配置响应。下面的示例创建response对象并设置cookie:
from flask import make_response @app.route('/') def index(): response = make_response(' <h1>This document carries a cookie!</h1> ') response.set_cookie('answer', '42') return response
有一类特殊的响应称作重定向。这类响应不包含页面文档;只是给浏览器一个新的URL去加载新的页面。重定向通常和web表单一起使用,你将在第四章学习。
重定向通常由302响应状态码注明并且重定向的URL由头部的Location给出。重定向响应可以使用三个值的返回生成,也可通过响应对象生成,但是鉴于它频繁的使用,Flask提供redirect()函数来创建这样的响应:
from flask import redirect @app.route('/') def index(): return redirect('http://www.example.com')
另一个具有中断功能的特殊响应用来错误处理。下面的示例,当URL给出的id动态参数不是一个合法的用户时返回状态码404:
from flask import abort @app.route('/user/<id>') def get_user(id): user = load_user(id) if not user: abort(404) return ' <h1>Hello, %s</h1> ' % user.name
注意终止不是指将控制权返回给调用它的函数,而是指通过抛出异常将控制权返回给web服务。
6、Flask扩展
Flask是可扩展的。它故意腾出地给重要的功能,例如数据库和用户授权,给你自由去选择最适合你的应用程序的包,或写一个自己想要的。
社区开发了非常多的扩展用于各种用途,如果这还不够,可以使用任何Python标准包和库。为了让你了解一个扩展是如何并入一个应用程序的,下面的章节给hello.py添加一个扩展,增加应用程序的命令行参数。
6.1、Flask-Script命令行选项
Flask开发,其web服务器支持一系列的启动配置选项,但是配置它们的唯一方式只有在脚本中传递参数给app.run()并调用。这不是非常的方便,理想方法是通过命令行参数传递配置选项。
Flask-Script是给你的Flask应用程序添加命令行解释的扩展。它打包了一组通用的选项,还支持自定义命令。
使用pip安装扩展:
(venv) $ pip install flask-script
下面展示了在 hello.py 应用程序中添加命令行解释的变化。
示例. hello.py:使用Flask-Script
from flask.ext.script import Manager manager = Manager(app) # ... if __name__ == '__main__': manager.run()
专为Flask开发的扩展暴露在flask.ext命名空间下。Flask-Script从flask.ext.script中导出一个名为Manager的类。
初始化这个扩展的方法和其他许多扩展一样:主类实例的初始化是通过将应用程序实例作为参数传递给构造函数实现的。创建的对象适当的用于每一个扩展。在这个示例中,服务器启动通过manager.run()来路由,且命令行在这被解析。
建议:如果你有克隆在GitHub上的应用程序,你现在可以运行git checkout 2c来切换到这个版本的应用程序。
因为这些变化,应用程序获得一组基本的命令行选项。运行hello.py显示可用信息:
$ python hello.py
usage: hello.py [-h] {shell, runserver} ... positional arguments: {shell, runserver} shell 在Flask应用程序上下文的内部运行一个Python Shell。 runserver 运行Flask开发服务器,例如:app.run() optional arguments: -h, --help 显示这个帮助信息并退出
shell命令用于在应用程序上下文中启动一个Python shell会话。你可以使用这个会话去运行维护任务,或测试,或调试错误。
runserver命令,就像它的名称一样,启动web服务。运行python hello.py runserver在调试模式下启动web服务,还有更多的选项:
(venv) $ python hello.py runserver --help usage: hello.py runserver [-h] [-t HOST] [-p PORT] [--threaded] [--processes PROCESSES] [--passthrough-errors] [-d] [-r]
运行Flask开发服务器,例如:app.run()
optional arguments: -h, --help 显示这个帮助信息并退出 -t HOST, --host HOST -p PORT, --port PORT --threaded --processes PROCESSES --passthrough-errors -d, --no-debug -r, --no-reload
--host参数是一个非常有用的选项,因为它能告诉web服务器监听哪个网络接口的客户端连接。默认,Flask开发的web服务器监听localhost的连接,所以只有来自内部计算机运行的服务器可以接收。下面的命令使得web服务器监听公网接口,其他网络上的计算机可以连接:
(venv) $ python hello.py runserver --host 0.0.0.0 * Running on http://0.0.0.0:5000/ * Restarting with reload
现在web服务器应该可以从网络中的任何一台计算机访问 http://a.b.c.d:5000 ,“a.b.c.d”是运行服务的计算机的外部IP地址。
更多用Python의 Flask 프레임워크를 사용하여 첫 번째 웹 애플리케이션 구축相关文章请关注PHP中文网!