Python学习--20 Web开发
HTTP格式
HTTP协议是基于TCP和IP协议的。HTTP协议是一种文本协议。
每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的。
HTTP请求格式:
GET:
GET /path HTTP/1.1
Header1: Value1
Header2: Value2
Header3: Value3
POST:
POST /path HTTP/1.1
Header1: Value1
Header2: Value2
Header3: Value3
body data goes here...
Header部分每行用\r\n
换行,每行里键名和键值之间以:
分割,注意冒号后有个空格。
当遇到\r\n\r\n
时,Header部分结束,后面的数据全部是Body。
HTTP响应格式:
200 OK
Header1: Value1
Header2: Value2
Header3: Value3
body data goes here...
HTTP响应如果包含body,也是通过\r\n\r\n
来分隔的。
请再次注意,Body的数据类型由
Content-Type
头来确定,如果是网页,Body就是文本,如果是图片,Body就是图片的二进制数据。
Body数据是可以被压缩的,如果看到Content-Encoding
,说明网站使用了压缩。最常见的压缩方式是gzip。
WSGI接口
了解了HTTP协议的格式后,我们可以理解一个Web应用的本质:
1、浏览器发送HTTP请求给服务器;
2、服务器接收请求后,生成HTML;
3、服务器把生成的HTML作为HTTP响应的body返回给浏览器;
4、浏览器接收到HTTP响应后,解析HTTP里body并显示。
接受HTTP请求、解析HTTP请求、发送HTTP响应实现起来比较复杂,有专门的服务器软件来实现,例如Nginx,Apache。我们要做的就是专注于生成HTML文档。
Python里也提供了一个比较底层的WSGI
(Web Server Gateway Interface)接口来实现TCP连接、HTTP原始请求和响应格式。实现了该接口定义的内容,就可以实现类似Nginx、Apache等服务器的功能。
WSGI
接口定义要求Web开发者实现一个函数,就可以响应HTTP请求,示例:
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [b'<h1>Hello, web!</h1>']
这是一个简单的文本版本的Hello, web!
。
上面的application()
函数就是符合WSGI
标准的一个HTTP处理函数,它接收两个参数:
environ:一个包含所有HTTP请求信息的dict对象;
start_response:一个发送HTTP响应的函数。
有了WSGI,我们关心的就是如何从environ
这个dict对象拿到HTTP请求信息,然后构造HTML,通过start_response()
发送Header,最后返回Body。
整个application()
函数本身没有涉及到任何解析HTTP的部分,即底层代码不需要自己编写,只负责在更高层次上考虑如何响应请求就可以了。
但是,application()
函数由谁来调用呢?因为这里的参数environ
、start_response
我们没法提供,返回的bytes也没法发给浏览器。
application()
函数必须由WSGI
服务器来调用。
有很多符合WSGI规范的服务器,Python提供了一个最简单的WSGI
服务器,可以把我们的Web应用程序跑起来。这个模块叫wsgiref
,它是用纯Python编写的WSGI
服务器的参考实现。所谓“参考实现”是指该实现完全符合WSGI
标准,但是不考虑任何运行效率,仅供开发和测试使用。
运行WSGI服务
有了wsgiref
,我们可以非常快的实现一个简单的web服务器:
# coding: utf-8
from wsgiref.simple_server import make_server
def application(environ, start_response):
print(environ)
start_response('200 OK', [('Content-Type', 'text/html')])
return [b'<h1>Hello web!</h1>']
print('HTTP server is running on http://127.0.0.1:9999')
# 创建一个服务器,IP地址可以为空,端口是9999,处理函数是application:
httpd = make_server('', 9999, application)
httpd.serve_forever()
运行后访问http://127.0.0.1:9999/
,会看到:
Hello web!
扩展知识:
make_server()
里第一个参数如果为空,实际等效于0.0.0.0
,表示监听本地所有ip地址(包括127.0.0.1
)。
通过Chrome浏览器的控制台,我们可以查看到浏览器请求和服务器响应信息:
# 请求信息:
GET / HTTP/1.1
Host: 127.0.0.1:9999
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Cookie: _ga=GA1.1.948200530.1463673425
# 响应信息:
HTTP/1.0 200 OK
Date: Sun, 12 Feb 2017 05:20:31 GMT
Server: WSGIServer/0.2 CPython/3.4.3
Content-Type: text/html
Content-Length: 19
<h1>Hello web!</h1>
我们再看终端的输出信息:
$ python user_wsgiref_server.py
HTTP server is running on http://127.0.0.1:9999
127.0.0.1 - - [12/Feb/2017 13:18:38] "GET / HTTP/1.1" 200 19
127.0.0.1 - - [12/Feb/2017 13:18:39] "GET /favicon.ico HTTP/1.1" 200 19
如果我们打印environ
参数信息,会看到如下值:
{
"SERVER_SOFTWARE": "WSGIServer/0.1 Python/2.7.5",
"SCRIPT_NAME": "",
"REQUEST_METHOD": "GET",
"SERVER_PROTOCOL": "HTTP/1.1",
"HOME": "/root",
"LANG": "en_US.UTF-8",
"SHELL": "/bin/bash",
"SERVER_PORT": "9999",
"HTTP_HOST": "dev.banyar.cn:9999",
"HTTP_UPGRADE_INSECURE_REQUESTS": "1",
"XDG_SESSION_ID": "64266",
"HTTP_ACCEPT": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"wsgi.version": "0",
"wsgi.errors": "",
"HOSTNAME": "localhost",
"HTTP_ACCEPT_LANGUAGE": "zh-CN,zh;q=0.8,en;q=0.6",
"PATH_INFO": "/",
"USER": "root",
"QUERY_STRING": "",
"PATH": "/usr/local/php/bin:/usr/local/php/sbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin",
"HTTP_USER_AGENT": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36",
"HTTP_CONNECTION": "keep-alive",
"SERVER_NAME": "localhost",
"REMOTE_ADDR": "192.168.0.101",
"wsgi.url_scheme": "http",
"CONTENT_LENGTH": "",
"GATEWAY_INTERFACE": "CGI/1.1",
"CONTENT_TYPE": "text/plain",
"REMOTE_HOST": "",
"HTTP_ACCEPT_ENCODING": "gzip, deflate, sdch"
}
为显示方便,已精简部分信息。有了环境变量信息,我们可以对程序做些修改,可以动态显示内容:
def application(environ, start_response):
print(environ['PATH_INFO'])
start_response('200 OK', [('Content-Type', 'text/html')])
body = '<h1>Hello %s!</h1>' % (environ['PATH_INFO'][1:] or 'web' )
return [body.encode('utf-8')]
以上使用了environ
里的PATH_INFO
的值。我们在浏览器输入http://127.0.0.1:9999/python
,浏览器会显示:
Hello python!
终端的输出信息:
$ python user_wsgiref_server.py
HTTP server is running on http://127.0.0.1:9999
/python
127.0.0.1 - - [12/Feb/2017 13:54:57] "GET /python HTTP/1.1" 200 22
/favicon.ico
127.0.0.1 - - [12/Feb/2017 13:54:58] "GET /favicon.ico HTTP/1.1" 200 27
web框架
实际项目开发中,我们不可能使用swgiref
来实现服务器,因为WSGI提供的接口虽然比HTTP接口高级了不少,但和Web App的处理逻辑比,还是比较低级。我们需要使用成熟的web框架。
由于用Python开发一个Web框架十分容易,所以Python有上百个开源的Web框架。部分流行框架:
Flask:轻量级Web应用框架;
Django:全能型Web框架;
web.py:一个小巧的Web框架;
Bottle:和Flask类似的Web框架;
Tornado:Facebook的开源异步Web框架
Flask
Flask是一个使用 Python 编写的轻量级 Web 应用框架。其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2 。
安装非常简单:
pip install flask
控制台输出:
Collecting flask
Downloading Flask-0.12-py2.py3-none-any.whl (82kB)
100% |████████████████████████████████| 92kB 163kB/s
Collecting itsdangerous>=0.21 (from flask)
Downloading itsdangerous-0.24.tar.gz (46kB)
100% |████████████████████████████████| 51kB 365kB/s
Collecting click>=2.0 (from flask)
Downloading click-6.7-py2.py3-none-any.whl (71kB)
100% |████████████████████████████████| 71kB 349kB/s
Collecting Jinja2>=2.4 (from flask)
Downloading Jinja2-2.9.5-py2.py3-none-any.whl (340kB)
100% |████████████████████████████████| 348kB 342kB/s
Collecting Werkzeug>=0.7 (from flask)
Downloading Werkzeug-0.11.15-py2.py3-none-any.whl (307kB)
100% |████████████████████████████████| 317kB 194kB/s
Collecting MarkupSafe>=0.23 (from Jinja2>=2.4->flask)
Downloading MarkupSafe-0.23.tar.gz
Building wheels for collected packages: itsdangerous, MarkupSafe
Running setup.py bdist_wheel for itsdangerous ... done
Successfully built itsdangerous MarkupSafe
Installing collected packages: itsdangerous, click, MarkupSafe, Jinja2, Werkzeug, flask
Successfully installed Jinja2-2.9.5 MarkupSafe-0.23 Werkzeug-0.11.15 click-6.7 flask-0.12 itsdangerous-0.24
安装完flask会同时安装依赖模块:itsdangerous
, click
, MarkupSafe
, Jinja2
, Werkzeug
。
现在我们来写个简单的登录功能,主要是三个页面:
- 首页,显示
home
字样; - 登录页,地址
/login
,有登录表单; - 登录后的欢迎页面,如果登录成功,提示欢迎语,否则提示用户名不正确。
那么一共有3个URL:
- GET /:首页,返回Home;
- GET /login:登录页,显示登录表单;
- POST /login:处理登录表单,显示登录结果。
user_flask_app.py
# coding: utf-8
from flask import Flask
from flask import request
app = Flask(__name__)
# 首页
@app.route('/', methods=['GET', 'POST'])
def home():
return '<h1>Home</h1><p><a href="/login">去登录</a></p>'
# 登录页
@app.route('/login', methods=['get'])
def login():
return '''<form action="/login" method="post">
<p>用户名:<input name="username"></p>
<p>密码:<input name="password" type="password"></p>
<p><button type="submit">登录</button></p>
</form>'''
# 登录页处理
@app.route('/login', methods=['post'])
def do_login():
# 从request对象读取表单内容:
param = request.form
if(param['username'] == 'yjc' and param['password'] == 'yjc'):
return '欢迎您 %s !' % param['username']
else:
return '用户名或密码不正确。'
pass
if __name__ == '__main__':
# run()方法参数可以都为空,使用默认值
app.run('', 5000)
我们可以打开:http://localhost:5000/ 看效果。实际的Web App应该拿到用户名和口令后,去数据库查询再比对,来判断用户是否能登录成功。
通过代码我们可以发现,Flask通过Python的装饰器在内部自动地把URL和函数给关联起来。
注意代码里同一个URL/login
分别有GET
和POST
两种请求,可以映射到两个处理函数中。
使用模板
Web框架让我们从编写底层WSGI接口拯救出来了,极大的提高了我们编写程序的效率。
但代码里嵌套太多的html让整个代码易读性变差,使程序变得复杂。我们需要将后端代码逻辑与前端html分离出来。这就是传说中的MVC
:Model-View-Controller,中文名“模型-视图-控制器”。
Controlle
r负责业务逻辑,比如检查用户名是否存在,取出用户信息等等;
View
负责显示逻辑,通过简单地替换一些变量,View最终输出的就是用户看到的HTML。
'Model'负责数据的获取,如从数据库查询用户信息等。Model简单可以理解为数据。
那么就是:Model
获取数据,Controlle
处理业务逻辑,View
显示数据。
现在,我们把上次直接输出字符串作为HTML的例子用MVC模式改写一下:
# coding: utf-8
from flask import Flask,request,render_template
app = Flask(__name__)
# 首页
@app.route('/', methods=['GET', 'POST'])
def home():
return render_template('home.html')
# 登录页
@app.route('/login', methods=['get'])
def login():
return render_template('login.html', param = [])
# 登录页处理
@app.route('/login', methods=['post'])
def do_login():
param = request.form
if(param['username'] == 'yjc' and param['password'] == 'yjc'):
return render_template('welcome.html', username = param['username'])
else:
return render_template('login.html', msg = '用户名或密码不正确。', param = param)
pass
if __name__ == '__main__':
app.run('', 5000)
Flask通过render_template()
函数来实现模板的渲染。和Web框架类似,Python的模板也有很多种。Flask默认支持的模板是jinja2
。
模板页面:
home.html
<h1>Home</h1><p><a href="/login">去登录</a></p>
login.html
{% if msg %}
<p style="color:red;">{{ msg }}</p>
{% endif %}
<form action="/login" method="post">
<p>用户名:<input name="username" value="{{ param.username }}"></p>
<p>密码:<input name="password" type="password"></p>
<p><button type="submit">登录</button></p>
</form>
welcome.html
<p>欢迎您, {{ username }} !</p>
项目目录:
user_flask_app
|-- templates
|-- home.html
|-- login.html
|-- welcome.html
|-- user_flask_app.py
render_template()
函数第一个参数是模板名,默认是templates
目录下。后面的参数是传给模板的变量。变量的值可以是数字、字符串、列表等等。
在Jinja2模板中,我们用{{ name }}
表示一个需要替换的变量。很多时候,还需要循环、条件判断等指令语句,在Jinja2中,用{% ... %}
表示指令。
比如循环输出页码:
{% for i in page_list %}
<a href="/page/{{ i }}">{{ i }}</a>
{% endfor %}
除了Jinja2
,常见的模板还有:
Mako:用<% ... %>和${xxx}的一个模板;
Cheetah:也是用<% ... %>和${xxx}的一个模板;
Django:Django是一站式框架,内置一个用{% ... %}和{{ xxx }}的模板。
Python学习--20 Web开发的更多相关文章
- python学习6 web开发
wsgi自带,用语构建简单服务器 例子 from wsgiref.simple_server import make_server def index(env, res): res('200 ok', ...
- pycharm+python+Django之web开发环境的搭建(windows)
转载:https://blog.csdn.net/yjx2323999451/article/details/53200243/ pycharm+python+Django之web开发环境的搭建(wi ...
- 使用eclipse搭建第一个python+Django的web开发实例
python+Django的web开发实例 一.创建一个项目如果这是你第一次使用Django,那么你必须进行一些初始设置.也就是通过自动生成代码来建立一个Django项目--一个Django项目的 ...
- Python学习-day18 Web框架
众所周知,对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端. ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1 ...
- 【Python】【Web开发】
# [[Web开发]] ''' 最早的软件都是运行在大型机上的,软件使用者通过“哑终端”登陆到大型机上去运行软件.后来随着PC机的兴起,软件开始主要运行在桌面上,而数据库这样的软件运行在服务器端,这种 ...
- python +Django 搭建web开发环境初步,显示当前时间
1.python 的安装 网上很多关于django跟python 开发的资料,这块我正在实习准备用这个两个合起来搞一个基于web 的东西出来现在开始学习,写点东西记录一下心得. 开发环境是window ...
- [ Python ] Flask 基于 Web开发 大型程序的结构实例解析
作为一个编程入门新手,Flask是我接触到的第一个Web框架.想要深入学习,就从<FlaskWeb开发:基于Python的Web应用开发实战>这本书入手,本书由于是翻译过来的中文版,理解起 ...
- Python学习之web框架 Flask
一.通过PIP 安装Flask 1.1 Windows环境安装pip A.首先PIP进入官网(https://pypi.python.org/pypi/pip)下载gz包 B.对gz压缩包进行解压,解 ...
- 学习 Laravel - Web 开发实战入门笔记(1)
本笔记根据 LearnKu 教程边学边记而成.该教程以搭建出一个类似微博的Web 应用为最终成果,在过程中学习 Laravel 的相关知识. 准备开发环境 原教程使用官方推荐的 Homestead 开 ...
随机推荐
- Android控件系列之RadioButton&RadioGroup
学习目的: 1.掌握在Android中如何建立RadioGroup和RadioButton 2.掌握RadioGroup的常用属性 3.理解RadioButton和CheckBox的区别 4.掌握Ra ...
- iOS开发——pch文件创建
新换的公司,接手的项目里面连pch文件都没有,每次需要用到屏幕的宽高时,都是现写.今天既然碰到了,就把PCH这个玩意说一下. 1.Command+N,打开新建文件窗口:iOS->Other-&g ...
- css清除浮动float的三种方法总结
原文地址: http://my.oschina.net/leipeng/blog/221125 张大神的解析: http://www.zhangxinxu.com/wordpress/2010/01/ ...
- 利用STM32F唯一96bit序列号实现反拷贝加密的源代码公开(转)
源:利用STM32F唯一96bit序列号实现反拷贝加密的源代码公开 //---------------------------------------------------------------- ...
- IOS开发中UITableView(表视图)的滚动优化及自定义Cell
IOS开发中UITableView(表视图)的滚动优化及自定义Cell IOS 开发中UITableView是非常常用的一个控件,我们平时在手机上看到的联系人列表,微信好友列表等都是通过UITable ...
- ps--记录几个方法步骤
1.图片文字去掉 1.1 矩形工具-->吸管-->alt+delete 1.2 钢笔工具-->Ctrl+回车(变换选区)-->吸管-->alt+delete 2.图层锁不 ...
- TCP/IP详解--拥塞控制 & 慢启动 快恢复 拥塞避免
TCP的拥塞控制 1. 拥塞:即对资源的需求超过了可用的资源.若网络中许多资源同时供应不足,网络的性能就要明显变坏,整个网络的吞吐量随之负荷的增大而下降. 拥塞控制:防止过多的数据注入到网络中,这样 ...
- Spring与Struts框架整合
Spring与Struts框架整合 Struts,用Action处理请求 Hibernate,操作数据库 Spring,负责对象创建 Spring与Struts框架整合的关键点在与:让Struts框架 ...
- Mysql中主从复制的原理、配置过程以及实际案例
Mysql中主从复制的原理.配置过程以及实际案例1.什么是主从复制?原理:主从分离,什么意思呢?我们不妨画个图看看.如图1所示: 2.准备工作:预备两台服务器,我这里使用虚拟机安装了两个Centos6 ...
- Windows7启用超级管理员administrator账户的多种方法
开启win7家庭普通版的超级管理员账户Administrotor WIN7家庭普通版没有"本地用户和组"项,不能通过"用计算机右键-管理"的方法开启超级管理员帐 ...