一、什么是web应用?
web应用本质上就是一个socket服务端,用户的浏览器就是socket的客户端。
这样一来,我们就能实现自己的简易版web框架了。话不多说,上代码。
import socket
server = socket.socket()
server.bind(('127.0.0.1', 8080)) # 绑定IP+PORT
server.listen(5) # 同时允许5个等待的连接(半连接池)
while True:
conn, addr = server.accept() # 阻塞,等待客户端链接
data = conn.recv(1024)
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 通讯协议
print(data)
# 手动处理http数据获取用户访问的路径
# http://127.0.0.1:8080/index,访问时,/index在请求头的第二位,按如下方式处理
current_path = data.decode('utf-8').split('\r\n')[0].split(' ')[1]
if current_path == '/index':
conn.send(b'index')
else:
conn.send(b'404')
在打印了请求响应后,我们看到了它的输出如下:
请求首行
b'GET / HTTP/1.1\r\n
请求头(一大堆kv键值对)
Host: 127.0.0.1:8080\r\n
Connection: keep-alive\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3\r\n
Accept-Encoding: gzip, deflate, br\r\n
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\n
\r\n
请求体
可以发现,它的请求头是HTTP,那么什么是http呢?
二、http协议简介
在之前的linux网络系列(戳这里)中,我已经介绍过了,这里再简单提一下。
http是一种超文本传输协议,它规定了客户端与服务端消息传输的格式。
'''
请求的格式:
请求首行
请求头(一堆k,v键值对)
回车,换行符
请求体(post请求携带的数据)
响应的格式:
响应首行
响应头(一堆k,v键值对)
回车,换行符
响应体(post请求携带的数据)
'''
http的四大特性:
"""
1、基于TCP/IP协议作用于应用层的协议
2、基于请求响应
3、无状态
4、无连接
"""
'''
无状态是指协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。即我们给服务器发送 HTTP 请求之后,服务器根据请求,
会给我们发送数据过来,但是,发送完,不会记录任何信息。
无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。
'''
响应状态码:
"""
1xx 服务器已经成功接收到你的数据正在处理,你可以继续提交其他数据
2xx 请求成功,服务器已经将你请求的数据发送给你了
3xx 重定向
4xx 请求资源不存在
5xx 服务器错误
"""
三、动静态网页
之前linux网络那篇博客已经介绍过(详情戳这里),这里再次提一嘴:
静态网页:
页面上的数据都是写死的,万年不变
动态网页:
页面上的数据是从后端动态获取的(比如后端获取当前时间;后端获取数据库数据然后传递给前端页面)
四、模板渲染
后端生成的数据直接传递给前端页面使用(并且前端页面可以灵活的操作数据),这就用到了模板语法。
模板渲染,模板语法需要依赖于第三方模块,安装如下:
pip3 install jinja2
模板语法: jinja2支持前端直接使用类似于python的语法操作数据
可以从数据库中查询数据,然后去替换html中的对应内容,再发送给浏览器完成渲染。 这个过程就相当于HTML模板渲染数据。 本质上就是HTML内容中利用一些特殊的符号来替换要展示的数据。jinja2就可以实现模板渲染。
到了这里,我们就要自定义一个框架,展示动态的网页了。
五、自定义web框架
除了使用jinja2来把数据处理前端数据外,我们还要用到一个wsgiref模块,来替换我们自己写的web框架的socket server部分。
WSGI(Web Server Common Interface)是专门为Python语言制定的web服务器与应用程序之间的网关接口规范。常用的WSGI服务器有uwsgi、Gunicorn。而Python标准库提供的独立WSGI服务器叫wsgiref,Django开发环境用的就是这个模块来做服务器。
wsgiref是官方给出的一个实现了WSGI标准用于演示用的简单Python内置库,它实现了一个简单的WSGI Server和WSGI Application(在simple_server模块中),主要分为五个模块:simple_server, util, headers, handlers, validate。
为了实现简易web框架,我们需要建立三个文件
main.py:主逻辑
urls.py:专门存放视图函数的对应关系
views.py:专门存放视图函数
还需要一个templates文件夹,专门放前端页面。
main.py:
from wsgiref.simple_server import make_server
from urls import *
def run(env, response):
'''
:param env: 请求相关的信息
:param response: 响应相关的信息
:return:
'''
print(env) # 是一个大字典 里面装了一堆处理好了的键值对数据
# 固定写法 后面列表里面一个个元组会以响应头kv键值对的形式返回给客户端
response('200 OK', [('username','shj'),('password','123')])
# 获取用户访问的路径,env是一个大字典,PATH_INFO键对应的值就是用户访问的路径
current_path = env.get('PATH_INFO')
# 定义一个存储函数名的变量
func = None
# 循环比对路由与视图函数的映射关系
for url_map in urls: # url_map=('/index', index)
if current_path == url_map[0]:
func = url_map[1]
# 只要匹配成功,直接结束循环
break
if func:
res = func(env)
else:
res = error(env)
return [res.encode('utf-8')] # 统一在这里对结果进行编码处理
if __name__ == '__main__':
server = make_server('127.0.0.1',8080,run)
server.serve_forever()
urls.py:
from views import *
urls = [
('/index', index),
('/login', login),
('/reg', reg),
('/get_time', get_time),
('/get_user', get_user),
('/get_db', get_db),
]
views.py:
import time
import pymysql
from jinja2 import Template
# 此文件专门用来存放视图函数
def index(env):
return 'index'
def login(env):
return 'login'
def reg(env):
return 'reg'
def get_time(env):
current_time = time.strftime('%Y-%m-%d %X')
# 打开html文件读取内容返回给客户端
with open('templates/get_time.html', 'r', encoding='utf-8') as f:
data = f.read()
# r模式打开的,read的结果是字符串
res = data.replace('@@time@@', current_time)
return res
'''自己手写数据,交给前端渲染'''
def get_user(env):
with open('templates/get_user.html', 'r', encoding='utf-8') as f:
data = f.read()
tmp = Template(data) # 生成模板文件
# 将字典传递给前端页面 前端通过变量名user_dic就可以获取到该字典
return tmp.render(user_dic={'name':'shj','password':'123'}) # 把数据填充到模板里面并返回
'''从数据库查询数据,交给前端页面渲染'''
def get_db(env):
# 连接数据库,获取数据,渲染到前端页面
conn = pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='123456',
database='test',
charset='utf8',
autocommit=True
)
cursor = conn.cursor(pymysql.cursors.DictCursor)
cursor.execute('select * from user_info')
user_dic = cursor.fetchall() # [{},{},]
with open('templates/get_db.html', 'r', encoding='utf-8') as f:
data = f.read()
tmp = Template(data) # 生成模板文件
return tmp.render(user_dic=user_dic) # 把数据填充到模板里面并返回
def error(env):
return '404 error'
get_db.html:(模板语法)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<table class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>password</th>
</tr>
</thead>
<tbody>
{% for user in user_dic %}
<tr>
<td>{{user.id}}</td>
<td>{{user.name}}</td>
<td>{{user.password}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>
get_user.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<p>{{user_dic}}</p>
<p>{{user_dic.name}}</p>
<p>{{user_dic['password']}}</p>
<p>{{user_dic.get('name')}}</p>
</body>
</html>
get_time.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
@@time@@
</body>
</html>
根据上面的代码,我们通过一张简易的请求流程图来总结一下
六、Django框架
6.1、什么是web框架呢?
Web应用框架(Web application framework)是一种开发框架,用来支持动态网站、网络应用程序及网络服务的开发。其类型有基于请求的和基于组件的两种框架。
Web应用框架有助于减轻网页开发时共通性活动的工作负荷,例如许多框架提供数据库访问接口、标准样板以及会话管理等,可提升代码的可再用性。
6.2、python三大主流框架
- Django:大而全。自带了很多功能模块,但是有点笨重
- Flask:短小精悍,自带的功能模块特别少,大部分都是依赖于第三方模块(小而精)
- Tornado:异步非阻塞。主要用在处理高IO,多路复用的情况,可以写游戏后端
web框架的三个部分:
a:socket
b:路由与视图函数
c:模板渲染
Django:
a用的别人的(wsgiref)
b、c是自己写的
Flask:
a用的别人的(werkzeug)
b是自己写的
c用的别人的(jinja2)
Tornado:
a、b、c都是自己写的
6.3、使用Django创建项目的注意事项
- 计算机的名称不能有中文
- 一个pycharm窗口就是一个项目,不要多个项目放在一个窗口里面
- 项目名不能起中文
6.4、下载
//下载
pip3 install django==1.11.11
//验证是否下载成功
django-admin
6.5、创建Django项目的方式
方式一(命令行创建):
创建Django项目: django-admin startproject 项目名
创建app应用: python3 manage.py startapp app01(我一般以app+编号命名)
启动django项目: python3 manage.py runserver
注意:
1、用命令行创建django默认不会自动创建templates文件夹,需要手动创建
2、去检查settings配置文件,是否加入了路径配置
方式二(pycharm创建):
FILE >>> new project 选择第二个django 需要注意名字不能有中文,选择本地的解释器,勾选后台管理
创建app
第一种:pycharm命令行创建:python36 manage.py startapp app01
第二种:Tools下面run manage task功能栏,然后输入:start app02
启动点小绿色箭头需要强调的是:
1、用django一定要保证只有一个在运行状态
2、一定记得清理浏览器的缓存
6.6、一些概念
app的概念:一个django项目就是一所大学,app就是大学里面的学院。
注意新创建的app需要在配置文件中注册才能生效
注册方式:
找到配置文件的INSTALLED_APPS
然后在后面追加,如下,既可以写全称,也可以简写
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config', # 可以用全称
'app01' # 也可以简写
]
6.7、django各个文件的作用
6.8、django必会三板斧
from django.shortcuts import HttpResponse, render, redirect
1、 HttpResponse:内部传入一个字符串参数,返回给浏览器。
例如:
def index(request): # 业务逻辑代码 return HttpResponse("OK")
2、render:除request参数外还接受一个待渲染的模板文件和一个保存具体数据的字典参数。
将数据填充进模板文件,最后把结果返回给浏览器。(类似于我们上面用到的jinja2)
两种给前端页面传值的方式:
'''第一种''' def reg(request): user_dict = {'name':'shj','password':'123'} return render(request,'reg.html',{'user_dict':user_dict}) # templates自动去找html文件 '''第二种''' def reg(request): user_dict = {'name':'shj','password':'123'} return render(request,'reg.html',locals()) # locals()会把作用域内所有变量传到前端,虽然方便,但一定慎用
模板语言: 渲染变量:{{ }} 渲染标签: {% %}
3、redirect:重定向------接收一个URL参数,跳转到指定的URL。
def login(request): return redirect('http://www.xiaohuar.com')