Web框架本质

我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端。 这样我们就可以自己实现Web框架了。

半成品自定义web框架

importsocket#创建socket对象,绑定ip,监听

sk =socket.socket()

sk.bind(("127.0.0.1", 80))

sk.listen()#循环,建立连接,接收数据,发送数据,关闭连接

whileTrue:

conn, addr=sk.accept()

data= conn.recv(8096)

conn.send(b"OK")

conn.close()

可以说Web服务本质上都是在这十几行代码基础上扩展出来的。这段代码就是它们的祖宗。

用户的浏览器一输入网址,会给服务端发送数据,那浏览器会发送什么数据?怎么发?这个谁来定? 你这个网站是这个规定,他那个网站按照他那个规定,这互联网还能玩么?

所以,必须有一个统一的规则,让大家发送消息、接收消息的时候有个格式依据,不能随便写。

这个规则就是HTTP协议,以后浏览器发送请求信息也好,服务器回复响应信息也罢,都要按照这个规则来。

HTTP协议主要规定了客户端和服务器之间的通信格式,那HTTP协议是怎么规定消息格式的呢?

让我们首先打印下我们在服务端接收到的消息是什么。

importsocket

sk=socket.socket()

sk.bind(("127.0.0.1", 80))

sk.listen()whileTrue:

conn, addr=sk.accept()

data= conn.recv(8096)print(data) #将浏览器发来的消息打印出来

conn.send(b"OK")

conn.close()

输出:

b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nDNT: 1\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\nCookie: csrftoken=RKBXh1d3M97iz03Rpbojx1bR6mhHudhyX5PszUxxG3bOEwh1lxFpGOgWN93ZH3zv\r\n\r\n'

然后我们再看一下我们访问博客园官网时浏览器收到的响应信息是什么。

响应相关信息可以在浏览器调试窗口的network标签页中看到。

点击view source之后显示如下图:

我们发现收发的消息需要按照一定的格式来,这里就需要了解一下HTTP协议了。

HTTP协议对收发消息的格式要求

每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的。 HTTP响应的Header中有一个 Content-Type表明响应的内容格式。如 text/html表示HTML网页。

HTTP GET请求的格式:

HTTP响应的格式:

处女版自定义web框架

经过上面的补充学习,我们知道了要想让我们自己写的web server端正经起来,必须要让我们的Web server在给客户端回复消息的时候按照HTTP协议的规则加上响应状态行,这样我们就实现了一个正经的Web框架了。

importsocket

sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)

sock.bind(('127.0.0.1', 8000))

sock.listen()whileTrue:

conn, addr=sock.accept()

data= conn.recv(8096)#给回复的消息加上响应状态行

conn.send(b"HTTP/1.1 200 OK\r\n\r\n")

conn.send(b"OK")

conn.close()

我们通过十几行代码简单地演示了web 框架的本质。

接下来就让我们继续完善我们的自定义web框架吧!

根据不同的路径返回不同的内容

这样就结束了吗? 如何让我们的Web服务根据用户请求的URL不同而返回不同的内容呢?

小事一桩,我们可以从请求相关数据里面拿到请求URL的路径,然后拿路径做一个判断...

"""根据URL中不同的路径返回不同的内容"""

importsocket

sk=socket.socket()

sk.bind(("127.0.0.1", 8080)) #绑定IP和端口

sk.listen() #监听

while 1:#等待连接

conn, add =sk.accept()

data= conn.recv(8096) #接收客户端发来的消息

#从data中取到路径

data = str(data, encoding="utf8") #把收到的字节类型的数据转换成字符串

#按\r\n分割

data1 = data.split("\r\n")[0]

url= data1.split()[1] #url是我们从浏览器发过来的消息中分离出的访问路径

conn.send(b'HTTP/1.1 200 OK\r\n\r\n') #因为要遵循HTTP协议,所以回复的消息也要加状态行

#根据不同的路径返回不同内容

if url == "/index/":

response= b"index"

elif url == "/home/":

response= b"home"

else:

response= b"404 not found!"conn.send(response)

conn.close()

根据不同的路径返回不同的内容--函数版

上面的代码解决了不同URL路径返回不同内容的需求。

但是问题又来了,如果有很多很多路径要判断怎么办?难道要挨个写if判断? 当然不用,我们有更聪明的办法。

"""根据URL中不同的路径返回不同的内容--函数版"""

