web框架本质
我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端。 这样我们就可以自己实现Web框架了。
import socket
server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)
while True:
conn, addr = server.accept()
data = conn.recv(1024)
print(data) # 将浏览器发来的消息打印出来
conn.send(b"OK")
conn.close()
可以说Web服务本质上都是在这十几行代码基础上扩展出来的。
用户的浏览器一输入网址,会给服务端发送数据,那浏览器会发送什么数据?怎么发?这个谁来定? 你这个网站是这个规定,他那个网站按照他那个规定,这互联网还能玩么?
所以,必须有一个统一的规则,让大家发送消息、接收消息的时候有个格式依据,不能随便写。
这个规则就是HTTP协议,以后浏览器发送请求信息也好,服务器回复响应信息也罢,都要按照这个规则来。
HTTP协议主要规定了客户端和服务器之间的通信格式,那HTTP协议是怎么规定消息格式的呢?
让我们首先打印下我们在服务端接收到的消息是什么。
输出结果:
"""
请求首行
b'GET / HTTP/1.1\r\n
请求头
Host: 127.0.0.1:8080\r\n
Connection: keep-alive\r\n
Cache-Control: max-age=0\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/75.0.3770.80 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\r\n
\r\n
请求体
......
"""
要想让我们自己写的web server端正经起来,必须要让我们的Web server在给客户端回复消息的时候按照HTTP协议的规则加上响应状态行,这样我们就实现了一个正经的Web框架了
import socket
server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)
while True:
conn, addr = server.accept()
data = conn.recv(1024)
#给回复的信息加上响应状态行
conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
conn.send(b'hello world')
print(data)
我们通过十几行代码简单地演示了web 框架的本质。
接下来就让我们继续完善我们的自定义web框架吧!
根据不同的路径返回不同的内容
这样就结束了吗? 如何让我们的Web服务根据用户请求的URL不同而返回不同的内容呢?
我们可以从请求相关数据里面拿到请求URL的路径,然后拿路径做一个判断...
import socket
server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)
while True:
conn, addr = server.accept()
data = conn.recv(1024)
conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
data = data.decode('utf-8') # 把从浏览器那里收到的字节类型的数据转换成字符串
# print(data)
target_url = data.split('\r\n')[0].split(' ')[1] #按\r\n分割
# 根据不同的路径返回不同内容
if target_url == '/index':
# conn.send(b'index')
with open(r'D:\demo.html','rb') as f:
conn.send(f.read())
elif target_url == '/login':
conn.send(b'login')
else:
conn.send(b'404 error')
conn.close()
基于wsgiref模块:
基于wsgiref模块以及文件拆分的特点:
若要开设新的资源
1.先在urls文件中写url与函数的对应关系
2.再去views文件中写对应的函数
urls.py:路由与视图函数的对应关系
views.py:里面就是放的一堆视图函数(视图函数可以是函数也可以是类)
templates文件夹:里面放的就是一堆html文件(模板文件夹)
动静态网页
{{ xxx }}
{{xxx.username}}
{{xxx['password']}}
{{xxx.get('hobby')}}
{{xxx.get('hobby')[0]}}
{{xxx.get('hobby').1}}
{%for user_dict in xxx %}
{{ user_dict.id }}
{{ user_dict.name }}
{{ user_dict.hobby }}
{% endfor %}
基于wsgiref模块、jinjia2实现web框架
from views import *
urls = [
('/index',index),
('/login',login),
('/xxx',xxx),
('/get_time',get_time),
('/get_user',get_user),
('/get_info',get_info)
]
def index(env):
return 'index'
def login(env):
return 'login'
def error(env):
return '404 error'
def xxx(env):
return 'xxx'
import time
def get_time(env):
# 该函数需要返回一个html页面
current_time = time.strftime('%Y-%m-%d %X')
# 文件操作 读取html文件
with open(r'F:\python\templates\02 get_time.html','r',encoding='utf-8') as f:
data = f.read() # html文件内容 字符串
data = data.replace('gfdgsffsda',current_time) # 利用字符串的替换
return data
from jinja2 import Template
def get_user(env):
user_dict = {'username':'jason','password':123,'hobby':['read','study','run']}
with open(r'F:\python\templates\04 get_info.html','r',encoding='utf-8') as f:
data = f.read()
temp = Template(data)
res = temp.render(xxx=user_dict) # 将user_dict传递给html页面 在页面上通过变量名xxx就能够获取到user_dict
return res
import pymysql
def get_info(env):
conn = pymysql.connect(
host = '127.0.0.1',
port = 3306,
user = 'root',
password = '123',
database = 'day49',
charset = 'utf8',
autocommit = True
)
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
sql = "select * from userinfo"
cursor.execute(sql)
data = cursor.fetchall() # [{},{},{}]
# 将列表套字典的结构数据 直接传递给html页面
with open(r'D:get_info.html','r',encoding='utf-8') as f:
res = f.read()
# 利用jinja2模块
tmp = Template(res)
# 利用对象的render方法 将数据直接传递给html页面
res = tmp.render(xxx=data)
return res
from wsgiref.simple_server import make_server
from views import *
from urls import urls
def run(env,response):
"""
:param env: 请求相关的所有数据
:param response: 响应相关的所有数据
:return: 浏览器能够接受的内容
"""
response('200 OK',[])
# print(env) # env是一个大字典 里面的PATH_INFO参数就是用户输入的后缀
target_url = env.get('PATH_INFO')
# if target_url == '/index':
# # 一堆逻辑判断
# return [b'index']
# elif target_url == '/login':
# return [b'login']
# 先定义一个变量 用来存储可能匹配到的函数名
func = None
# 1 for循环获取一个个的url与函数对应关系的元组
for url in urls: # url = (),(),()
# 2 判断当前用户访问的url与元组第一个元素是否一致
if target_url == url[0]:
# 3 如果相等 说明有对应的后端逻辑代码 将匹配到的函数名赋值给func
func = url[1]
# 4 一旦用户匹配上了响应的url 应该立刻结束当前for循环了 因为再循环就没有意义
break
# 针对func是否有值 还需要判断
if func:
# 匹配上了 加括号直接调用
res = func(env)
else:
# 匹配404逻辑代码
res = error(env)
return [res.encode('utf-8')] # 返回函数的返回值
if __name__ == '__main__':
# 监听127.0.0.1:8080 一旦有客户端来访问 会立刻将make_server第三个参数加括号调用执行
server = make_server('127.0.0.1',8080,run)
server.serve_forever() # 启动服务端
web框架简介
python三大主流web框架
Django 优势:
Tornado 异步非阻塞,天然支持高并发 甚至可以用它来开发游戏服务器
Django框架
Django版本:
推荐使用1.X版本里面的1.11.09~1.11.13
安装
pip3 install django==1.11.11
#测试是否安装成功
命令行输入 django-admin
创建django项目的两种方式
1.使用命令行创建
创建django项目:
django-admin startproject mysite(项目名)
效果:创建了一个mysite的文件夹
目录介绍
mysite/
├── manage.py # 管理文件
└── mysite # 项目目录
├── __init__.py
├── settings.py # 配置
├── urls.py # 路由 --> URL和函数的对应关系
└── wsgi.py # runserver命令就使用wsgiref模块做简单的web server
启动Django项目:
python manage.py runserver # django默认的端口号是8000
Django 若启动报错“SyntaxError: Generator expression must be parenthesized”
报这个错很大可能是因为使用了Python3.7.0,而目前(2018-06-12)Python3.7.0和Django还有点兼容性问题。解决方案如下
Django 启动时报错 “UnicodeEncodeError ...”
报这个错误通常是因为计算机名为中文,改成英文的计算机名重启下电脑就可以了。
创建具有独立功能的app
python manage.py startapp app01 #通常情况下应该做到见名知意
app01
-- migrations 文件夹
-- __init__.py
-- admin.py
-- apps.py
-- models.py
-- tests.py
-- views.py
app(application)的概念
django其实是一个专注于开发app的web框架,一个空的django项目就类似于是一所大学,app就类似于大学里面的各个学院。
每个app其实就类似于不同的功能模块
购物网站
用户相关 user
用户相关的app
订单相关 order
订单相关的app
投诉相关 tousu
投诉相关的app
不同的功能模块推荐使用不同的app去开发,django支持多app
Django主要文件功能
mysite -mysite --__init__.py --settings.py 项目配置文件 --urls.py 路由视图函数对应关系 项目的总路由 --wsgi.py -manage.py
app01 --migrations文件夹 数据库改动记录 --__init__.py --__init__.py --admin.py django后台管理 --apps.py 注册app相关 --models.py 模型类(ORM) --tests.py 测试文件 --views.py 视图函数(******)
db.sqlite3 django自带的一个小型用于本地测试的数据库(对日期格式的数据不是很敏感)
2.使用pycharm创建
注意:1.使用命令行创建的django项目是不会自动创建templates模板文件夹,只能自己手动创建
2.命令行创建的django项目不但没有templates文件夹,配置文件中也没有填写路径,而pycharm创建的会自动添加
容易犯的错误:代码修改了始终没有效果
1.在同一个端口起了多个服务 一直跑的是最开始的那个服务
2.浏览器缓存问题
解决浏览器缓存如下图
补充:简化命令的方式
项目配置文件夹注意事项:(注册)
注意:创建app之后一定一定要先去setting文件中注册
用pycharm创建的Django项目的配置文件
用命令行创建的Django项目的配置文件
Django基础必备三件套
HttpResponse:返回字符串
render:返回html页面 并可以给html页面传数据
模板的渲染(将数据在后端按照模板语法放入html对应的位置)
redirect:重定向
from django.shortcuts import render,HttpResponse,redirect
# Create your views here.
def index(request):
return HttpResponse('字符串')
def login(request):
return render(request,'templates文件夹下的html文件名',{'user_dict':{'username':'jason','password':123},'userxxx':'hello world'}))
def home(request):
# return redirect('http://www.baidu.com')
return redirect('/index')