放弃不难,但坚持一定很酷。
基本概念
1、web应用
web应用是运行在浏览器上的应用。
一个Web应用程序是由完成特定任务的各种Web组件(web components)构成的并通过Web将服务展示给外界。
在实际应用中,Web应用程序是由多个Servlet、JSP页面、HTML文件以及图像文件等组成。
所有这些组件相互协调为用户提供一组完整的服务。
2、B/S和C/S架构
client/server:客户端服务器架构,一般独立运行,通常使用C++
brower/server:浏览器服务器架构,一般借助浏览器来运行,通常使用Java、Python
🌸底层均是基于socket
3、Python Web框架
a.socket b.页面路由 c.模板渲染
Django 重量级 功能全但是笨重
Flask 轻量级 主要依赖第三方模块
Tornado 原生的 异步非阻塞,主要用在高io,多路复用的情况,支持高并发
Django a用的wsgiref b自己写的 c自己写的
Flask a用的第三方 b自己写的 c自己写的
Tornado a自己写的 b自己写的 c自己写的
4、HTTP协议
网络七层协议(OSI七层模型)


(1)概念:HTTP(HyperText Transport Protocol)是超文本传输协议,规定了客户端和服务端消息传输的格式。
(2)四大特性:
a .基于TCP/IP协议基础上的应用层协议,底层实现仍然是socket。
b.基于请求-响应模式:通信一定是从客户端开始,服务端接收到客户端的请求一定会做出对应的响应,但是服务器不会主动发送请求。
c.无状态:协议不对任何一次通信状态和任何数据做保存。
d.无连接:一次连接只完成一次请求-响应,请求-响应完毕后会立即断开连接。
🐨websocket是常链
(3)HTTP工作原理(事务)
一次http操作称之为一个事务,工作过程可分为四步:
a.客户端与服务端建立连接;
b.客户端发一个http协议指定格式的请求;
c.服务器端接收请求后,响应一个http协议指定格式的响应;
d.客户端将服务器的响应展示给用户。
(4)请求报文(请求响应)
请求首行(请求方式,请求路径,http协议)
请求头(一堆k,v键值对)
\r\n (\r\n是换行,将响应体隔开)
请求体(post请求或get请求携带的数据)

POST / HTTP/1.1\r\n
Host: 127.0.0.1:8001\r\n
Connection: keep-alive\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\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
usr=abc&pwd=123
(5)响应报文
响应首行(状态信息,请求信息)
响应头(一堆k,v键值对)
\r\n (空着,隔开)
响应体(响应结果)