importsocket

sk=socket.socket()

sk.bind(("127.0.0.1", 8080)) #绑定IP和端口

sk.listen() #监听

#将返回不同的内容部分封装成函数

defindex(url):

s= "这是{}页面!".format(url)return bytes(s, encoding="utf8")defhome(url):

s= "这是{}页面!".format(url)return bytes(s, encoding="utf8")while 1:#等待连接

conn, add =sk.accept()

data= conn.recv(8096) #接收客户端发来的消息

#从data中取到路径

data = str(data, encoding="utf8") #把收到的字节类型的数据转换成字符串

#按\r\n分割

data1 = data.split("\r\n")[0]

url= data1.split()[1] #url是我们从浏览器发过来的消息中分离出的访问路径

conn.send(b'HTTP/1.1 200 OK\r\n\r\n') #因为要遵循HTTP协议,所以回复的消息也要加状态行

#根据不同的路径返回不同内容,response是具体的响应体

if url == "/index/":

response=index(url)elif url == "/home/":

response=home(url)else:

response= b"404 not found!"conn.send(response)

conn.close()

根据不同的路径返回不同的内容--函数进阶版

看起来上面的代码还是要挨个写if判断,怎么办?我们还是有办法!(只要思想不滑坡,方法总比问题多!)

"""根据URL中不同的路径返回不同的内容--函数进阶版"""

importsocket

sk=socket.socket()

sk.bind(("127.0.0.1", 8080)) #绑定IP和端口

sk.listen() #监听

#将返回不同的内容部分封装成函数

defindex(url):

s= "这是{}页面!".format(url)return bytes(s, encoding="utf8")defhome(url):

s= "这是{}页面!".format(url)return bytes(s, encoding="utf8")#定义一个url和实际要执行的函数的对应关系

list1 =[

("/index/", index),

("/home/", home),

]while 1:#等待连接

conn, add =sk.accept()

data= conn.recv(8096) #接收客户端发来的消息

#从data中取到路径

data = str(data, encoding="utf8") #把收到的字节类型的数据转换成字符串

#按\r\n分割

data1 = data.split("\r\n")[0]

url= data1.split()[1] #url是我们从浏览器发过来的消息中分离出的访问路径

conn.send(b'HTTP/1.1 200 OK\r\n\r\n') #因为要遵循HTTP协议,所以回复的消息也要加状态行

#根据不同的路径返回不同内容

func = None #定义一个保存将要执行的函数名的变量

for i inlist1:if i[0] ==url:

func= i[1]break

iffunc:

response=func(url)else:

response= b"404 not found!"

#返回具体的响应消息

conn.send(response)

conn.close()

返回具体的HTML文件

完美解决了不同URL返回不同内容的问题。 但是我不想仅仅返回几个字符串,我想给浏览器返回完整的HTML内容,这又该怎么办呢?

没问题,不管是什么内容,最后都是转换成字节数据发送出去的。 我们可以打开HTML文件,读取出它内部的二进制数据,然后再发送给浏览器。

"""根据URL中不同的路径返回不同的内容--函数进阶版

返回独立的HTML页面"""

importsocket

sk=socket.socket()

sk.bind(("127.0.0.1", 8080)) #绑定IP和端口

sk.listen() #监听

#将返回不同的内容部分封装成函数

defindex(url):#读取index.html页面的内容

with open("index.html", "r", encoding="utf8") as f:

s=f.read()#返回字节数据

return bytes(s, encoding="utf8")defhome(url):

with open("home.html", "r", encoding="utf8") as f:

s=f.read()return bytes(s, encoding="utf8")#定义一个url和实际要执行的函数的对应关系

list1 =[

("/index/", index),

("/home/", home),

]while 1:#等待连接

conn, add =sk.accept()

data= conn.recv(8096) #接收客户端发来的消息

#从data中取到路径

data = str(data, encoding="utf8") #把收到的字节类型的数据转换成字符串

#按\r\n分割

data1 = data.split("\r\n")[0]

url= data1.split()[1] #url是我们从浏览器发过来的消息中分离出的访问路径

conn.send(b'HTTP/1.1 200 OK\r\n\r\n') #因为要遵循HTTP协议,所以回复的消息也要加状态行

#根据不同的路径返回不同内容

func = None #定义一个保存将要执行的函数名的变量

for i inlist1:if i[0] ==url:

func= i[1]break

iffunc:

