Python之WEB编程入门

前面很多内容都是在介绍 Python 的知识点。这些知识点可以说涵盖了 Python 80% 的方面。在接着往下分享的话,那就是 Python 高阶的知识点。比如语言本身的自身能力(反射)、描述器、元编程等内容了。再接着就是 WEB 相关的内容了。

接下来会花很多篇章来介绍 WEB 相关的知识点。然后再介绍如何从零开始实现一个 WEB 框架。内容还是比较多的,请有需要的同学多多关注。

这里从零开始写一个框架出来,目的是为了更好地使用现有的 WEB 框架,了解现有 WEB 框架是如何工作的,这才是我们最终的目的。

WEB发展历程

最早的时候,WEB就是一些静态文件。由开发人员通过编辑器直接编辑生成静态的HTML页面,如果要修改WEB页面的内容或布局,就得编辑这些静态的HTML文件。早期的时候就是这么来玩的,是不是让人着实不爽?

由于静态WEB页面无法与用户进行交互(提交表单等操作)。为了解决这一问题,人们开发出了一种称为CGI(Common Gateway Interface)的技术,使用 C/C++ 编写。

由于WEB应用的特点是频繁更新,用低级语言非常不适合高效地开发。而脚本语言(JSP、PHP)由于开发效率高,与HTML结合紧密,因此,迅速取代了CGI模式。JSP使用Java编写脚本,而PHP则是开源的脚本语言。

最早的动态页面是CGI实现的,CGI是怎么工作的?当请求某个地址的时候,http服务器会启动一个外部程序,并且把外部程序的输出返回给用户。CGI规范了http和外部程序的通信方式。

  • 输入:通过环境变量
  • 输出:通过标准输出

CGI的缺点:

  1. 并发模式是多进程
  2. 进程由http服务器管理

基于CGI以上的缺点,由此衍生出了FastCGI技术,把进程管理交给其他服务来处理。http服务器把http协议转化成fastcgi协议,通过socket发送给FastCGI守护进程。这样并发模型变得多种多样,进程管理由FastCGI实现,单一原则。

另一个分支就是由cgi发展的wsgi技术。接下来就看看wsgi。

Web框架的主要关键点:

  1. 路由
  2. Request解析
  3. Response封装

WSGI

Web服务器网关接口(WSGI)是Python Web应用程序框架和Web服务器之间标准接口的规范。

在Python Web应用程序的早期,由于没有统一的标准,将Web应用程序框架连接到Web服务器是不容易的。Python Web应用程序是用于工作的,具体是通过运用现有的CGI、FastCGI或mod_python(Apache)标准之一。这意味着在一个Web服务器上工作的应用程序可能无法在另一个Web服务器上工作。换言之,统一的应用程序与Web服务器之间的互操作性没有了。

WSGI通过在服务器和Web应用框架之间指定一个简单但统一的接口来解决这个问题,从而允许移植Web应用程序。

WSGI指定了两个方面:服务器(或网关)端,以及应用程序或框架端。WSGI请求得到了以下处理:

  • 服务器端执行应用程序,给应用程序提供一个环境和一个回调函数。
  • 应用程序处理请求,并使用所提供的回调函数返回响应给服务器。

流程如下:

wsgi两部分组成:容器,应用。也可以把容器称为网关或服务器。容器实现了协议转换,由http协议到wsgi协议的转换;应用用来处理业务逻辑。

容器和应用怎么通信呢?FastCGI通过socket;wsgi的容器和应用通过函数调用进行通信。什么情况下可以通过函数调用实现通信?同一进程内可以实现函数调用,wsgi的容器和应用是运行在同一进程之中。

常用的wsgi容器:gunicorn,uWSGI。

一个可工作的HelloWorld

说了这么多,还是看个简单的例子吧,看看wsgi是如何工作的。接下来实现一个helloworld的程序。

wsgi application应该是一个callable对象。函数是可调用的,如果一个类有__call__方法,也是可调用的。

wsgi函数的书写有一定的要求,它的函数签名一般如下,比较固定:

def application(environ, start_response):
    ‘‘‘
    # 函数传入的参数是有规定的:
    # environ:environ变量是从服务器传递到应用程序的环境变量的字典,如请求内容等;
    # 由公共网关接口(CGI)规范定义。WSGI在其规范中规定了一些环境变量受托者
    # start_response:一个可调用对象,其提供一个从服务器端到应用程序端的回调,从而开启服务器端的
    # 响应处理。它必须有两个位置参数。第一个应该是具有整型状态吗的状态字符串,第二个是一个(header_name, header_value)列表,其是描述HTTP头响应的元组。
    ‘‘‘
    pass

