基础
# HTTP响应状态码 10X:服务端已经接受到你的数据了 你可以继续提交数据进行下一步操作 20X:请求成功(200) 30X:重定向(301,302) 40X:请求错误(404) 50X:服务端错误(500) # GET请求与POST请求 GET请求:获取页面资源 POST请求:朝服务端发送数据(让服务端校验该数据)
一、Web框架本质
所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端
根据不同的路径返回不同的内容
可以写几个简单页面,然后通过http://127.0.0.1:8080/页面名称 来访问对应的页面测试
import socket server = socket.socket() # 默认是TCP协议 server.bind(('127.0.0.1',8080)) # 绑定IP端口 server.listen(5) # 监听 半链接池:保护计算机安全 while True: conn, addr = server.accept() # 等待连接 data = conn.recv(1024) # 接收客户端信息 # 遵循HTTP协议,给回应的消息加上响应状态行 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') res = data.decode('utf-8') current_path = res.split('\r\n')[0].split(' ')[1] # 字符串切割,获取路径 # print(current_path) # 根据不同的路径返回不同的内容 if current_path == '/index': # conn.send(b'index') with open('templates/111.html','rb') as f: conn.send(f.read()) elif current_path == '/login': conn.send(b'login') else: conn.send(b'404') conn.close()
浏览器访问页面请求信息:
HTTP协议主要规定了客户端和服务器之间的通信格式
响应相关信息可以在浏览器调试窗口的network标签页中看到。可以根据view信息切割获得需要请求的页面
请求首行 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.142 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 Cookie: csrftoken=3vPenhmlRQb8Tvl4okwYM0OZpDCl3P7rbxvfpRDOHJy1zUApw89ugxM6OZSxhIBM\r\n \r\n 请求体 '
http://127.0.0.1:8080/index
view展开:
根据不同的路径返回不同页面请求---函数版
import socket sk = socket.socket() sk.bind(('127.0.0.1',8001)) sk.listen() # 将不同的内容部分封装成函数 def index(url): # 读取index.html页面内容 with open("index.html",'rb',encoding="utf-8") as f: s = f.read() # 返回字节数据 return bytes(s,encoding="utf-8") def login(url): with open("login.html",'rb',encoding="utf-8") as f: s = f.read() return bytes(s, encoding="utf-8") list1= [ ("templates/111.html","/index"), ("templates/login.html",'/login'), ] while True: # 等待连接 conn,add = sk.accept() data = conn.recv(1024) # 接收客户端发来的消息 # 从data 中获取路径 data = str(data,encoding='utf-8') # 把收到的字节类型数据转换成字符串 print("data>>>",data) # 按\r\n切割,url是从浏览器发过来的消息中分离出来的访问路径 url = data.split("\r\n")[0].split(' ')[1] print("url>>>>",url) conn.send(b'HTTP/1.1 200 OK\r\n\r\n') #遵循http协议,所以回复的消息也要加状态行 # 根据不同的路径返回不同内容,reponse是具体的响应体 func = None for i in list1: if i[0].split("/")[1] == url: func = i[1] break # print("func>>>",func) if func: reponse = func(url) else: reponse = b"404 not found!" conn.send(reponse) conn.close() # 还有点问题//TODO
二、服务器程序和应用程序
对于真实开发中的python web程序来说,一般会分为两部分:服务器程序和应用程序。
服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。
应用程序则负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py等。不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。
WSGI(Web Server Gateway Interface)就是一种规范,它定义了使用Python编写的web应用程序与web服务器程序之间的接口格式,实现web应用程序与web服务器程序间的解耦
常用的WSGI服务器有uwsgi、Gunicorn。而Python标准库提供的独立WSGI服务器叫wsgiref,Django开发环境用的就是这个模块来做服务器。
1.wsgiref 模块
利用wsgiref模块来替换我们自己写的web框架的socket server部分
# 未拆分前代码
# http://127.0.0.1/index访问对应的名称到对应的页面
from wsgiref.simple_server import make_server
def index():
return 'index'
def reg():
return 'res'
def login():
return 'login'
def error():
return '404'
urls = [
('/index',index),
('/reg',reg),
('/login',login),
]
def run(env,reponse):
reponse('200 OK', []) # 固定格式
print(env) # 将http格式的数据处理完毕,形成一个字段给你调用
current_path = env.get('PATH_INFO')
# if current_path == '/index':
# return [b'index']
func = None
for url_tuple in urls:
if current_path == url_tuple[0]:
func = url_tuple[1] # 如果路由匹配上了,就回去对应的函数
break
if func:
res = func()
else:
res = error()
return [res.encode('utf-8')]
if __name__ == '__main__':
server = make_server('127.0.0.1',8080,run) # 一直监听地址,只要有请求,就会给最后的函数或对象调用:run()执行
server.serve_forever()
# urls.py 存放访问链接后的页面 from views import * urls = [ ('/index',index), ('/reg',reg), ('/login',login), ] # views.py 存放访问页面函数 def index(): return 'index' def reg(): return 'res' def login(): return 'login' def error(): return '404' # wsgiref.py存放启动 from wsgiref.simple_server import make_server from urls import urls from views import * def run(env,response): response('200 OK', []) # 固定格式 不需掌握 print(env) # 将http格式的数据处理完毕 形成一个字段给你调用 current_path = env.get('PATH_INFO') func = None for url_tuple in urls: if current_path == url_tuple[0]: func = url_tuple[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(路由与视图函数的映射关系)和views.py(视图函数)中增加对应的链接和函数就可以
2.动静态页面
静态网页:数据是写死的,一直不变
动态网页:数据实时获取的,一直在改变(eg:数据库的数据或当前时间)
2.1获取时间并显示在页面上:
# 在上面拆分代码的基础上 # urls.py增加 urls = [ ('/index',index), ('/reg',reg), ('/login',login), ('/get_time',get_time), ] # 对象视图views.py里增加函数 import time def get_time(): with open('templates/show_time.html','r',encoding='utf-8') as f: data = f.read() current_time = time.strftime('%Y-%m-%d %X') res = data.replace('timekkk',current_time) # 字符串替换 return res 然后运行wsgiref.py就可以看到页面上显示动态的时间了,每次刷新时间都会变化
2.2 给前端传字典并且字典可以取值
# urls.py中新增对应访问路径 from view2 import * urls2 = [ ('/index', index), ('/reg', reg), ('/login', login), ('/get_time', get_time), ('/get_user', get_user), ] # views.py中新增对应函数 from jinja2 import Template def get_user(env): user_dict = {'username': 'simon', 'password': '123'} with open('templates/get_user.html', 'r', encoding='utf-8') as f: data = f.read() tmp = Template(data) # 实例化产生对象 res = tmp.render(data=user_dict) # 将user_dict传递给前端页面,前端页面用过变量名data就能够拿到user_dict字典 return res # html页面body中设置 {#传data这里就写data#} {{ data }} {#获取账号密码#}{{data.username}}
{{data['password']}}
{{data.hobby.0}}
{{data.hobby.1}}
模板渲染(雏形)
后端产生的数据直接传递给前端页面,前端页面获取数据通过模板语法展示
模板语法:
{{}} 获取后端传递的数据,通过变量名(变量名相关的)
{%%} 与逻辑相关的使用这个语法
jinja2模板语法极为接近后端python语法
{{ data }}{{data.username}}
{{data['password']}}
{{data.hobby.0}}
{{data.hobby.1}}
逻辑相关:
{%for user_dict in user_list%}
{%endfor%}
2.3 数据库取值页面显示
#urls2.py from view2 import * urls2 = [ ('/get_data',get_data), ] # view2.py 新增函数 from jinja2 import Template import pymysql def get_data(env): conn = pymysql.connect( host = '127.0.0.1', port = 3306, user = 'root', password = '123', database = 'test', charset = 'utf8', autocommit = True ) cursor = conn.cursor(pymysql.cursors.DictCursor) cursor.execute('select * from userinfo') res = cursor.fetchall() with open('templates/get_data.html','r',encoding='utf-8') as f: data = f.read() tmp = Template(data) res1 = tmp.render(user_list = res) return res1 # simple_server.py模块,和之前的没什么变化 from wsgiref.simple_server import make_server from urls2 import urls2 from view2 import * def run(env,reponse): reponse('200 OK', []) # 固定格式 print(env) # 将http格式的数据处理完毕,形成一个字段给你调用 current_path = env.get('PATH_INFO') # if current_path == '/index': # return [b'index'] func = None for url_tuple in urls2: if current_path == url_tuple[0]: func = url_tuple[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) # 一直监听地址,只要有请求,就会给最后的函数或对象调用:run()执行 server.serve_forever() # get_data.html前端页面代码 # body内容
id | username | password |
---|---|---|
{{user_dict.id}} | {{user_dict.username}} | {{user_dict.password}} |
2.4 web服务渲染流程
根据早上的流程,我们可以划出下面这个图
2.5 web框架
python主流三大框架:
a.socket服务
b:路由与视图函数映射关系
c:模板渲染
django:大而全 类似航空母舰
a.用的别人的 wsgiref 上线之后会换成uwsgi;默认并发1000多,自己可以加nginx之类处理
b.自己写的
c.自己写的
flask:小而精,轻量级框架
a用的别人的 werkzeug
b自己写的
c用的别人 jinja2
tornado:异步非阻塞
三者都是自己写的
三、Django简介
1.Django安装与注意事项
# 主意事项 1.计算机名称不能含有中文 2.一个pycharm窗口就是一个工程(项目) 3.项目文件夹不要有中文 # ps:django版本: django 1.X(现在用的版本是这个1.11.11) # 安装 pip3 install django # 或pycharm直接安装,可以指定版本specify version
# 如果已经安装了高版本需要降低版本:pip3 uninstall django,然后重新指定安装:pip3 install django ===1.11.20 或pycharm指定版本安装 # 查看djiango是否安装成功 命令行界面:django-admin # 命令行创建django项目 django-admin startproject 项目名 ps:创建一个应用面的文件夹,里面有一个跟应用名同名的文件夹和一个manage.py的文件 # 命令行创建应用 django-admin startapp 应用名 # application 一个django项目 可以有多个应用,django是一款开发应用的web框架 django项目就类似是一所大学,而里面的应用就类似于一个个学院 # 命令行启动项目 python manage.py runserver 启动成功后浏览器可以访问:http://127.0.0.1:8000和http://127.0.0.1:8000/admin ps:命令行创建django项目不会自动新建templates文件夹,并且settings.py配置文件(TEMPLATES 列表中DIRS)不会自动写templates文件夹路径,所以都需要手动添加[os.path.join(BASE_DIR,'templates')]
如下图: # pycharm创建 项目:File-->New Project-->Django-->Location(创建项目)-->More Settings创建应用--->Application name:应用名 在pycharm的命令行中创建应用:Tools-->Run manage.py TASK-->startapp 应用名 应用创建后需要再settings.py中注册应用INSTALLED_APPS里添加 启动后可以访问:http://127.0.0.1:8000 # 注意: 1 在django中创建的应用必须去settings文件中注册才能生效,否则django不识别 2 确保不要端口冲突
2.Django项目目录结构
项目名
应用名文件夹
migrations文件夹
数据库迁移记录
admin.py
django admin后台管理相关
models.py
模型类
views.py
视图函数
项目同名文件夹
settings.py
django暴露给用户可配置的配置文件
urls.py
路由与视图函数映射关系
templates
所有的html文件
manage.py
django入口文件
3.Django 必会三板斧
# django小白必会三板斧 # HttpResponse:返回字符串 # views.py def index(request): return HttpResponse("Hello Django index") # render:返回html页面 def login(request): return render(request,'login.html') # redirect:重定向 def home(request): return redirect('https://www.baidu.com') # urls.py配置文件中加对应访问路径 from django.contrib import admin from django.urls import path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path('index/', views.index), path('login/', views.login), path('home/', views.home), ]
4.静态文件配置
# 动态实时监测到前缀的变化 login.html "UTF-8">Title {% load static %} "{% static 'bootstrap-3.3.7/css/bootstrap.min.css' %}" rel="stylesheet"> #用上述方法settings.py中接口前缀随便修改,也不需要修改html中的对应前缀 # settings.py # 接口前缀:要想访问静态资源必须static打头: # STATIC_URL = '/static/' # 新增文件放置路径 STATICFILES_DIRS = [ os.path.join(BASE_DIR,'static'), os.path.join(BASE_DIR,'static1'), os.path.join(BASE_DIR,'static2'), ]
例:登录功能简单实现
# form表单默认是get请求 get请求携带的参数是拼接在url后面的以?开头&链接,默认method方式为Get ps:get请求可以携带参数 但是参数的大小有限制 最大4KB,并且是明文的 http://127.0.0.1:8000/login/?username=simon&password=123 # 如果改为method改为post提交,需要将settings.py中一行注释,否则会报403错误: # 'django.middleware.csrf.CsrfViewMiddleware', # 跨站请求伪造 获取用户输入的框 都必须要有name属性 action参数有三种写法 1.什么都不写 默认往当前页面的url地址提交 2.只写路由后缀(******)
# 数据后端获取 #前端# views.py:获取访问login页面的method以及post提交的数据 def login(request): print(request.method) # 获取当前请求方式 if request.method == 'POST': # 获取post请求提交的数据 print(request.POST) # get请求默认拿列表最后一个值:如-获取到2个username,取值永远拿最后一个元素 username = request.POST.get('username') password = request.POST.get('password') # 还可以在后面增加判断账号密码判断,以后通过数据库来判断传递的账号密码
if username == "simon" and password == "123":
return redirect("http://www.xiaohuar.com")
return "xxxxxxx" hobby = request.POST.getlist('xxx') print(hobby,type(hobby)) print(username,type(username)) print(password, type(password)) return render(request,'login.html') # 结果: POST