response=func(url)else:

response= b"404 not found!"

#返回具体的响应消息

conn.send(response)

conn.close()

让网页动态起来

这网页能够显示出来了,但是都是静态的啊。页面的内容都不会变化的,我想要的是动态网站。

没问题,我也有办法解决。我选择使用字符串替换来实现这个需求。(这里使用时间戳来模拟动态的数据)

"""根据URL中不同的路径返回不同的内容--函数进阶版

返回HTML页面

让网页动态起来"""

importsocketimporttime

sk=socket.socket()

sk.bind(("127.0.0.1", 8080)) #绑定IP和端口

sk.listen() #监听

#将返回不同的内容部分封装成函数

defindex(url):

with open("index.html", "r", encoding="utf8") as f:

s=f.read()

now=str(time.time())

s= s.replace("@@oo@@", now) #在网页中定义好特殊符号,用动态的数据去替换提前定义好的特殊符号

return bytes(s, encoding="utf8")defhome(url):

with open("home.html", "r", encoding="utf8") as f:

s=f.read()return bytes(s, encoding="utf8")#定义一个url和实际要执行的函数的对应关系

list1 =[

("/index/", index),

("/home/", home),

]while 1:#等待连接

conn, add =sk.accept()

data= conn.recv(8096) #接收客户端发来的消息

#从data中取到路径

data = str(data, encoding="utf8") #把收到的字节类型的数据转换成字符串

#按\r\n分割

data1 = data.split("\r\n")[0]

url= data1.split()[1] #url是我们从浏览器发过来的消息中分离出的访问路径

conn.send(b'HTTP/1.1 200 OK\r\n\r\n') #因为要遵循HTTP协议,所以回复的消息也要加状态行

#根据不同的路径返回不同内容

func = None #定义一个保存将要执行的函数名的变量

for i inlist1:if i[0] ==url:

func= i[1]break

iffunc:

response=func(url)else:

response= b"404 not found!"

#返回具体的响应消息

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开发环境用的就是这个模块来做服务器。

从这继续...

wsgiref

我们利用wsgiref模块来替换我们自己写的web框架的socket server部分:

"""根据URL中不同的路径返回不同的内容--函数进阶版

返回HTML页面

让网页动态起来

wsgiref模块版"""

importtimefrom wsgiref.simple_server importmake_server#将返回不同的内容部分封装成函数

defindex(url):

with open("index.html", "r", encoding="utf8") as f:

s=f.read()

now=str(time.time())

s= s.replace("@@oo@@", now)return bytes(s, encoding="utf8")defhome(url):

with open("home.html", "r", encoding="utf8") as f:

s=f.read()return bytes(s, encoding="utf8")#定义一个url和实际要执行的函数的对应关系

list1 =[

("/index/", index),

("/home/", home),

]defrun_server(environ, start_response):

start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) #设置HTTP响应的状态码和头信息

url = environ['PATH_INFO'] #取到用户输入的url

func =Nonefor i inlist1:if i[0] ==url:

func= i[1]break

iffunc:

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()

jinja2

上面的代码实现了一个简单的动态,我完全可以从数据库中查询数据,然后去替换我html中的对应内容,然后再发送给浏览器完成渲染。 这个过程就相当于HTML模板渲染数据。 本质上就是HTML内容中利用一些特殊的符号来替换要展示的数据。 我这里用的特殊符号是我定义的,其实模板渲染有个现成的工具: jinja2

下载jinja2:

pip install jinja2

Title

姓名:{{name}}

爱好:

  • {% for hobby in hobby_list %}
  • {{hobby}}{% endfor %}

index2.html文件

index2.html文件

使用jinja2渲染index2.html文件:

from wsgiref.simple_server importmake_serverfrom jinja2 importTemplatedefindex():

with open("index2.html", "r") as f:

data=f.read()

template= Template(data) #生成模板文件

ret = template.render({"name": "Alex", "hobby_list": ["烫头", "泡吧"]}) #把数据填充到模板里面

return [bytes(ret, encoding="utf8"), ]defhome():

with open("home.html", "rb") as f:

data=f.read()return[data, ]#定义一个url和函数的对应关系

URL_LIST =[

("/index/", index),

("/home/", home),

]defrun_server(environ, start_response):

start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) #设置HTTP响应的状态码和头信息

url = environ['PATH_INFO'] #取到用户输入的url

