什么是Web框架

Web框架是用来简化Web开发的软件框架。事实上,框架根本就不是什么新的东西,它只是一些能够实现常用功能的Python文件。可以把框架看作是工具的集合,而不是特定的东西。框架的存在是为了避免重新发明轮子,并且在创建一个新的项目时帮助减轻开发成本。典型的框架提供了如下常用功能:
þ 管理路由。
þ 支持数据库。
þ 支持MVC。
þ 支持ORM。
þ 支持模板引擎。
þ 管理会话和Cookies。

5.1.2什么是MVC

MVC早在1978年就作为Smalltalk的一种设计模式被提出来了,应用到Web应用上,模型Model用于封装与业务逻辑相关的数据和数据处理方法,视图View是数据的HTML展现,控制器Controller负责响应请求,协调Model和View。Model,View和Controller的分开,是一种典型的关注点分离的思想,不仅使得代码复用性和组织性更好,使得Web应用的配置性和灵活性更好。常见的MVC模式如图5.1所示。
图5.1MVC模式示意图

5.1.3什么是ORM

对象-关系映射(Object/RelationMapping,简称ORM),是随着面向对象软件开发方法发展而产生的。面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。对象和关系数据是业务实体的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系。因此,对象-关系映射(ORM)系统一般以中间件的形式存在,主要实现程序对象到关系数据库数据的映射。ORM与数据库的对应关系如图5.2所示。
图5.2 ORM与数据库的对应关系

5.1.4什么是模板引擎

模板引擎是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎一般生成一个标准的HTML文档。Python很多Web框架都内置了模板引擎,使用了模板引擎可以在HTML页面中使用变量,例如:
01
02
03
04
05
06

Hello,{{username}}!


07
08
上述代码中的{{}}中的变量会被替换成变量值,这就可以让程序实现界面与数据分离,业务代码与逻辑代码的分离,这就大大提升了开发效率,良好的设计也使得代码重用变得更加容易。

5.2 常用的Python Web框架

前面我们学习了WSGI(服务器网关接口),它是Web服务器和Web应用程序或框架之间的一种简单而通用的接口。也就是说,只要遵循WSGI接口规则,就可以自主开发Web框架。所以,各种开源Web框架至少有上百个,关于Python框架优劣的讨论也仍在继续。作为初学者,应该选择一些主流的框架来学习使用。这是因为主流框架文档齐全,技术积累较多,社区繁盛,并且能得到更好的支持。下面,介绍几种Python的主流Web框架。

1.Django

这可能是最广为人知和使用最广泛的Python Web框架了。Django有世界上最大的社区,最多的包。它的文档非常完善,并且提供了一站式的解决方案,包括缓存、ORM、管理后台、验证、表单处理等,使得开发复杂的数据库驱动的网站变得简单。但是,Django系统耦合度较高,替换掉内置的功能比较麻烦,所以学习曲线也相当陡峭。

2.Flask

Flask是一个轻量级Web应用框架。它的名字暗示了它的含义,它基本上就是一个微型的胶水框架。它把Werkzeug和Jinja粘合在了一起,所以它很容易被扩展。Flask也有许多的扩展可以供你使用,Flask也有一群忠诚的粉丝和不断增加的用户群。它有一份很完善的文档,甚至还有一份唾手可得的常见范例。Flask很容易使用,你只需要几行代码就可以写出来一个“HelloWorld”。

3.Tornado

Tornado不单单是个框架,还是个Web服务器。它一开始是给FriendFeed开发的,后来在2009年的时候也给Facebook使用。它是为了解决实时服务而诞生的。为了做到这一点,Tornado使用了异步非阻塞IO,所以它的运行速度非常快。

4.FastAPI

FastAPI是一个现代的,快速(高性能)Python Web框架。基于标准的Python类型提示,使用Python5.6+构建API的Web框架。FastAPI使用了类型提示,能够减少开发人员容易引发的错误。此外,FastAPI可以自动生成API文档,编写API接口后,可以使用符合标准的UI如Swagger UI,ReDoc等来使用API。
以上4种框架各有优劣,使用时需要根据自身的应用场景选择适合自己的Web框架。在后面的章节中,会详细介绍每一个框架的使用。

HTTP协议

服务器代码:

  1. import socket
  2. sock = socket.socket()
  3. sock.bind(("127.0.0.1",8800))
  4. sock.listen(5)
  5. while True:
  6. print("server waiting ....")
  7. conn, addr = sock.accept()
  8. data = conn.recv(1024)
  9. print("data", data)
  10. conn.send(b'HTTP/1.1 200 OK\r\n\r\nhello world')
  11. conn.close()

浏览器中访问:127.0.0.1:8800, 会输出hello world。

image.png
data由请求首行和请求体组成,数据解析:

  1. b'GET /favicon.ico HTTP/1.1\r\nHost: 127.0.0.1:8800\r\nConnection: keep-alive\r\nsec-ch-ua: "Chromium";v="104", " Not A;Brand";v="99", "Google Chrome";v="104"\r\nsec-ch-ua-mobile: ?0\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36\r\nsec-ch-ua-platform: "macOS"\r\nAccept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8\r\nSec-Fetch-Site: same-origin\r\nSec-Fetch-Mode: no-cors\r\nSec-Fetch-Dest: image\r\nReferer: http://127.0.0.1:8800/\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9,en;q=0.8,la;q=0.7,ja;q=0.6\r\nCookie: csrftoken=6v0DgFdfctKKk978I5vMEQi7sFTCokDHLTtj0NrNJbOeKhdtwGlOGUYPInayFZSg; sessionid=k57zdqw8pdov11akjekomwvydaxpsv6i\r\n\r\n'

请求方法 + URI + 协议版本如下:

  1. GET /favicon.ico HTTP/1.1

请求首行:

  1. Host: 127.0.0.1:8800\r\nConnection: keep-alive\r\nsec-ch-ua: "Chromium";v="104", " Not A;Brand";v="99", "Google Chrome";v="104"\r\nsec-ch-ua-mobile: ?0\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36\r\nsec-ch-ua-platform: "macOS"\r\nAccept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8\r\nSec-Fetch-Site: same-origin\r\nSec-Fetch-Mode: no-cors\r\nSec-Fetch-Dest: image\r\nReferer: http://127.0.0.1:8800/\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9,en;q=0.8,la;q=0.7,ja;q=0.6\r\nCookie: csrftoken=6v0DgFdfctKKk978I5vMEQi7sFTCokDHLTtj0NrNJbOeKhdtwGlOGUYPInayFZSg; sessionid=k57zdqw8pdov11akjekomwvydaxpsv6i\r\n\r\n

请求体:
如果是get, 直接显示在参数里。
只有post请求,才有请求体。

注意:请求首行之间只有一个换行(\r\n),而请求首行和请求体之间,需要2个换行(\r\n\r\n)。