12.1 HTTP协议
12.1.1 HTTP简介
- 超文本传输协议 Hyper Text Transfer Protocol
- 是一种用于分布式、协作式和超媒体信息系统的应用层协议
- HTTP是万维网的数据通信的基础
- HTTP有很多应用,但最著名的是用于web浏览器和web服务器之间的双工通信
- HTTP是一个客户端终端和服务器端请求和响应的标准
12.1.2 HTTP 请求/响应的步骤
- 客户端连接到Web服务器
- 一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接
- 发送HTTP请求
- 通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求首行(\r\n分隔)、请求头部、空行(\r\n\r\n)和请求数据4部分组成
- 服务器接受请求并返回HTTP响应
- Web服务器解析请求,定位请求资源,服务器将资源复本写到TCP套接字,由客户端读取,一个响应由状态行、响应头部、空行和响应数据4部分组成
- 释放连接TCP连接
- 若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求
- 客户端浏览器解析HTML内容
- 客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码,然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集,客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示
- 面试题:在浏览器地址栏键入URL,按下回车之后会经历的流程:
- 浏览器向DNS服务器请求解析该URL中的域名所对应的IP地址
- 解析出IP地址后,根据该IP地址和默认端口80,和服务器建立TCP连接
- 浏览器发出读取文件(URL 中域名后面部分对应的文件)的HTTP请求,该请求报文作为TCP三次握手的第三个报文的数据发送给服务器
- 服务器对浏览器请求作出响应,并把对应的html文本发送给浏览器
- 释放TCP连接
- 浏览器将该html文本并显示内容
12.1.3 HTTP请求方法
- GET:获取一个页面、图片(资源)
- POST:提交数据
- HEAD
- PUT
- DELETE
- TRACE
- OPTIONS
- CONNECT
请求方式: get与post请求
- GET提交的数据会放在URL之后,以?分割URL和传输数据,参数之间以&相连,如EditBook?name=test1&id=123456. POST方法是把提交的数据放在HTTP包的Body中.
- GET提交的数据大小有限制(因为浏览器对URL的长度有限制),而POST方法提交的数据没有限制.
- GET与POST请求在服务端获取请求数据方式不同。
- GET方式提交数据,会带来安全问题,比如一个登录页面,通过GET方式提交数据时,用户名和密码将出现在URL上,如果页面可以被缓存或者其他人可以访问这台机器,就可以从历史记录获得该用户的账号和密码.
12.1.4 HTTP状态码
1.状态代码的第一个数字代表当前响应的类型:
12.1.5 URL:统一资源定位符
- URL包含的信息:
- 传送协议
- 层级URL标记符号(为 // ,固定不变)
- 访问资源需要的凭证信息(可省略)
- 服务器(通常为域名,有时为IP地址)
- 端口号(以数字方式表示,可省略,HTTP的默认值为80,HTTPS的默认值为443)
- 路径(以 / 字符区别路径中的每一个目录名称)
- 查询(GET模式的窗体参数,以 ? 字符为起点,每个参数以 & 隔开,再以 = 分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题)
- 片段(以“#”字符为起点)
- 示例:
http://www.luffycity.com:80/news/index.html?id=250&page=1
- http,是传送协议
www.luffycity.com
,是服务器- 80,是服务器上的网络端口号
- /news/index.html,是路径
- ?id=250&page=1,是查询
12.2 Web框架
12.2.1 Web框架本质
- 所有的web应用本质是就是一个socket服务器,而用户的游览器就是一个socket客户端
12.2.2 Web框架功能
- socket收发消息 —— wsgiref(测试)、uwsgi(线上)
- 根据不同的路径返回不同的字符串
- 返回动态页面(字符串的替换)—— jinja2
12.2.3 Web框架种类
- django
- 根据不同的路径返回不同的字符串
- 返回动态页面(字符串的替换)
- flask
- 根据不同的路径返回不同的字符串
- tornado
- socket收发消息
- 根据不同的路径返回不同的字符串
- 返回动态页面(字符串的替换)
12.2.4 自定义web框架
示例一:socket服务端
import socket # 创建一个socket对象 sk = socket.socket() # 绑定IP和端口 sk.bind(('127.0.0.1', 8000)) # 监听 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
ok!
') # 断开连接 conn.close()示例二:根据不同路径返回不同的内容(普通版)
import socket # 创建一个socket对象 sk = socket.socket() # 绑定IP和端口 sk.bind(('127.0.0.1', 8000)) # 监听 sk.listen(5) # 等待连接 while True: conn, addr = sk.accept() # 接收数据 data = conn.recv(1024) data = data.decode('utf-8') url = data.split()[1] conn.send(b'HTTP/1.1 200 OK\r\n\r\n') if url == '/index/': # 返回数据 conn.send(b'
index!
') elif url == '/home/': conn.send(b'home!
') else: conn.send(b'404 not found!
') # 断开连接 conn.close()示例三:根据不同路径返回不同的内容(函数版)
import socket # 创建一个socket对象 sk = socket.socket() # 绑定IP和端口 sk.bind(('127.0.0.1', 8000)) # 监听 sk.listen(5) # 函数 def index(url): ret = '
index!
({})'.format(url) return ret.encode('utf-8') def home(url): ret = 'home!
({})'.format(url) return ret.encode('utf-8') # 等待连接 while True: conn, addr = sk.accept() # 接收数据 data = conn.recv(1024) data = data.decode('utf-8') url = data.split()[1] conn.send(b'HTTP/1.1 200 OK\r\n\r\n') if url == '/index/': # 返回数据 ret = index(url) elif url == '/home/': ret = home(url) else: ret = b'404 not found!
' conn.send(ret) # 断开连接 conn.close()示例四:根据不同路径返回不同的内容(函数进阶版)
import socket # 创建一个socket对象 sk = socket.socket() # 绑定IP和端口 sk.bind(('127.0.0.1', 8000)) # 监听 sk.listen(5) # 函数 def index(url): ret = '
index!
({})'.format(url) return ret.encode('utf-8') def home(url): ret = 'home!
({})'.format(url) return ret.encode('utf-8') # 定义一个list1和实际要执行的函数的对应关系 list1 = [ ('/index/', index), ('/home/', home), ] # 等待连接 while True: conn, addr = sk.accept() # 接收数据 data = conn.recv(1024) data = data.decode('utf-8') url = data.split()[1] conn.send(b'HTTP/1.1 200 OK\r\n\r\n') func = None for i in list1: if url == i[0]: func = i[1] break if func: ret = func(url) else: ret = b'404 not found!
' conn.send(ret) # 断开连接 conn.close()示例五:返回HTML页面
import socket # 创建一个socket对象 sk = socket.socket() # 绑定IP和端口 sk.bind(('127.0.0.1', 8000)) # 监听 sk.listen(5) # 函数 def index(url): with open('index.html','rb') as f: ret = f.read() return ret def home(url): ret = '
home!
({})'.format(url) return ret.encode('utf-8') # 定义一个list1和实际要执行的函数的对应关系 list1 = [ ('/index/', index), ('/home/', home), ] # 等待连接 while True: conn, addr = sk.accept() # 接收数据 data = conn.recv(1024) data = data.decode('utf-8') url = data.split()[1] conn.send(b'HTTP/1.1 200 OK\r\n\r\n') func = None for i in list1: if url == i[0]: func = i[1] break if func: ret = func(url) else: ret = b'404 not found!
' conn.send(ret) # 断开连接 conn.close()示例六:返回动态页面
import socket import time # 创建一个socket对象 sk = socket.socket() # 绑定IP和端口 sk.bind(('127.0.0.1', 8000)) # 监听 sk.listen(5) # 函数 def index(url): with open('index.html', 'rb') as f: ret = f.read() return ret def home(url): ret = '
home!
({})'.format(url) return ret.encode('utf-8') def timer(url): now = time.strftime('%H:%M:%S') with open('time.html','r',encoding='utf-8') as f: data = f.read() data = data.replace('xxtimexx',now) return data.encode('utf-8') # 定义一个list1和实际要执行的函数的对应关系 list1 = [ ('/index/', index), ('/home/', home), ('/time/', timer), ] # 等待连接 while True: conn, addr = sk.accept() # 接收数据 data = conn.recv(1024) data = data.decode('utf-8') url = data.split()[1] conn.send(b'HTTP/1.1 200 OK\r\n\r\n') func = None for i in list1: if url == i[0]: func = i[1] break if func: ret = func(url) else: ret = b'404 not found!
' conn.send(ret) # 断开连接 conn.close()补充:time.html
Title 当前时间是:@@time@@
12.2.5 wsgiref
常用的WSGI服务器有uWSGI、Gunicorn
- Python标准库提供的独立WSGI服务器叫wsgiref,Django开发环境用的就是这个模块来做服务器
示例:
""" 根据URL中不同的路径返回不同的内容--函数进阶版 返回HTML页面 让网页动态起来 wsgiref模块版 """ from wsgiref.simple_server import make_server # 将返回不同的内容部分封装成函数 def index(url): # 读取index.html页面的内容 with open("index.html", "r", encoding="utf8") as f: s = f.read() # 返回字节数据 return bytes(s, encoding="utf8") def home(url): with open("home.html", "r", encoding="utf8") as f: s = f.read() return bytes(s, encoding="utf8") def timer(url): import time with open("time.html", "r", encoding="utf8") as f: s = f.read() s = s.replace('@@time@@', time.strftime("%Y-%m-%d %H:%M:%S")) return bytes(s, encoding="utf8") # 定义一个url和实际要执行的函数的对应关系 list1 = [ ("/index/", index), ("/home/", home), ("/time/", timer), ] 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 list1: if i[0] == url: func = i[1] break if func: response = func(url) else: response = b"404 not found!" return [response, ] if __name__ == '__main__': httpd = make_server('127.0.0.1', 8090, run_server) print("我在8090等你哦...") httpd.serve_forever()
12.2.6 jinja2
模板渲染现成的工具:jinja2
- 下载jinja2:pip install jinja2
示例:
from wsgiref.simple_server import make_server from jinja2 import Template def index(url): # 读取HTML文件内容 with open("index2.html", "r", encoding="utf8") as f: data = f.read() template = Template(data) # 生成模板文件 ret = template.render({'name': 'alex', 'hobby_list': ['抽烟', '喝酒', '烫头']}) # 把数据填充到模板中 return bytes(ret, encoding="utf8") def home(url): with open("home.html", "r", encoding="utf8") as f: s = f.read() return bytes(s, encoding="utf8") # 定义一个url和实际要执行的函数的对应关系 list1 = [ ("/index/", index), ("/home/", home), ] 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 list1: if i[0] == url: func = i[1] break if func: response = func(url) else: response = b"404 not found!" return [response, ] if __name__ == '__main__': httpd = make_server('127.0.0.1', 8090, run_server) print("我在8090等你哦...") httpd.serve_forever()
补充:index2.html
Title 姓名:{{name}}
爱好:
-
{% for hobby in hobby_list %}
- {{hobby}} {% endfor %}
12.3 Django基本知识
12.3.1 安装及使用
下载安装
- 命令行:pip3 install django==1.11.21
- pycharm
创建项目
- 命令行:
- 找一个文件夹存放项目文件,打开终端:
- django-admin startproject 项目名称
- 项目目录
- pycahrm
- 命令行:
启动
- 命令行
- 切换到项目的根目录下 manage.py
python36 manage.py runserver
—— 127.0.0.1:80`python36 manage.py runserver 80
——127.0.0.1:80python36 manage.py runserver 0.0.0.0:80
——0.0.0.0:80
- pycharm:点绿三角启动 可配置
- 命令行
简单使用
- 示例:返回HTML指定文件
# 在urls.py中 # 导入 from django.shortcuts import HttpResponse,render # 函数 def index(request): # return HttpResponse('index') return render(request,'index.html') # url和函数对应关系 urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', index), ]
12.3.2 静态文件
配置
- 在settings.py中设置
STATIC_URL = '/static/' # 别名 STATICFILES_DIRS = [ # 设置文件路径,可以设置多个 os.path.join(BASE_DIR, 'static1'), os.path.join(BASE_DIR, 'static'), os.path.join(BASE_DIR, 'static2'), ]
使用
- 在路径前添加别名:/static/
- 多个文件路径,也是使用同一个别名,不是文件名
- 如果别名后的路径名相同,按照STATICFILES_DIRS列表的顺序进行查找
{# 别名开头 #}
12.3.3 简单的登录实例
form表单提交数据注意的问题:
- 提交的地址:action="",请求的方式:method="post"
- 所有的input框有name属性,如name="username"
- 有一个input框的type="submit"或者有一个button
提交post请求,由于Django中有一个csrf校验,所有请求会出问题
- 解决方式:把settings中MIDDLEWARE的'django.middleware.csrf.CsrfViewMiddleware'注释掉
- 或者在html 页面form表单下写 {% csrf_token %}
重定向
导入方式
from django.shortcuts import redirect
使用方式
在函数中使用: return redirect('/index/') #参数 url #注意:前面必须加/,代表从url根拼接,否则就会在当前url后面一直拼接
示例:
from django.shortcuts import HttpResponse, render, redirect def index(request): # return HttpResponse('index') return render(request, 'index.html') def login(request): if request.method == 'POST': # 获取form表单提交的书籍 username = request.POST['username'] password = request.POST['password'] # 验证用户名和密码 if models.User.objects.filter(username=username,password=password): # 验证成功跳转到index页面 # return redirect('https://www.baidu.com/') return redirect('/index/') # 不成功 重新登录 return render(request, 'login.html') urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', views.index), url(r'^login/', views.login), ]
12.3.4 app
创建app
- 命令行:python manage.py startapp app名称
- pycharm:tools --> run manage.py task --> 输入命令:startapp app名称
注册app
- 在settings.py中设置,例:app名为app01
INSTALLED_APPS = [ ... 'app01', 'app01.apps.App01Config', # 推荐写法 ]
app中的文件
- migrations:存放迁移文件的
- admin.py:Django提供的后台管理工具
- app.py:与app信息相关的
- models.py:跟ORM有关的内容
- views.py:视图,写函数的
12.3.5 使用MySQL流程
创建一个MySQL数据库:create database day53;
在settings.py中设置,Django连接MySQL数据库:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', # 引擎 'NAME': 'day53', # 数据库名称 'HOST': '127.0.0.1', # ip地址 'PORT':3306, # 端口 'USER':'root', # 用户 'PASSWORD':'123' # 密码 } }
在与settings,py同级目录下的init文件中写入:
import pymysql pymysql.install_as_MySQLdb()
创建表(在app下的models.py中写类):
from django.db import models class User(models.Model): username = models.CharField(max_length=32) # username varchar(32) password = models.CharField(max_length=32) # username varchar(32)
执行数据库迁移的命令:
- python manage.py makemigrations:检测每个注册app下的model.py,记录model的变更记录
- python manage.py migrate:同步变更记录到数据库中
12.3.6 MVC和MTV
- MVC
- M: model 模型 —— 和数据库打交道
- V:view 视图 —— HTML
- C: controller 控制器 —— 调度 传递指令 业务逻辑
- MTV:
- M: model 模型 ORM —— 和数据库打交道
- T: tempalte 模板 —— HTML
- V:view 视图 —— 函数 业务逻辑
- djando是MTV模式
12.4 Django模板系统:Template
12.4.1 模板常用语法
- 特殊符号:
- 变量:{{ }}
- 标签tag:{% %}