func = None #将要执行的函数

for i inURL_LIST:if i[0] ==url:

func= i[1] #去之前定义好的url列表里找url应该执行的函数

break

if func: #如果能找到要执行的函数

return func() #返回函数的执行结果

else:return [bytes("404没有该页面", encoding="utf8"), ]if __name__ == '__main__':

httpd= make_server('', 8000, run_server)print("Serving HTTP on port 8000...")

httpd.serve_forever()

现在的数据是我们自己手写的,那可不可以从数据库中查询数据,来填充页面呢?

使用pymysql连接数据库:

conn = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="xxx", db="xxx", charset="utf8")

cursor= conn.cursor(cursor=pymysql.cursors.DictCursor)

cursor.execute("select name, age, department_id from userinfo")

user_list=cursor.fetchall()

cursor.close()

conn.close()

创建一个测试的user表:

CREATE TABLE user(

id int auto_increment PRIMARY KEY,

name CHAR(10) NOT NULL,

hobby CHAR(20) NOT NULL

)engine=innodb DEFAULT charset=UTF8;

模板的原理就是字符串替换,我们只要在HTML页面中遵循jinja2的语法规则写上,其内部就会按照指定的语法进行相应的替换,从而达到动态的返回内容。

Django

安装(安装最新LTS版):

pip3 install django==1.11.9

创建一个django项目:

下面的命令创建了一个名为"mysite"的Django 项目:

django-admin startproject mysite

目录介绍:

mysite/├── manage.py#管理文件

└── mysite #项目目录

├── __init__.py

├── settings.py#配置

├── urls.py #路由 --> URL和函数的对应关系

└── wsgi.py #runserver命令就使用wsgiref模块做简单的web server

运行Django项目:

python manage.py runserver 127.0.0.1:8000

模板文件配置:

TEMPLATES =[

{'BACKEND': 'django.template.backends.django.DjangoTemplates','DIRS': [os.path.join(BASE_DIR, "template")], #template文件夹位置

'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',

],

},

},

]

静态文件配置:

STATIC_URL = '/static/' #HTML中使用的静态文件夹前缀

STATICFILES_DIRS =[

os.path.join(BASE_DIR,"static"), #静态文件存放位置

]

看不明白?有图有真相:

刚开始学习时可在配置文件中暂时禁用csrf中间件,方便表单提交测试。

MIDDLEWARE =['django.middleware.security.SecurityMiddleware','django.contrib.sessions.middleware.SessionMiddleware','django.middleware.common.CommonMiddleware',#'django.middleware.csrf.CsrfViewMiddleware',

'django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware',

]

Django基础必备三件套:

from django.shortcuts import HttpResponse, render, redirect

HttpResponse

内部传入一个字符串参数,返回给浏览器。

例如:

defindex(request):#业务逻辑代码

return HttpResponse("OK")

render

除request参数外还接受一个待渲染的模板文件和一个保存具体数据的字典参数。

将数据填充进模板文件,最后把结果返回给浏览器。(类似于我们上面用到的jinja2)

例如:

defindex(request):#业务逻辑代码

return render(request, "index.html", {"name": "alex", "hobby": ["烫头", "泡吧"]})

redirect

接受一个URL参数,表示跳转到指定的URL。

例如:

defindex(request):#业务逻辑代码

return redirect("/home/")

重定向是怎么回事?

课后练习:

Django版登录

启动Django报错:

Django 启动时报错 UnicodeEncodeError ...

报这个错误通常是因为计算机名为中文,改成英文的计算机名重启下电脑就可以了。