HTTP/1.1 200 OK\r\n
Content-type:text/html\r\n
\r\n
Login Success
(6)状态码
1XXX 服务器已经成功接收到你的数据正在处理,你可以继续提交其他数据
2XXX 请求成功 服务器已经将你请求的数据发送给你了
3XXX 重定向
4XXX 请求资源不存在/错误
5XXX 服务器错误
5、动静态网页
静态网页:页面上的数据都是写死的,万年不变
动态网页:页面上的数据是从后端动态获取的
比如后端获取当前时间
后端获取数据库数据然后传递给前端页面
6、模版渲染
后端生成的数据直接传递给前端页面使用(并且前端页面可以灵活的操作该数据)>>>模版语法
模版渲染 模版语法需要依赖第三方模块jinjia2
模版语法 jinja2支持前端直接使用类似于python的语法操作数据
🐨templates:该文件夹存放的就是所有的页面文件(.html)
🐷关于jinja2🐷
(1)首先了解什么是模板
模板在Python的web开发中广泛使用,它能够有效的将业务逻辑和页面逻辑分开,使代码可读性增强、并且更加容易理解和维护。简单来说就是一个其中包涵占位变量表示动态的部分的文件,模板文件在经过动态赋值后,返回给用户。 --> 可以理解为渲染
python中自带一个简单的模板,就是string提供的,但是string模块不支持控制语句、表达式和继承等功能。
import string
a=string.Template('$who is $role')
print(a.substitute(who='wpr',role='god'))
输出>>>:wpr is god
目前主流的模板系统,最常用的就是jinja2和mako
(2)jinja2简介
jinja2是Flask作者开发的一个模板系统,起初是仿django模板的一个模板引擎,为Flask提供模板支持,由于其灵活,快速和安全等优点被广泛使用。
ps:jinja2语法和django很多语法类似。
🍓jinja2优点:
a、相对于Template,jinja2更加灵活,它提供了控制结构,表达式和继承等。
b、相对于Mako,jinja2仅有控制结构,不允许在模板中编写太多的业务逻辑。
c、相对于Django模板,jinja2性能更好。
d、Jinja2模板的可读性很棒。
(3)jinja2基本语法
控制结构 {% %}
变量取值 {{ }}
注释 {# #}
(4)控制结构
jinja2中的if语句类似与Python的if语句,它也具有单分支,多分支等多种结构,不同的是,条件语句不需要使用冒号结尾,而结束控制语句,需要使用endif关键字。
{% if wpr.safe %}
wpr is safe.
{% elif wpr.dead %}
wpr is dead
{% else %}
wpr is okay
{% endif %}
(5)变量
jinja2模板中使用 {{ }} 语法表示一个变量,它是一种特殊的占位符。当利用jinja2进行渲染的时候,它会把这些特殊的占位符进行填充/替换,jinja2支持python中所有的Python数据类型比如列表、字段、对象等。
<p>this is a dicectory:{{ mydict['key'] }} </p>
<p>this is a list:{{ mylist[3] }} </p>
<p>this is a object:{{ myobject.something() }} </p>
(6)jinja2中的过滤器
变量可以通过“过滤器”进行修改,过滤器可以理解为是jinja2里面的内置函数和字符串处理函数。

过滤器用法:在变量后面使用管道(|)分割,多个过滤器可以链式调用,前一个过滤器的输出会作为后一个过滤器的输入。
{{ 'abc' | captialize }} >>>输出Abc
{{ 'hello world' | replace('world','wpr') | upper }} >>>输出HELLO WPR
(7)for循环
jinja2中的for循环用于迭代Python的数据类型,包括列表,元组和字典。在jinja2中不存在while循环。
迭代列表
<ul>
{% for user in users %}
<li>{{ user.username|title }}</li>
{% endfor %}
</ul>
迭代字典
<dl>
{% for key, value in my_dict.iteritems() %}
<dt>{{ key }}</dt>
<dd>{{ value}}</dd>
{% endfor %}
</dl>
当然也可以加入else语句,在循环正确执行完毕后执行
在for循环中,jinja2还提供了一些特殊的变量,用以来获取当前的遍历状态:

(8)宏
宏类似于Python中的函数,我们在宏中定义行为,还可以进行传递参数,就像Python中的函数一样的。
在宏中定义一个宏的关键字是macro,后面跟其宏的名称和参数等
{% macro input(name,age=18) %} # 参数age的默认值为18
<input type='text' name="{{ name }}" value="{{ age }}" >
{% endmacro %}
调用方法也和Python的类似
<p>{{ input('wpr') }} </p>
<p>{{ input('wpr',age=17) }} </p>
(9)继承与Super函数
jinja2中最强大的部分就是模板继承。模板继承允许我们创建一个基本(骨架)文件,其他文件从该骨架文件继承,然后针对自己需要的地方进行修改。
jinja2的骨架文件中,利用block关键字表示其包涵的内容可以进行修改。
以下面的骨架文件base.html为例:
<!DOCTYPE html>
<html lang="en">
<head>
{% block head %}
<link rel="stylesheet" href="style.css"/>
<title>{% block title %}{% endblock %} - My Webpage</title>
{% endblock %}
</head>
<body>
<div id="content">{% block content %}{% endblock %}</div>
<div id="footer">
{% block footer %}
<script>This is javascript code </script>
{% endblock %}
</div>
</body>
</html>
这里定义了四处 block,即:head,title,content,footer,下面进行继承和变量替换。
{% extend "base.html" %} # 继承base.html文件
{% block title %} Dachenzi {% endblock %} # 定制title部分的内容
{% block head %}
{{ super() }} # 用于获取原有的信息
<style type='text/css'>
.important { color: #FFFFFF }
</style>
{% endblock %}
# 其他不修改的原封不同的继承
ps:super()函数 表示获取block块中定义的原来的内容。
7、原生socket服务
目录结构
--index.html
-- server.py
基础socket服务
在server.py中
import socket
# 利用socket.socket()
server = socket.socket()
#设置ip和端口
server.bind(('127.0.0.1',8080))
# 设置监听
server.listen(5)
print('服务设置成功')
print('浏览器访问:http://127.0.0.1:8080')
while True:
# 阻塞等待客户端数据
client, address = server.address = server.accept()
# 接收数据
data = client.recv(1024)
print('接收到数据: ', data)
# 返回数据
client.send(b'Normal Socket Web')
# 关闭连接(必须关闭每一次连接)
client.close()
# 浏览器错误:发送的响应无效——>原因:响应不满足http协议
# 请求发来的数据
b'GET / HTTP/1.1\r\n
Host: 127.0.0.1:8080\r\n
Connection: keep-alive\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\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=szfYLDVuqvRhlveNpNE2rp1GYOcI5x7mRNfvkRWTMRNRwWxXMZWOhL1MqknYJ7jg; sessionid=3pphvmw2icub0bea7nn02u6wev17k4uw\r\n
\r\n'
修改返回的数据,完善响应体
# 字符串
client.send(b'HTTP/1.1 200 OK\r\n')
client.send(b'\r\n')
client.send(b'Normal Socket Web')
# html文件(同级目录建立一个index.html页面)
client.send(b'HTTP/1.1 200 OK\r\n')
client.send(b'Content-type:text/html\r\n')
client.send(b'\r\n')
# 利用文件方式读取页面
with open('index.html', 'rb') as f:
dt = f.read()
client.send(dt)
修改接收数据,模拟后台路由
# 分析接收到的数据
data = client.recv(1024)
# 保证接收到的数据作为字符串进行以下处理
data = str(data, encoding='utf-8')
# 拆分出地址位
route = data.split('\r\n')[0].split(' ')[1]
# 匹配地址,做出不同的响应
if route == '/index':
with open('index.html', 'rb') as f:
dt = f.read()
elif route == '/login': # 新建login页面
with open('login.html', 'rb') as f:
dt = f.read()
else:
dt = b'404'
client.send(dt)
index.html代码中
# html代码,请求头要设置支持html代码
client.send(b'HTTP/1.1 200 OK\r\n')
client.send(b'Content-type:text/html\r\n')
client.send(b'\r\n')
client.send(b'<h1>Normal Socket Web</h1>')
# html文件(同级目录建立一个index.html页面)
client.send(b'HTTP/1.1 200 OK\r\n')
client.send(b'Content-type:text/html\r\n')
client.send(b'\r\n')
# 利用文件方式读取页面
with open('index.html', 'rb') as f:
dt = f.read()
client.send(dt)
8、框架演变
目录结构
-- favicon.ico
-- index.html
-- manage.py
在manage.py中
import socket
import pymysql
# 响应头
RESP_HEADER = b'HTTP/1.1 200 OK\r\nContent-type:text/html\r\n\r\n'
# 请求处理
def index():
# 以字节方式读取文件
with open('index.html', 'rb') as f:
dt = f.read()
return dt
def ico():
with open(favicon.jpeg, 'rb') as f:
dt = f.read()
return dt
def user():
# 数据库操作
conn = pymysql.connect(host='127.0.0.1', port=3306, db='django', user='root', password='root')
cur = conn.cursor(pymysql.cursors.DictCursor)
cur.execute('select * from user')
users = cur.fetchall()
print(users)
users = '''%d:%s
%d:%s''' % (users[0]['id'], users[0]['name'], users[1]['id'], users[1]['name'])
return users.encode('utf-8')
# 设置路由
urls = {
# 请求路径与请求处理函数一一对应
'/index': index,
favicon.jpeg: ico,
'/user': user
}
# 设置socket
def serve(host, port):
server = socket.socket()
server.bind((host, port))
print('start:http://' + host + ':' + str(port))
server.listen(5)
while True:
sock, addr = server.accept()
data = sock.recv(1024)
data = str(data, encoding='utf-8')
print(data)
route = data.split('\r\n')[0].split(' ')[1]
resp = b'404'
if route in urls:
resp = urls[route]()
sock.send(RESP_HEADER)
sock.send(resp)
sock.close()
# 启服务
if __name__ == '__main__':
serve('127.0.0.1', 8002)
9、项目演变
目录结构
-- template
-- index.html
-- user.html
--favicon.ico
--start.py
--urls.py
--views.py
在index.html中
<h1>{{ name }}</h1>
在user.html中
<table border="1">
<tr>
<th>id</th>
<th>name</th>
<th>password</th>
</tr>
{% for user in users%}
<tr>
<td>{{user.id}}</td>
<td>{{user.name}}</td>
<td>{{user.password}}</td>
</tr>
{% endfor %}
</table>
在start.py中
from wsgiref.simple_server import make_server
from urls import urls
def app(env, response):
print(env)
# 设置响应头
response("200 OK", [('Content-type', 'text/html')])
route = env['PATH_INFO']
print(route)
data = urls['error']()
if route in urls:
data = urls[route]()
# 返回二进制响应体
return [data]
if __name__ == '__main__':
server = make_server('127.0.0.1', 8003, app)
print('start:http://127.0.0.1:8003')
server.serve_forever()
在urls.py中
from views import *
urls = {
'/index': index,
'/favicon.ico': ico,
'/user': user,
'error': error
}
在views.py中
import pymysql
# 利用jinja2来渲染模板,将后台数据传给前台
from jinja2 import Template
def index():
with open('templates/index.html', 'r') as f:
dt = f.read()
tem = Template(dt)
resp = tem.render(name='主页')
return resp.encode('utf-8')
def ico():
with open('favicon.ico', 'rb') as f:
dt = f.read()
return dt
def user():
# 数据库操作
conn = pymysql.connect(host='127.0.0.1', port=3306, db='django', user='root', password='root')
cur = conn.cursor(pymysql.cursors.DictCursor)
cur.execute('select * from user')
users = cur.fetchall()
print(users)
with open('templates/user.html', 'r') as f:
dt = f.read()
tem = Template(dt)
resp = tem.render(users=users)
return resp.encode('utf-8')
def error():
return b'404'
网友评论