理解 Web 应用
我们可以这样理解:所有的 Web 应用本质上就是一个 Socket 服务端,而用户的浏览器就是一个 Socket 客户端。
浏览器在地址栏回车时会帮我们把 URL 封装到 HTTP 协议的报文中,然后请求到 URL 对应的服务端,服务端按照 HTTP 协议来响应请求,浏览器接收到 HTTP 响应后就会按照相应的规则将报文中响应体的部分渲染到浏览器,这部分通常是 HTML 格式的文本内容。
Socket 响应 Web 请求
响应字符串
下面就使用 Socket 完成一个最基本的基于 HTTP 协议的服务端。
import socket
# 创建 Socket 对象
server = socket.socket()
# 绑定到本机回环地址的 8080 端口
server.bind(('127.0.0.1', 8080))
# 允许监听
server.listen()
# 阻塞等待连接请求的到来
conn, addr = server.accept()
# 接收来自浏览器的请求信息
browser_msg = conn.recv(1024)
# 解码字节流为字符串
msg = browser_msg.decode('utf8')
print(msg)
conn.send(b'HTTP/1.1 200 ok \r\n\r\n')
conn.send(b'hello')
conn.close()
server.close()
运行上面程序,浏览器请求 127.0.0.1:8080
,会发现 hello
已经显示在浏览器上:
并且控制台输出了浏览器的请求报文如下:
GET / HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
Sec-Fetch-User: ?1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: navigate
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-US;q=0.7,en-GB;q=0.6,en-IN;q=0.5,en-NZ;q=0.4,en-ZA;q=0.3,en-CA;q=0.2,en-AU;q=0.1
响应 HTML
上个版本中,我们是直接返回给浏览器一个普通的字符串,而浏览器是支持解析 HTML 的,如果我们有一个 HTML 文件,该如何响应给浏览器呢?
在脚本统计目录创建一个 index.html
文件内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<h1>hello</h1>
</body>
</html>
修改脚本如下:
import socket
# 创建 Socket 对象
server = socket.socket()
# 绑定到本机回环地址的 8080 端口
server.bind(('127.0.0.1', 8080))
# 允许监听
server.listen()
# 阻塞等待连接请求的到来
conn, addr = server.accept()
# 接收来自浏览器的请求信息
browser_msg = conn.recv(1024)
# 解码字节流为字符串
msg = browser_msg.decode('utf8')
conn.send(b'HTTP/1.1 200 ok \r\n\r\n')
# 读取 html 文件内容
with open('index.html','rb') as html_file:
conn.send(html_file.read())
conn.close()
server.close()
运行该脚本,浏览器请求 127.0.0.1:8080
,浏览器显示如下:
响应图片
在上一个示例中查看浏览器的请求:
可以看到在浏览器地址栏敲上 127.0.0.1:8080
回车后浏览器实际上是发送了两个请求,一个就是以地址栏为目标地址的请求,另一个则是请求当前页面的图标,地址为 127.0.0.1:8080/favicon.ico
,下面来响应一下这个图标。
在脚本同级目录下放上如下名为 favicon.jpg
的图片作为图标:
修改脚本如下:
import socket
# 创建 Socket 对象
server = socket.socket()
# 绑定到本机回环地址的 8080 端口
server.bind(('127.0.0.1', 8080))
# 允许监听
server.listen()
while 1:
# 阻塞等待连接请求的到来
conn, addr = server.accept()
# 接收来自浏览器的请求信息
browser_msg = conn.recv(1024)
# 解码字节流为字符串
msg = browser_msg.decode('utf8')
print(msg)
conn.send(b'HTTP/1.1 200 ok \r\n\r\n')
if 'favicon.ico' in msg:
# 读取图片文件
with open('favicon.jpg','rb') as jpg_file:
conn.send(jpg_file.read())
else:
# 读取 html 文件内容
with open('index.html','rb') as html_file:
conn.send(html_file.read())
浏览器请求 127.0.0.1:8080
,可以看到图标已经显示在标签栏:
wsgiref 实现的 Web 应用
上面是我们自己编写原生的 Socket 代码来完成对 HTTP 请求的响应,而类似这种功能已经有很多成熟的模块或框架帮我们做了,wsgiref 就是其中之一,下面使用 wsgiref 模块完成上面响应 Web 请求的功能:
from wsgiref.simple_server import make_server
def index():
with open('index.html', 'rb') as html_file:
html_bytes = html_file.read()
return html_bytes
def favicon():
with open('favicon.jpg', 'rb') as jpg_file:
jpg_bytes = jpg_file.read()
return jpg_bytes
def run_server(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) # 设置HTTP响应的状态码和头信息
url = environ['PATH_INFO'] # 取到用户输入的url
response = ''
if url == '/':
response = index()
elif url == '/favicon.ico':
response = favicon()
return [response, ]
if __name__ == '__main__':
httpd = make_server('127.0.0.1', 8080, run_server)
print("我在8080等你哦...")
httpd.serve_forever()
使用 Jinjia2 返回动态页面
1、安装:
$ pip3 install jinjia2
2、主程序代码如下:
from wsgiref.simple_server import make_server
from jinja2 import Template
def users():
with open("user_template.html", "r", encoding="utf-8") as f:
data = f.read()
template = Template(data) # 生成模板文件
# 从数据库中取数据
import pymysql
conn = pymysql.connect(
host="127.0.0.1",
port=3306,
user="root",
password="root",
database="testdb",
charset="utf8",
)
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
cursor.execute("SELECT * FROM UserInfo;")
user_list = cursor.fetchall()
print(user_list)
# 实现字符串的替换
ret = template.render({"user_list": user_list}) # 把数据填充到模板里面
return [bytes(ret, encoding="utf8"), ]
def index():
with open("index.html", "rb") as f:
data = f.read()
return [data, ]
# 定义一个url和函数的对应关系
URL_LIST = [
("/index", index),
("/users", users),
]
def run_server(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) # 设置HTTP响应的状态码和头信息
url = environ['PATH_INFO'] # 取到用户输入的url
func = None # 将要执行的函数
for i in URL_LIST:
if i[0] == url:
func = i[1] # 去之前定义好的url列表里找url应该执行的函数
break
if func: # 如果能找到要执行的函数
return func() # 返回函数的执行结果
else:
return [bytes("404没有该页面", encoding="utf8"), ]
if __name__ == '__main__':
httpd = make_server('', 8080, run_server)
print("Serving HTTP on port 8080...")
httpd.serve_forever()
3、在脚本统计目录新建模板文件 user_template.html
:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<table border="1">
<thead>
<tr>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
</tr>
</thead>
<tbody>
{% for user in user_list %}
<tr>
<td>{{user.Name}}</td>
<td>{{user.AGE}}</td>
<td>{{user.SEX}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
4、UserInfo
表数据如下:
5、启动主程序,浏览器访问 127.0.0.1:8080/users
,结果如下:
MVC 和 MTV 框架
MVC
MVC 是 Web 服务器开发领域里著名的开发模式,所谓 MVC 就是把 Web 应用分为模型(Model),控制器(Controller)和视图(View)三层,他们之间以一种插件式的、松耦合的方式连接在一起,模型负责业务对象与数据库的映射(ORM),视图负责与用户的交互(页面),控制器接受用户的输入调用模型和视图完成用户的请求,其示意图如下所示:
MTV
MTV 模式被 Django 所使用,其本质和 MVC 是一样的,也是为了各组件间保持松耦合的关系,知识定义上有些许不同,Django 的 MTV 分别代表的是:
- M 代表模型(Model):负责业务对象和数据库的关系映射(ORM)。
- T 代表模板(Template):负责如何把页面(HTML)展示给用户。
- V 代表视图(View):负责业务逻辑,并在适当时候调用 Model 和 Template。
除了以上三层之外,还需要一个 URL 分发器,它的作用是将一个个 URL 的对应的页面请求分发给不同的 View 处理,View 再调用相应的 Model 和 Template,MTV 的响应模式如下所示:
一般是用户通过浏览器向我们的服务器发起一个请求(request),这个请求会访问视图函数,视图函数调用模型,模型去数据库查找数据,然后逐级返回,视图函数把返回的数据填充到模板中空格中,最后返回网页给用户(如果不涉及到数据调用,那么这个时候视图函数直接返回一个模板也就是一个网页给用户)。
Django 的介绍与使用
Django 官网:djangoproject.com
Django 是 BSD 许可下的、开放源代码的项目,建议使用最新版本的 Python3。支持 Python 2.7 的版本是 Django 1.11 LTS。
支持的版本
Django 大概每 8 个月发布一次,这些版本将包含新功能和对现有功能的改进。
Hello Django
安装
1、直接在终端下执行下面命令进行下载安装:
$ pip3 install django==1.11.9
==1.11.9
表示安装指定的1.11.9
版本,如果不指定则默认安装最新版本。
创建工程
2、创建一个 Django 工程,以创建一个名为 mysite
的工程为例,在指定目录下执行如下命令:
$ django-admin startproject mysite
执行完成后会在目录下生成 mysite
文件夹,目录结构如下:
mysite/
├── manage.py
└── mysite
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py
其中各文件功用如下:
manage.py
:通过它可以调用 Django shell 和数据库,启动关闭项目与项目交互等;settings.py
:包含了项目的默认设置,包括数据库信息,调试标志以及其它的一些工作变量;urls.py
:负责把 URL 请求映射到应用程序中的代码块;wsgi.py
:所有与 Socket 相关的内容都是从这个文件开始;
启动并测试访问
3、进入 mysite
目录执行下面命令启动 Django 项目:
$ python3 manage.py runserver 127.0.0.1:8000
Performing system checks...
System check identified no issues (0 silenced).
February 13, 2020 - 09:19:31
Django version 1.11.9, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
4、浏览器访问:
创建并配置应用
5、在 manage.py
所在目录执行以下命令来创建应用:
$ python3 manage.py startapp app1
其中
app1
是应用名称,一个 Django 工程中可有多个应用。
命令执行完毕后会在manage.py
的同级目录下创建一个以应用名称命名的文件夹,在这里就是app1
文件夹,其目录结构如下:app1 ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── tests.py └── views.py
这里我们只需要关注这两个文件:
models.py
:存放与 app 相关的表结构;views.py
:存放与 app 相关的视图函数;
6、如果要使用新创建的应用需要修改 mysite
目录下的 settings
文件,在 INSTALLED_APPS
字段下添加上创建的应用名称,如下:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app1' # 此行是我们创建的应用
]
配置路由
7、修改 urls.py
文件如下:
from django.conf.urls import url
from django.contrib import admin
from app1 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^hello/', views.hello)
]
在其中导入新创建应用的
views
模块,并配置路由/hello/
映射到views
模块的hello
方法,路由格式支持正则。
编写视图
8、在 app1.views
文件中添加如下代码:
def hello(request):
from django.shortcuts import HttpResponse
return HttpResponse('Hello Django~~~')
启动并测试访问
9、重启项目,访问 127.0.0.1:8000/hello/
,响应如下:
至此,基于 Django 的第一个 Web 工程就搭建完成。
小结
1、建议使用 pip 安装 Django,安装的方法如下:
$ pip install Django==<版本号>
更多安装方式可参考官网:https://www.djangoproject.com/download/。
2、Django 安装完成后,可进入 Python 的命令提示符下验证是否安装成功:
$ python3
Python 3.7.7 (default, Mar 10 2020, 15:43:33)
[Clang 11.0.0 (clang-1100.0.33.17)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import django
>>> django.__version__
'2.0'
3、创建 Django 项目可以在 CMD 窗口下输入 django-admin startproject <项目名>
完成,也能在 PyCharm 的可视化窗口下创建。
4、创建 App 由 Django 项目的 manage.py
实现,在 CMD 窗口或 Pycharm 的 Terminal 中输入 python manage.py startapp <App 名>
完成 App 的创建,项目创建后,需要掌握 Django 的目录结构以及含义:
manage.py
:命令行工具,允许以多种方式与项目进行交互。在 CMD 窗口下,将路径切换到项目根目录下并输入python manage.py help
,可以查看该工具的具体功能。__init__.py
:初始化文件,一般情况下无需修改。settings.py
:项目的配置文件,具体配置信息在后面详述。urls.py
:项目的 URL 配置,可理解为网站的地址信息。wsgi.py
:全称为 Python Web Server Gateway Interface,即 Python 服务器网关接口,是 Python 应用与 Web 服务器之间的接口,用于 Django 项目在服务器上的部署和上线,一般不需要修改。migrations
:用于数据库数据的迁移记录。admin.py
:当前 App 的后台管理系统。models.py
:定义映射类关联数据库,实现数据持久化,即 MTV 里面的模型(Model)。test.py
:自动化测试的模块。views.py
:逻辑处理模块,即 MTV 里面的视图(View)。
评论区