django for 前端_Django 前端Wbe框架相关推荐

  1. Web前端要学什么框架呢?推荐这几款

    经过行业的发展,Web前端行业愈加成熟,企业对Web前端人员要求也越来越高,对于想要进入前端行业的人而言挑战比以前更大了.学习Web框架可以加快Web开发速度,节约时间.就目前来说,Web前端要学什么 ...

  2. 进阶攻略|前端最全的框架总结

    前端的技术日渐更新,最近得空,花了一上午的时间,将前端常见的UI框架总结了一下,在开发的过程之中,有了这些,不断能够提高自己的工作效率,还可以在工作之余了解更多.希望大家喜欢. 1.Layui 官方网 ...

  3. Web前端-Vue.js必备框架(一)

    Web前端-Vue.js必备框架(一) <!DOCTYPE html> <html lang="en"> <head><meta char ...

  4. 懒懒交流会《前端,架构,框架与库》里面提到的一些问题

    这个视频看了快两三个月了,一直很是在意里面提到的很多很有意思的问题,因为都没有给出一个答案啊.就又看了一遍,边听边记先把问题题目记录下来. 1.对comet技术的了解,什么时候用,什么时候不用 2.如 ...

  5. 前端大屏展示框架搭建(二)

    大屏基本布局实现 基于element-ui进行布局. 安装 安装运行依赖 element-ui 安装开发依赖 babel-plugin-component 安装插件 vue-cli-plugin-el ...

  6. 视频教程-Web前端开发利器 SPRY框架之表单验证-JavaScript

    Web前端开发利器 SPRY框架之表单验证 有17年互联网行业从业经验,始终在教学第一线,勇于创新,从有效教学,不断向高效教学转变.始终坚持"学生为主体,教师为主导:商业化案例,企业化情境& ...

  7. 2012年度最佳Web前端开发工具和框架总结

    2012年度最佳Web前端开发工具和框架总结 2013/01/18 | 分类: 工具与资源 | 1 条评论 | 标签: 前端, 开发工具, 开发框架 分享到:0 来源:梦想天空 技术的快速发展让很多人 ...

  8. 2012年度最佳 Web 前端开发工具和框架——《上篇》

    技术的快速发展让很多人学习起来无所适从,幸运的是,很多优秀的 Web 开发人员和设计人员在努力寻找各种有特色的解决方案. 因此,我们有了很多优秀的小工具和库,每一个都是用来解决特定的问题或维护一组特定 ...

  9. 分享两款智慧物业系统源码,前后端分离,前端VUE,Uni-app框架

    分享两款智慧物业管理系统源码,源码免费分享,需要源码学习参考的小伙伴可以私信我. ▶▶▶1:Java智慧物业管理系统源码(App+业主端微信小程序+物业端H5) 智慧物业介绍: 一.技术架构 基于Sp ...

  10. Web前端-Vue.js必备框架(二)

    Web前端-Vue.js必备框架(二) vue调式工具vue-devtools 过滤器:vue.js允许你自定义过滤器,可被用作一些常见的文本格式化. mustache插值和v-bind表达式. vu ...

最新文章

  1. 计算机设备板块超跌,半导体全线拉升,沪指强势突破3600点,午后A股会再次冲高回落吗...
  2. 【Android 应用开发】Android之Bluetooth编程
  3. Linux进程状态解析 之 R、S、D、T、Z、X (主要有三个状态)
  4. no BDOC found in SMW01
  5. 水晶报表基础入门——1.水晶报表技术
  6. 今天的离离原上草的飞鸽传书
  7. java httpclient 跨域_13、HttpClient服务器跨域请求
  8. python编译出来的程序员_Windows下编译Python2.7源码
  9. “遗留代码是传奇!”
  10. matlab调用库函数,Matlab调用函数的详细操作
  11. android 自动更新apk版本
  12. 机器学习入门(三):神经网络起手式
  13. MT4MT5跟单EA系统跨平台
  14. 《即兴演讲》学习总结
  15. 如何在面试中介绍自己的项目经验
  16. android logo颜色渐变,华为悄然更新品牌Logo:无渐变色,更加扁平化
  17. 光缆弹性模量计算_光纤光缆布线基础知识及系统设计
  18. 如何使用 Reflector Keygen
  19. mac设置共享屏幕 苹果mac屏幕共享设置详细教程
  20. No.41-VulnHub-DEFCON Toronto: Galahad-Walkthrough渗透学习

热门文章

  1. 复旦大学《高等代数学习指导书(第三版)》勘误表
  2. 计算机等级考试四级信息安全工程师
  3. js 正则表达式大全 数字、字符等
  4. 山东大学计算机图形学实验(Opengl实现):Loop Subdivision算法对模型进行细分
  5. [qq机器人]nonebot2 群管插件2.0
  6. 知道创宇获CNNVD年度优秀技术支撑单位及漏洞预警报送专项奖
  7. idea进入方法快捷键详情大全(idea快捷键大全最新设置)
  8. 方舟php服务器控制,方舟基本管理命令代码
  9. OPNET14.5+WIN10+VS2010安装教程(附安装包)
  10. android系统计步修改,安卓手机计步软件怎么修改步数 无需root轻松修改步数