咱们能够这样理解:全部的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端。 这样咱们就能够本身实现Web框架了。css
先写一个html
import socket sk = socket.socket() sk.bind(('127.0.0.1', 8080)) sk.listen(5) while True: conn, addr = sk.accept() data = conn.recv(1024) print(data) # 打印浏览器发过来的消息并分析 conn.send(b'ok') conn.close()
能够说Web服务本质上都是在这几行代码基础上扩展出来的。这段代码就是它们的祖宗。前端
用户的浏览器一输入网址,会给服务端发送数据,那浏览器会发送什么数据?怎么发?这个谁来定? 你这个网站是这个规定,他那个网站按照他那个规定,这互联网还能玩么?python
因此,必须有一个统一的规则,让你们发送消息、接收消息的时候有个格式依据,不能随便写。mysql
这个规则就是HTTP协议,之后浏览器发送请求信息也好,服务器回复响应信息也罢,都要按照这个规则来。jquery
HTTP协议主要规定了客户端和服务器之间的通讯格式,那HTTP协议是怎么规定消息格式的呢?web
运行上面的代码,在浏览器输入服务器的地址和端口sql
获得浏览器发过来的data的打印结果:chrome
# data结果 ''' 1.请求首行: b'GET / HTTP/1.1\r\n 2.请求体:一大堆K:V键值对 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 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36 chrome-extension\r\n Sec-Fetch-Mode: navigate\r\n Sec-Fetch-User: ?1\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 Sec-Fetch-Site: cross-site\r\n Accept-Encoding: gzip, deflate, br\r\n Accept-Language: zh-CN,zh;q=0.9,en-GB;q=0.8,en;q=0.7\r\n \r\n 3.请求体: 这里是请求数据,get请求没有,post请求才有 '''
咱们发现收发的消息须要按照必定的格式来,数据库
1.数据格式
get请求格式
请求首行(请求方式,协议版本。。。)
请求头(一大堆k:v键值对)
\r\n
请求体(真正的数据 发post请求的时候才有 若是是get请求不会有)
响应格式
响应首行
响应头
\r\n
响应体
HTTP GET请求的格式:
HTTP响应的格式:
这里就须要了解一下HTTP协议了。
HTTP协议对收发消息的格式要求
每一个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的。 HTTP响应的Header中有一个 Content-Type
代表响应的内容格式。如 text/html
表示HTML网页。
HTTP协议特色:
超文本传输协议
1.四大特性
1.基于TCP/IP之上做用于应用层
2.基于请求响应
3.无状态 cookie session token...
4.无链接
2.响应状态码
用特定的数字表示一些意思
1XX:服务端已经成功接收到了你的数据 正在处理 你能够继续提交其余数据
2XX:服务端成功响应(200请求成功)
3XX:重定向
4XX:请求错误(404 请求资源不存在 403 拒绝访问)
5XX:服务器内部错误(500 )
若是咱们想要本身写的web server服务端真正运行起来,达到一种请求与响应的对应关系,咱们必需要在sercer服务端给客户端回复消息的时候,按照HTTP协议的规则加上响应状态行 ,就是 协议版本+状态码+状态描述符:b'HTTP/1.1 200 OK\r\n\r\n'
以下例子:
import socket sk = socket.socket() sk.bind(('127.0.0.1', 8080)) sk.listen(5) while True: conn, addr = sk.accept() data = conn.recv(1024) print(data) # 须要向客服端发送响应头,客户端才能正常显示信息 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') conn.send(b'hello world') conn.close()
若是咱们在浏览器客户端输入:http://127.0.0.1:8080/home
,浏览器的页面显示就为home,那么能够这样作:
import socket sk = socket.socket() sk.bind(('127.0.0.1', 8080)) # 绑定IP和端口 sk.listen(5) # 监听 while True: # 等待链接 conn, addr = sk.accept() # 接收客户端返回的信息 data = conn.recv(1024) # print(data) # 从data中取到路径 并将收到的字节类型的数据转换成字符串 # data = str(data,encoding='utf8') data = data.decode('utf8') # 按\r\n分割 data1 = data.split('\r\n')[0] # print(data1) # 请求首行的进行切割拿到url url是咱们从浏览器发来的消息分离出来的访问路径 url=data1.split(' ')[1] # print(url) # 必须遵循HTTP协议,须要向客服端发送状态行,客户端才能正常显示信息 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 根据不一样的路径返回不一样内容 if url == '/index': response = b'index' elif url == '/home': response = b'home' else: response = b'404 not found!!!' conn.send(response) conn.close()
import socket sk = socket.socket() sk.bind(('127.0.0.1', 8080)) # 绑定IP和端口 sk.listen(5) # 监听 # 将返回不一样的内容部分封装成函数 def index(url): res = f'这是{url}页面|' return bytes(res,encoding='gbk') # return res.encode('utf-8') def home(url): res = f'这是{url}页面|' return bytes(res,encoding='gbk') # return res.encode('utf-8') while True: # 等待链接 conn, addr = sk.accept() # 接收客户端返回的信息 data = conn.recv(1024) # print(data) # 从data中取到路径 并将收到的字节类型的数据转换成字符串 # data = str(data,encoding='utf8') data = data.decode('utf8') # 按\r\n分割 data1 = data.split('\r\n')[0] # print(data1) # 请求首行的进行切割拿到url url是咱们从浏览器发来的消息分离出来的访问路径 url=data1.split(' ')[1] # print(url) # 根据不一样的路径返回不一样内容 if url == '/index': response = index(url) elif url == '/home': response = home(url) else: response = b'404 not found!!!' # 必须遵循HTTP协议,须要向客服端发送状态行,客户端才能正常显示信息 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') conn.send(response) conn.close()
import socket sk = socket.socket() sk.bind(('127.0.0.1', 8080)) # 绑定IP和端口 sk.listen(5) # 监听 # 定义一个url和实际要执行的函数的对应关系 def home(url): res = bytes(url, encoding='utf8') return res def index(url): res = bytes(url, encoding='utf8') return res # 定义一个url和要执行函数对应关系的字典 dt = { '/index':index, '/home':home } while True: # 等待链接 conn, addr = sk.accept() # 接收客户端返回的信息 data = conn.recv(1024) # print(data) # 从data中取到路径 并将收到的字节类型的数据转换成字符串 # data = str(data,encoding='utf8') data = data.decode('utf8') # 按\r\n分割 data1 = data.split('\r\n')[0] # print(data1) # 请求首行的进行切割拿到url url是咱们从浏览器发来的消息分离出来的访问路径 url=data1.split(' ')[1] # print(url) func = None # 根据不一样的路径返回不一样内容 for k,v in dt.items(): print(k,v) if url == k: func = v break if func: response = func(url) else: response = b'404 not found!!!' # 必须遵循HTTP协议,须要向客服端发送状态行,客户端才能正常显示信息 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') conn.send(response) conn.close()
首先建立咱们须要的html页面,而后把在代码里面以rb模式读取出来,发送到浏览器
import socket sk = socket.socket() sk.bind(('127.0.0.1', 8080)) # 绑定IP和端口 sk.listen(5) # 监听 def home(url): with open('home页面.html','rb') as fr: res = fr.read() return res def index(url): with open('index页面.html', 'rb') as fr: res = fr.read() return res # 定义一个url和实际要执行的函数的对应关系 dt = { '/index':'index', '/home':'home' } while True: # 等待链接 conn, addr = sk.accept() # 接收客户端返回的信息 data = conn.recv(1024) # print(data) # 从data中取到路径 并将收到的字节类型的数据转换成字符串 # data = str(data,encoding='utf8') data = data.decode('utf8') # 按\r\n分割 data1 = data.split('\r\n')[0] # print(data1) # 请求首行的进行切割拿到url url是咱们从浏览器发来的消息分离出来的访问路径 url=data1.split(' ')[1] # print(url) func = None # 根据不一样的路径返回不一样内容 for k,v in dt.items(): print(k,v) if url == k: func = v break if func: response = func(url) else: response = b'404 not found!!!' # 必须遵循HTTP协议,须要向客服端发送状态行,客户端才能正常显示信息 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') conn.send(response) conn.close()
上面的网页是不会变化的,如何实现获得一个动态的网站呢?下面作个例子:每次刷新都在获取新的时间,模拟动态的数据
import socket import datetime sk = socket.socket() sk.bind(('127.0.0.1', 8080)) # 绑定IP和端口 sk.listen(5) # 监听 def home(url): with open('get_time.html', 'r',encoding='utf8') as fr: res = fr.read() now = datetime.datetime.now().strftime("%Y-%m-%d %X") # 在网页中定义好特殊符号,用动态的数据替换特殊字符 res = res.replace('*time*',now).encode('utf8') return res def index(url): with open('index页面.html', 'rb') as fr: res = fr.read() return res # 定义一个url和实际要执行的函数的对应关系 dt = { '/index': index, '/home': home } while True: # 等待链接 conn, addr = sk.accept() # 接收客户端返回的信息 data = conn.recv(1024) # print(data) # 从data中取到路径 并将收到的字节类型的数据转换成字符串 # data = str(data,encoding='utf8') data = data.decode('utf8') # 按\r\n分割 data1 = data.split('\r\n')[0] # print(data1) # 请求首行的进行切割拿到url url是咱们从浏览器发来的消息分离出来的访问路径 url=data1.split(' ')[1] # print(url) func = None # 根据不一样的路径返回不一样内容 for k,v in dt.items(): print(k,v) if url == k: func = v break if func: response = func(url) else: response = b'404 not found!!!' # 必须遵循HTTP协议,须要向客服端发送状态行,客户端才能正常显示信息 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') conn.send(response) conn.close()
对于真实开发中的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开发环境用的就是这个模块来作服务器
from wsgiref.simple_server import make_server def run(environ,response): # 当客户发送请求过来时,会先调用wsgi内部接口,而后调用run函数,而且携带了两个参数给run函数 # environ:一个包含全部HTTP请求信息的dict对象; # response:一个发送HTTP响应的函数。 # 向客户端发送的状态码和头信息 response('200 OK',[('content-type','text/html; charset=utf-8'),]) # 返回的是一个列表,内容是发送给客户端展现的内容 return ['hello world'.encode('utf-8')] if __name__ == '__main__': # 至关于socket绑定ip和端口 server = make_server('127.0.0.1',8080,run) # 实时监听地址,等待客户端链接,有链接来了就调用run函数 server.serve_forever()
from wsgiref.simple_server import make_server import datetime def index(url): with open('index页面.html', 'rb') as fr: data = fr.read() return data def home(url): with open('home页面.html', 'rb') as fr: data = fr.read() return data def get_time(url): now = datetime.datetime.now().strftime('%Y-%m-%d %X') with open('get_time.html','r',encoding='utf-8') as fr: data = fr.read() data = data.replace('*time*',now) return data.encode('utf-8') dic={ '/index':index, '/home':home, '/get_time':get_time } def run(env,response): # 发送状态码和头信息到客户端 response('200 ok',[('content-type','text/html;charset=utf-8'),]) # print(env) # 由于env就是客户端发过来的请求信息(k:v键值对形式), # 经过打印信息得出PATH_INFO就是请求的url, url = env.get('PATH_INFO') print(url) func = None if url in dic: func = dic.get(url) if func: res = func(url) else: res = b'404 not found!!!' return [res] if __name__ == '__main__': server = make_server('127.0.0.1',8080,run) server.serve_forever()
jinja2模块,跟上面的用特殊符号去替换须要展现的内容的原理是同样的,jinja2他将html页面封装成一个能够渲染数据的模板,而后获得咱们真正想要返回给浏览器的html页面。
1.建立一张user表:
2.建立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.3.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.3.1/js/bootstrap.min.js"></script> </head> <body> <div class="container"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <h1 class="text-center">用户列表</h1> <table class="table table-bordered table-striped table-hover"> <thead> <tr> <th>id</th> <th>name</th> <th>pwd</th> </tr> </thead> <tbody> <!--user_list是渲染的数据 --> {% for user_dict in user_list %} <tr> <td>{{ user_dict.id }}</td> <td>{{ user_dict.name }}</td> <td>{{ user_dict.hobby}}</td> </tr> {% endfor %} </tbody> </table> </div> </div> </div> </body> </html>
3.使用jinja2渲染html文件。
from wsgiref.simple_server import make_server from jinja2 import Template import pymysql # 从数据库中获取,并使用jinja2将数据渲染到html def get_db(url): conn = pymysql.connect( host='127.0.0.1', port=3306, user='tomjoy', password='123456', database='user_info', charset='utf8', autocommit=True ) cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) sql = "select * from user" cursor.execute(sql) # 1.从数据库中获取数据 res = cursor.fetchall() with open('get_db.html','r',encoding='utf8') as fr: data = fr.read() # 2.生成html渲染模板对象 temp = Template(data) # 3.将数据库中获取回来的数据,传到html模板对象进行渲染, # 返回一个咱们真正想要展现的html页面 ret = temp.render(user_list=res) return ret.encode('utf8') dic = { '/get_db' : get_db } def run(env,response): response('200 ok',[('content-type','text/html;charset=utf-8'),]) func = None url = env.get('PATH_INFO') if url in dic: func = dic.get(url) if func: res = func(url) else: res = b'404 not found!!!' return [res] if __name__ == '__main__': server = make_server('127.0.0.1',8080,run) server.serve_forever()
jinja2模板语法(极其贴近python后端语法)
<p>{{ user }}</p> <p>{{ user.name }}</p> <p>{{ user['pwd'] }}</p> <p>{{ user.get('hobby') }}</p> {% for user_dict in user_list %} <tr> <td>{{ user_dict.id }}</td> <td>{{ user_dict.name }}</td> <td>{{ user_dict.pwd }}</td> </tr> {% endfor %}
模板渲染:利用模板语法 实现后端传递数据给前端html页面
模板语法书写格式:
变量相关:{{}}
逻辑相关:{%%}
注意:Django的模板语法因为是本身封装好的,只支持 点.取值
注:模板渲染的原理就是字符串替换,咱们只要在HTML页面中遵循jinja2的语法规则写上,其内部就会按照指定的语法进行相应的替换,从而达到动态的返回内容。
效果以下:
1.安装django
pip3 install django==1.11.11
2.建立django项目
在cmd命令行下建立一个名为mysite的Django项目
django-admin startproject mysite
3.目录介绍
mysite ├── manage.py # Django入口管理文件 └── templates # 存放html文件 └── mysite # 项目目录 ├── __init__.py ├── settings.py # 配置 ├── urls.py # 路由 --> URL和函数的对应关系 └── wsgi.py # runserver命令就使用wsgiref模块作简单的web server
4.模板文件配置
使用命令行建立django项目 不会自动帮你建立templates文件夹, 只能本身建立
在.settings文件中 须要你手动在TEMPLATES的DIRS写配置
[os.path.join(BASE_DIR, 'templates')]
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], # templates 文件夹位置 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
5.启动django项目
python manage.py runserver
6.建立应用app01
python manage.py startapp app01
7.app应用目录:
└── app01 # 项目目录 ├── migrations文件夹 # 存放数据库迁移记录 ├── __init__.py ├── admin.py # django后台管理 └── apps.py # 注册相关 └── models.py # 模型类 └── tests.py # 测试文件 └── views.py # 存放视图函数
注意:若是是在命令行下建立app后,须要你去settings配置文件中注册添加app名字。这样django项目才能识别到你这个app
8.静态文件配置:
什么是静态文件?
静态文件就是在打开网页时所用到的 图片、 js、css以及第三方的框架bootstrap、fontawesome、sweetalert
一般状况下 网站所用到的静态文件资源 统一都放在static文件夹下,为了方便识别
STATIC_URL = '/static/' # 是访问静态资源的接口前缀,并非存放静态文件的文件夹 """只要你想访问静态资源 你就必须以static开头""" # 手动在settings最底下添加配置静态文件访问资源 # 下面都是存放静态文件的文件夹的路径 # 从上往下找静态文件,找不到就报错 STATICFILES_DIRS = [ os.path.join(BASE_DIR,'static'), os.path.join(BASE_DIR,'static1'), os.path.join(BASE_DIR,'static2'), ]
图解:
9.禁用中间件:
前期为了方便表单提交测试。在settings配置文件中暂时禁用csrf中间件
10.重定向:
重定向的意思就是,我访问的连接不是我刚刚输入的那个连接,而是我一输入他就跳转到了另一个连接,这就是重定向
最后注意事项:
1.计算机的名称不能有中文
2.一个pycharm窗口就是一个项目
3.项目名里面尽可能不要用中文
django版本问题
1.X 2.X 如今市面上用的比较多的仍是1.X
推荐使用1.11.9~1.11.13
django安装
pip3 install django==1.11.11
如何验证django是否安装成功
命令行直接敲django-admin
一个django项目就相似因而一所大学,而app就相似于大学里面的学院
django其实就是用来一个个应用的
一个app就至关于一块独立的功能
用户功能
管理功能
.........
django支持任意多个app