上述函数传入的参数是有规定的:

  1. environenviron变量是从服务器传递到应用程序的环境变量的字典,如请求内容等;由公共网关接口(CGI)规范定义。WSGI在其规范中规定了一些环境变量受托者
  2. start_response:一个可调用对象,其提供一个从服务器端到应用程序端的回调,从而开启服务器端的响应处理。它必须有两个位置参数。第一个应该是具有整型状态吗的状态字符串,第二个是一个(header_name, header_value)列表,其是描述HTTP头响应的元组。

要了解更多的WSGI规范,可以看看PEP 3333提案。

开始写:

def application(environ, start_response):
    body = ‘hello world‘
    status = ‘200 OK‘
    headers = [
        (‘content-type‘, ‘text/plain‘),
        (‘content-length‘, str(len(body)))
    ]
    start_response(status, headers)
    return [body.encode()]

# 以上代码只有应用,没有容器;
if __name__ == ‘__main__‘:
    # 引入wsgi容器
    from wsgiref.simple_server import make_server

    server = make_server(‘0.0.0.0‘, 8000, application)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        server.shutdown()

运行以上代码,并在浏览器上进行访问:

[[email protected]_113_230_centos 12-web]$ python wsgi01.py
183.192.25.121 - - [13/Oct/2018 18:48:08] "GET / HTTP/1.1" 200 11
183.192.25.121 - - [13/Oct/2018 18:48:09] "GET /favicon.ico HTTP/1.1" 200 11
183.192.25.121 - - [13/Oct/2018 18:48:09] "GET /favicon.ico HTTP/1.1" 200 11

也可以通过curl命令行工具进行访问:

curl -XPOST http://127.0.0.1:8000 -d ‘{"a": 1}‘

接下来使用gunicorn容器来运行上述代码:

# 首先安装gunicorn
pip install gunicorn

# 接着运行wsgi01.py的application应用
[[email protected]_113_230_centos 12-web]$ gunicorn -b 0.0.0.0 wsgi01:application
[2018-10-13 18:55:38 +0800] [18359] [INFO] Starting gunicorn 19.6.0
[2018-10-13 18:55:38 +0800] [18359] [INFO] Listening at: http://0.0.0.0:8000 (18359)
[2018-10-13 18:55:38 +0800] [18359] [INFO] Using worker: sync
[2018-10-13 18:55:38 +0800] [18397] [INFO] Booting worker with pid: 18397

通常在开发的时候,使用wsgiref就行了,如果在生产中,可以使用第三方的wsgi容器。

上面的代码中,如果想看environ中的内容,可以在上述代码中添加如下的代码:

for k, v in environ.items():
    print(‘{} => {}‘.format(k, v))

# 完整的代码如下:
# coding: utf-8

def application(environ, start_response):
    for k, v in environ.items():
        print(‘{} => {}‘.format(k, v))
    body = ‘hello world‘
    status = ‘200 OK‘
    headers = [
        (‘content-type‘, ‘text/plain‘),
        (‘content-length‘, str(len(body)))
    ]
    start_response(status, headers)
    return [body.encode()]

# 以上只有应用代码,没有容器

if __name__ == ‘__main__‘:

    from wsgiref.simple_server import make_server
    server = make_server(‘0.0.0.0‘, 8000, application)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        server.shutdown()

如果再次运行代码,会有如下的输出:

wsgi.input => <gunicorn.http.body.Body object at 0x7fe20286c198>
HTTP_ACCEPT_ENCODING => gzip, deflate
HTTP_ACCEPT_LANGUAGE => zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
SERVER_PORT => 8000
RAW_URI => /
REMOTE_PORT => 16252
HTTP_USER_AGENT => Mozilla/5.0 (Windows NT 6.3; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0
wsgi.file_wrapper => <class ‘gunicorn.http.wsgi.FileWrapper‘>
REMOTE_ADDR => 183.192.25.121
wsgi.version => (1, 0)
wsgi.url_scheme => http
HTTP_HOST => 115.159.125.96:8000
wsgi.multithread => False
HTTP_ACCEPT => text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
wsgi.multiprocess => False
QUERY_STRING =>
SERVER_SOFTWARE => gunicorn/19.6.0
SERVER_PROTOCOL => HTTP/1.1
PATH_INFO => /
HTTP_CONNECTION => keep-alive
REQUEST_METHOD => GET
wsgi.run_once => False
wsgi.errors => <gunicorn.http.wsgi.WSGIErrorsWrapper object at 0x7fe20286c1d0>
SERVER_NAME => 0.0.0.0
HTTP_CACHE_CONTROL => max-age=0
SCRIPT_NAME =>
gunicorn.socket => <socket.socket fd=10, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(‘10.105.113.230‘, 8000), raddr=(‘183.192.25.121‘, 16252)>

environ中有一个QUERY_STRING的key,我们可以打印该信息:

[[email protected]_113_230_centos 12-web]$ cat wsgi01.py
# coding: utf-8

def application(environ, start_response):
    print(environ.get(‘QUERY_STRING‘))

    body = ‘hello world‘
    status = ‘200 OK‘
    headers = [
        (‘content-type‘, ‘text/plain‘),
        (‘content-length‘, str(len(body)))
    ]
    start_response(status, headers)
    return [body.encode()]

# 以上只有应用代码,没有容器
if __name__ == ‘__main__‘:
    from wsgiref.simple_server import make_server
    server = make_server(‘0.0.0.0‘, 8000, application)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        server.shutdown()

在浏览器的URL地址栏里输入:

http://115.159.125.96:8000?name=lavenliu&age=25

运行效果如下:

[[email protected]_113_230_centos 12-web]$ gunicorn -b 0.0.0.0 wsgi01:application
[2018-10-13 19:15:36 +0800] [20609] [INFO] Starting gunicorn 19.6.0
[2018-10-13 19:15:36 +0800] [20609] [INFO] Listening at: http://0.0.0.0:8000 (20609)
[2018-10-13 19:15:36 +0800] [20609] [INFO] Using worker: sync
[2018-10-13 19:15:36 +0800] [20647] [INFO] Booting worker with pid: 20647
name=lavenliu&age=25 # 获得的来自客户端的输入

上面的输出中,

name=lavenliu&age=25 # 获得的来自客户端的输入

接下来就是解析来自客户端的输入,解析这件事也不是很困难,不过有标准库来处理这个解析的工作,引入标准库:

from urllib.parse import parse_qs
from html import escape

parse_qs(‘name=lavenliu&age=25‘)
# 返回的value是列表,因为有可能key有同名的

执行结果如下:

{‘age‘: [‘25‘], ‘name‘: [‘lavenliu‘]}

接下来的代码如下:

[[email protected]_113_230_centos 12-web]$ cat wsgi02.py
# coding: utf-8
from urllib.parse import parse_qs
from html import escape

def application(environ, start_response):
    params = parse_qs(environ.get(‘QUERY_STRING‘))
    name = params.get(‘name‘, [‘anon‘])[0]
    body = ‘hello {}‘.format(name)  # 如果用户在浏览器里输入非法的东东时,可能会有问题
    status = ‘200 OK‘
    headers = [
        (‘content-type‘, ‘text/html‘),
        (‘content-length‘, str(len(body)))
    ]
    start_response(status, headers)
    return [body.encode()]

# 以上只有应用代码,没有容器
if __name__ == ‘__main__‘:
    from wsgiref.simple_server import make_server

    server = make_server(‘0.0.0.0‘, 8001, application)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        server.shutdown()

如果用户在浏览器这样输入,就有可能有问题:

http://115.159.125.96:8001?name=<script type=‘text/javascript‘>alert(‘f**k it‘)</script>
# chrome浏览器则没有问题,它会检测到这个输入有问题;如果在IE6上可能就会执行上述代码。
Navigated to http://115.159.125.96:8001/?%3Cscript%20type=%27text/javascript%27%3Ealert(%27f**k%20it%27)%3C/script%3E
?name=<script type=‘text/javascript‘>alert(‘f**k it‘)</script>:1 The XSS Auditor refused to execute a script in ‘http://115.159.125.96:8001/?name=%3Cscript%20type=%27text/javascript%27%3Ealert(%27f**k%20it%27)%3C/script%3E‘ because its source code was found within the request. The auditor was enabled as the server sent neither an ‘X-XSS-Protection‘ nor ‘Content-Security-Policy‘ header.

Navigated to http://115.159.125.96:8001/?name=%3Cscript%20type=%27text/javascript%27%3Ealert(%27f**k%20it%27)%3C/script%3E

# 在火狐浏览器就能通过,不会报错
# IE11版本则没有这个问题

好了,今天就到这里。不知你看了这些内容有什么想法,随时欢迎留言进行讨论、一起学习。

后面会逐步介绍如何在此基础上进行封装,进而做出一个小小的框架。有任何问题欢迎留言交流。

原文地址:https://blog.51cto.com/lavenliu/2417870

时间: 2024-10-07 06:20:39

Python之WEB编程入门的相关文章

初识Django —Python API接口编程入门

初识Django -Python API接口编程入门 一.WEB架构的简单介绍 Django是什么? Django是一个开放源代码的Web应用框架,由Python写成.我们的目标是用Python语言,基于Django框架,利用MVC模型,实现后台方面的针对数据库的API开发.先了解一下互联网的WEB架构, 如上图: 互联网的WEB架构大致分为三层,web层.app层和数据库层.Web层:如apache网站服务器:app层主要是应用业务:DB指后台数据库.随着互联网的高速发展,网站访问量的增长.数

都说python是最佳编程入门语言,为什么你学习却是如此坎坷?

为什么都说python是最佳编程入门语言? 引用Elliott Hauser 的说法,好的编程语言学生在入门时需要获得五样东西. 非常棒的首次体验,就像一本书的第一页,首先需要"入迷",学习新知识不可避免的会遇到挫折,但要有持续的热情和好奇心,这对于那些从未接触过编码的年轻人来说是至关重要的: Web编程的能力,对于职业发展和程序工艺来说,Web编程越来越重要,学生有机会就应当掌握一定的Web架构基础: 桌面编程能力,尽管将来趋势将更多的转移到Web应用上,但没什么能比开发和运行一个本

golang之web编程入门

golang之web编程入门示例,聊聊数行,简单理解. package main import ( "fmt" "html/template" "log" "net/http" "strings" ) func sayhelloName(w http.ResponseWriter, r *http.Request) { r.ParseForm() //解析url传递的参数,对于POST则解析响应包的主体(r

Python可视化界面编程入门

Python可视化界面编程入门具体实现代码如所示: (1)普通可视化界面编程代码入门: import sysfrom PyQt5.QtWidgets import QWidget,QApplication #导入两个类来进行程序界面编程 if __name__=="__main__": #创建一个Application的类 app=QApplication(sys.argv) #创建一个窗口 w=QWidget() #设置窗口的尺寸大小 w.resize(400,200) # 移动窗口

python的web编程

python的web相关模块,有两种不同的类型,urlparse和urllib,分别以不同的功能和兼容性来处理URL,前者主要包括urlparse().urlunparse()和urljoin(),后者可以使用urlopen()或urlretrive()创建web客户端.urllib2模块是一个比urllib功能更加高级的模块. web开发,无疑要用到统一资源定位器URL,URL格式如下: prot_sch://net_loc/path:params?query#flag prot_sch:网路

Python的Web编程[0] -&gt; Web客户端[1] -&gt; Web 页面解析

 Web页面解析 / Web page parsing 1 HTMLParser解析 下面介绍一种基本的Web页面HTML解析的方式,主要是利用Python自带的html.parser模块进行解析.其主要步骤为: 创建一个新的Parser类,继承HTMLParser类; 重载handler_starttag等方法,实现指定功能; 实例化新的Parser并将HTML文本feed给类实例. 完整代码 1 from html.parser import HTMLParser 2 3 # An HTML

Python的Web编程[2] -&gt; WebService技术[0] -&gt; 利用 Python 调用 WebService 接口

WebService技术 / WebService Technology 1 关于webservice / Constants WebService是一种跨编程语言和跨操作系统平台的远程调用技术. WebService主要由以下三种技术构成,XML+XSD,SOAP和WSDL XML+XSD: WebService采用HTTP协议传输数据,采用XML格式封装数据(即XML中说明调用远程服务对象的哪个方法,传递的参数是什么,以及服务对象的返回结果是什么).XML是WebService平台中表示数据

Java Web编程入门--spring boot 项目构建

1.clean项目 2.Maven install 注:不要Maven clean BUG: 1.参考博客 Tomcat启动时报错,Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext 错误日志: 22-Aug-2017 15:25:34.939 信息 [main] org.apache.catalina.startup.VersionLoggerListener.l

编程入门教程

编程入门教程 编程入门教程由QKXue.NET梳理的面向程序开发入门初学者的编程入门教程,是一个涵盖了游戏.PLC.VB.数控.JAVA.APP.Matlab.C语言.Shell.IOS.Android安卓等手机和PC编程的入门教程. 1. 编程入门教程 1.1. 游戏编程入门 游戏编程入门适用于任何对C++语言有基本了解的读者阅读,适宜作为读者进入游戏开发领域的技术入门学习用书. 游戏编程入门介绍如何设计和构建自己的电脑游戏.游戏编程入门不是泛泛地介绍编程理论,而是引导读者开发一个“即插即用”