1. Python은 웹 서버를 구현합니다
웹 개발에는 먼저 웹 서버가 있어야 합니다. 예를 들어 Apache이지만 개발 단계에서는 간단하고 편리한 개발 서버를 갖는 것이 가장 좋습니다.
개발 및 디버깅이 완료된 후 코드를 성숙한 상태로 배포할 수 있습니다. 안정적이고 효율적인 웹 서버.
# -*- coding: utf-8 -*- from wsgiref import simple_server # 定义一个输出 hello world 和环境变量的简单web应用程序 def hello_app(environ, start_response): # 输出 http 头,text/plain 表示是纯文本 start_response('200 OK', [('Content-type','text/plain')]) # 准备输出的内容 content = [] content.append('Hello world') for key, value in environ.items(): content.append('%s : %s' % (key, value)) # 输出,根据 wsgi 协议,返回的需要是一个迭代器,返回一个 list 就可以 return ['\n'.join(content)] # 构造开发服务器对象,设置绑定的地址和端口,并把 hello world 应用程序传给他 server = simple_server.make_server('localhost', 8080, hello_app) # 启动开发服务器 server.serve_forever()
위 프로그램을 실행한 후 브라우저를 열고 http://www.php.cn/:8080으로 시작하는 URL에 접속하세요. Environ에 무엇이 포함되어 있는지 확인할 수 있습니다.
(일부 잘라내기)
기본지식
http 프로토콜은 브라우저와 웹 애플리케이션 간에 사용되며 요청 및 응답 형식을 지정합니다.
1. 요청 패키지(Http Request)
요청에는 주로 요청 방법, 요청 URL, 요청 헤더, 요청 본문이 포함됩니다.
http 요청 방법에는 GET, POST, PUT 및 DELETE가 포함되지만 브라우저를 통해 시작된 웹 요청에는 일반적으로 GET 및 POST 요청만 포함됩니다.
GET은 일반적으로 서버 콘텐츠를 얻는 데 사용되며 POST는 콘텐츠 수정과 유사하며 PUT이 추가되고 DELETE가 삭제됩니다.
일반적으로 POST 요청은 HTML 양식을 제출하여 시작됩니다. 성공 후 리디렉션이 필요합니다.
프로토콜 관점에서 GET을 살펴보면 HTTP 요청의 가장 큰 차이점은 GET 요청에는 요청 본문이 없지만 POST 요청에는 있다는 점입니다. 이는 파일 업로드 등과 같은 POST 요청
을 통해 많은 양의 데이터가 서버로 전송될 수 있음을 의미합니다. 물론 GET 요청은 URL 자체와 다음과 같은 매개 변수를 통해 서버에 매개 변수를 전달할 수도 있습니다.
url?arg1=value&arg2=value
요청 헤더에는 요청 패키지의 설명 정보가 포함됩니다. 인코딩, 패킷 길이 등과 같은
2. 응답 패키지(Http Response)
상태 코드, 응답 헤더 및 응답 본문을 포함하여 http 응답 패키지의 형식이 더 간단합니다.
200은 성공을 의미합니다.
404는 리소스를 찾을 수 없음을 의미합니다.
500은 서버 오류를 의미합니다.
301은 리소스의 주소가 변경되었으며 클라이언트가 점프해야 함을 의미합니다.
응답 헤더는 일부 설명 정보를 포함하여 요청 헤더와 유사합니다. 응답 본문은 일반적으로 출력 콘텐츠이며 대부분은 페이지 HTML 코드입니다.
3. 요청 수명주기
1. 웹 서버는 원본 http 요청을 받은 후 이를 어느 정도 패키징한 후 웹 애플리케이션으로 전달합니다.
2. 웹 애플리케이션에서 처리한 후 데이터는 특정 형식으로 웹 서버에 반환됩니다.
3. 그런 다음 웹 서버는 데이터를 http 응답 패킷으로 패키징하여 브라우저에 반환합니다.
4. cgi 소개
cgi(공통 게이트웨이 인터페이스)는 웹 서버와 웹 애플리케이션 간의 오래된 프로토콜입니다. http 요청에 의해 요청된 다양한 정보를 cgi 애플리케이션의 환경 변수에 넣은 다음 cgi 애플리케이션은 응답 헤더
와 해당 내용을 표준 출력을 통해 웹 서버에 출력합니다.
위에서 사용하는 개발 서버와 애플리케이션 사이에 사용되는 프로토콜은 cgi와 유사합니다. 또한 요청을 키-값 쌍으로 패키징합니다.
그러나 cgi는 다음을 통해 cgi 애플리케이션에 전달됩니다. 환경 변수 프로그램 및 wsgi는 이를 전달하기 위해 Python의 사전 객체를 직접 사용합니다.
hello_app의 첫 번째 매개변수인 Environ은 요청 정보가 포함된 사전 개체입니다. 두 번째 매개변수는 웹 애플리케이션에서 이를 호출하여 응답 콘텐츠를 출력하기 전에 상태 코드와 응답 헤더를 출력해야 합니다
.
웹 요청 및 응답 처리하기 webob 모듈은 설치가 필요합니다. 여기서는 먼저 패키지 관리 도구인 setuptools 모듈을 설치해야 합니다. Ubuntu 앱과 유사한 필수 소프트웨어 패키지입니다. 주소는 다음과 같습니다. http://www.php.cn/ 설치가 완료된 후 명령줄에 easy_install webob을 직접 입력하면 자동으로 다운로드되어 설치됩니다.
단순 사용:
>>> # 가져오기 요청 개체 >>> from webob import 요청>>environ = {}>>> # Environ 사전 포장에 요청 사용>>> req = Request(environ)Request 클래스를 사용하여 Environ을 래핑한 다음 Request 개체의 속성과 메서드를 통해 Environ에 액세스합니다. 실제 환경 사전은 웹 환경에서만 얻을 수 있으므로 셸에서 테스트를 용이하게 하기 위해 webob은 간단한 웹 요청을 시뮬레이션하는 방법을 제공합니다.
也可以通过req查找其它有用的信息
同时也可以通过webob模块中的Response对象来包装响应信息。
下面使用webob模块重写之前的hello_app
# -*- coding: utf-8 -*- from wsgiref import simple_server from webob import Request, Response # 我们顺便增加了一个功能,就是根据用户在 URL 后面传递的参数 # 显示相应的内容 def hello_app(request): content = [] # 获取 get 请求的参数 content.append('Hello %s'%request.GET['name']) # 输出所有 environ 变量 for key, value in request.environ.items(): content.append('%s : %s' % (key, value)) response = Response(body='\n'.join(content)) response.headers['content-type'] = 'text/plain' return response # 对请求和响应进行包装 def wsgi_wrapper(environ, start_response): request = Request(environ) response = hello_app(request) # response 对象本身也实现了与 wsgi 服务器之间通讯的协议, # 所以可以帮我们处理与web服务器之间的交互。 # 这一句比较奇怪,对象使用括号是什么意思。。。。 return response(environ, start_response) server = simple_server.make_server('localhost', 8080, wsgi_wrapper) server.serve_forever()
为了让 wsgi_wrapper 更加通用一点,可以把它设计成装饰器的形式:
# -*- coding: utf-8 -*- from wsgiref import simple_server from webob import Request, Response # 写成装饰器的 wsgi_wrapper def wsgi_wrapper(func): def new_func(environ, start_response): request = Request(environ) response = func(request) return response(environ, start_response) new_func.__name__ = func.__name__ new_func.__doc__ = func.__doc__ return new_func # 应用程序 @wsgi_wrapper def hello_app(request): content = [] content.append('Hello %s'%request.GET['name']) for key, value in request.environ.items(): content.append('%s : %s' % (key, value)) response = Response(body='\n'.join(content)) response.headers['content-type'] = 'text/plain' return response server = simple_server.make_server('localhost', 8080, hello_app) server.serve_forever()
三、模板
果然,还是需要用到模板,不能总是直接在Response中写上长串的html代码。
python中的模板引擎主要有mako, genshi, jinjia等。
mako 主要特点在于模板里面 可以比较方便的嵌入Python代码,而且执行效率一流;
genshi 的特点在于基于 xml, 非常简单易懂的模板语法,对于热爱xhtml的朋友来说是很好的选择,
同时也可以嵌入Python 代码,实现一些复杂的展现逻辑;
jinja 和genshi 一样拥有很简单的模板语法,只是不 依赖于 xml 的格式,同样很适合设计人员直接进行模板的制作,
同时也可以嵌入Python 代码实现一些复杂的展现逻辑。
这里使用Mako,地址ttp://pypi.python.org/pypi/Mako,下载python setup.py install进行安装
简单的模块例子:
## -*- coding: utf-8 -*- <html> <head> <title>简单mako模板</title> </head> <body> <h5>Hello ${name}!</h5> <ul> % for key, value in data.items(): <li> ${key} - ${value} <li> % endfor </ul> </body> </html>
保存为simple.html文件,然后需要给模板对象传递data和name两个参数,然后进行渲染,就可以输入html内容
# -*- coding: utf-8 -*- # 导入模板对象 from mako.template import Template # 使用模板文件名构造模板对象 tmpl = Template(filename='./simple.html', output_encoding='utf-8') # 构造一个简单的字典填充模板,并print出来 print tmpl.render(name='python', data = {'a':1, 'b':2})
保存为test_template.py文件,运行就可以输入内容:
$ python test_template.py
<html> <head> <title>简单mako模板</title> </head> <body> <h5>Hello python!</h5> <ul> <li> a - 1 <li> <li> b - 2 <li> </ul> </body> </html>
下面对hello_app程序进行重构:
1. 把 wsgi_wrapper 单独放到通用模块 utils.py:
# -*- coding: utf-8 -*- from webob import Request def wsgi_wrapper(func): def new_func(environ, start_response): request = Request(environ) response = func(request) return response(environ, start_response) new_func.__name__ = func.__name__ new_func.__doc__ = func.__doc__ return new_func
2. 把 hello_app 给彻底独立出来,形成单独的模块 controller.py :
# -*- coding: utf-8 -*- from utils import wsgi_wrapper from webob import Response from mako import Template # 整合了模板功能的 hello_app @wsgi_wrapper def hello_app(request): tmpl = Template(filename='./simple.html', output_encoding='utf-8') content = tmpl.render(name=request.GET['name'], data=request.environ) return Response(body=content)
3. 这样 main.py 就变成这样了:
# -*- coding: utf-8 -*- from wsgiref import simple_server from controller import hello_app server = simple_server.make_server('localhost', 8080, hello_app) server.serve_forever()
四、ORM(Object Relation Mapping, 对象关系映射)
终于也要这一步了,作为web应用,还是需要与数据库进行合作。
这里使用sqlalchemy,是一个 ORM (对象-关系映射)库,提供Python对象与关系数据库之间的映射。和Django的models
用法很像,也是可以通过python代码来创建数据库表,并进行操作。
sqlalchemy 还可以自动映射 Python 对象的继承,可以实现eager loading、lazy loading, 可以直接将 Model 映射到自定
义的 SQL 语句,支持n多的数据库等等等等。 可以说 sqlalchemy 既有不输于 Hibernate 的强大功能,同时不失 Python
的简洁优雅。
使用方法:
# -*- coding: utf-8 -*- from sqlalchemy import * from sqlalchemy.orm import sessionmaker, scoped_session from sqlalchemy.ext.declarative import declarative_base # 创建数据库引擎,这里我们直接使用 Python2.5 自带的数据库引擎:sqlite, # 直接在当前目录下建立名为 data.db 的数据库 engine = create_engine('sqlite:///data.db') # sqlalchemy 中所有数据库操作都要由某个session来进行管理 # 关于 session 的详细信息请参考:http://www.sqlalchemy.org/docs/05/session.html Session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine)) Base = declarative_base() class Dictionary(Base): # Python 对象对应关系数据库的表名 __tablename__ = 't_dictionary' # 定义自动,参数含义分别为:数据库字段名,字段类型,其他选项 key = Column('key', String(255), primary_key=True) value = Column('value', String(255)) # 创建数据库 Base.metadata.create_all(engine) session = Session() for item in ['python','ruby','java']: # 构造一个对象 dictionary = Dictionary(key=item, value=item.upper()) # 告诉 sqlalchemy ,将该对象加到数据库 session.add(dictionary) # 提交session,在这里才真正执行数据库的操作,添加三条记录到数据库 session.commit() # 查询数据库中Dictionary对象对应的数据 for dictionary in session.query(Dictionary): print dictionary.key, dictionary.value
上面的代码你执行两遍就会报错,为什么。。。因为插入数据库的主键重复了。。。。
这样就可以整合到之前的controller.py文件中
# -*- coding: utf-8 -*- from utils import wsgi_wrapper from webob import Response from mako.template import Template # 导入公用的 model 模块 from model import Session, Dictionary @wsgi_wrapper def hello_app(request): session = Session() # 查询到所有 Dictionary 对象 dictionaries = session.query(Dictionary) # 然后根据 Dictionary 对象的 key、value 属性把列表转换成一个字典 data = dict([(dictionary.key, dictionary.value) for dictionary in dictionaries]) tmpl = Template(filename='./simple.html', output_encoding='utf-8') content = tmpl.render(name=request.GET['name'], data=data) return Response(body=content)
五、URL分发控制
给不同的资源设计不同的 URL, 客户端请求这个 URL,web应用程序再根据用户请求的 URL 定位到具体功能并执行之。
提供一个干净的 URL 有很多好处:
1. 可读性,通过 URL 就可以大概了解其提供什么功能
2. 用户容易记住也方便直接输入
3.设计良好的 URL 一般都更短小精悍,对搜索引擎也 更友好
使用selector模块来处理url映射
下载地址http://pypi.python.org/pypi/selector, 下载那个source文件进行python setup.py install
首先把urls的配置单独放到urls.py中
# -*- coding: utf-8 -*- from controller import hello_app mappings = [('/hello/{name}', {'GET':hello_app})]
修改main.py
# -*- coding: utf-8 -*- from wsgiref import simple_server from urls import mappings from selector import Selector # 构建 url 分发器 app = Selector(mappings) server = simple_server.make_server('localhost', 8080, app) server.serve_forever()
然后,在 hello_app 中就可以通过 environ['wsgiorg.routing_args'] 获取到 name 参数了,
不过在 wsgi_wrapper 其实还可以进一步简化 hello_app 的工作: 直接把解析得到的参数
当作函数参数传过去!修改 utils.py:
from webob import Request def wsgi_wrapper(func): def new_func(environ, start_response): request = Request(environ) position_args, keyword_args = environ.get('wsgiorg.routing_args', ((), {})) response = func(request, *position_args, **keyword_args) return response(environ, start_response) new_func.__name__ = func.__name__ new_func.__doc__ = func.__doc__ return new_func
那 hello_app 就可以改成这样了:
... @wsgi_wrapper def hello_app(request, name=''): ... content = tmpl.render(name=name, data=data) return Response(body=content) 执行main.py,访问http://localhost:8080/hello/Python
总结
以上部分的实现,就是类似Django框架中的几个主要的功能模块,希望对大家的学习有所帮助。
更多Python은 Django 프레임워크를 시뮬레이션합니다.相关文章请关注PHP中文网!