放弃不难,但坚持一定很酷。
基本概念
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数据类型比如列表、字段、对象等。
this is a dicectory:{{ mydict['key'] }}
this is a list:{{ mylist[3] }}
this is a object:{{ myobject.something() }}
(6)jinja2中的过滤器
变量可以通过“过滤器”进行修改,过滤器可以理解为是jinja2里面的内置函数和字符串处理函数。
过滤器用法:在变量后面使用管道(|)分割,多个过滤器可以链式调用,前一个过滤器的输出会作为后一个过滤器的输入。
{{ 'abc' | captialize }} >>>输出Abc
{{ 'hello world' | replace('world','wpr') | upper }} >>>输出HELLO WPR
(7)for循环
jinja2中的for循环用于迭代Python的数据类型,包括列表,元组和字典。在jinja2中不存在while循环。
迭代列表
{% for user in users %}
- {{ user.username|title }}
{% endfor %}
迭代字典
{% for key, value in my_dict.iteritems() %}
- {{ key }}
- {{ value}}
{% endfor %}
当然也可以加入else语句,在循环正确执行完毕后执行
在for循环中,jinja2还提供了一些特殊的变量,用以来获取当前的遍历状态:
(8)宏
宏类似于Python中的函数,我们在宏中定义行为,还可以进行传递参数,就像Python中的函数一样的。
在宏中定义一个宏的关键字是macro,后面跟其宏的名称和参数等
{% macro input(name,age=18) %} # 参数age的默认值为18
{% endmacro %}
调用方法也和Python的类似
{{ input('wpr') }}
{{ input('wpr',age=17) }}
(9)继承与Super函数
jinja2中最强大的部分就是模板继承。模板继承允许我们创建一个基本(骨架)文件,其他文件从该骨架文件继承,然后针对自己需要的地方进行修改。
jinja2的骨架文件中,利用block关键字表示其包涵的内容可以进行修改。
以下面的骨架文件base.html为例:
{% block head %}
{% block title %}{% endblock %} - My Webpage
{% endblock %}
{% block content %}{% endblock %}
这里定义了四处 block,即:head,title,content,footer,下面进行继承和变量替换。
{% extend "base.html" %} # 继承base.html文件
{% block title %} Dachenzi {% endblock %} # 定制title部分的内容
{% block head %}
{{ super() }} # 用于获取原有的信息
{% 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'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)
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中
{{ name }}
在user.html中
id
name
password
{% for user in users%}
{{user.id}}
{{user.name}}
{{user.password}}
{% endfor %}
在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'