基于Python的主流Web开发框架研究及实现,python web框架主要的三大基本框架

  基于Python的主流Web开发框架研究及实现,python web框架主要的三大基本框架

  仅供学习,转载请注明出处。

  之前我用TCP协议返回HTTP数据,实现了web静态页面返回的服务器端功能。

  但这并不能满足大部分的功能需求。

  首先你要知道浏览器在进行http请求时,不仅会请求静态资源,还会请求动态页面。

  那么什么是静态资源和动态页面呢?

  静态资源:如html文件、图片文件、css、js文件等。可以视为静态资源。

  动态页面:当诸如登录页面、查询页面、注册页面等页面被请求时,需要动态页面。可能会改变。

  哦,好像很厉害。

  浏览器请求动态页面的过程可以通过下图来理解,如下图:

  我之前开发的web静态服务器只是中间部分,只用来返回静态资源。那么后台应用框架就是处理动态请求的页面。

  比如浏览器发送http://172.16.5.81:7788/login.py的请求,会返回一个关于登录浏览器的页面,包括服务器当前时间等。

  还可以看到web服务器用wsgi协议调用应用程序框架。在这里,我们先不说什么是wsgi协议。让我们来看看我之前写的静态web服务器。

  您可以通过以下方式访问之前开发的web静态服务器:

  Python web服务器开发,多进程优化Python web服务器开发,多线程

  那么,我先取这两个代码中的一个进行优化开发,所以我采用多进程版本。

  优化的下一步就是把原来面向过程的代码修改成面向对象的代码,封装好。

  查看多进程web服务器代码-面向进程#coding=utf-8

  从套接字导入*

  进口re

  导入多重处理

  定义句柄_客户端(客户端_套接字):

  “为客户服务”

  #接收对方发送的数据

  recv _ data=client _ socket . recv(1024)。Decode (UTF-8) # 1024表示这次接收的最大字节数

  #打印从客户端发送的数据内容

  #print(客户端接收:,接收数据)

  request _ header _ lines=recv _ data . split lines()

  对于请求标题行中的行:

  打印(行)

  #返回浏览器数据

  #设置内容正文

  #使用常规匹配来匹配文件路径。

  打印(-,request_header_lines[0])

  打印(文件路径-,。/html/ re.match(r[^/]/([^\s]*),request_header_lines[0]).组(1))

  /([^\s]*),request_header_lines[0 re.match(r[^/])

  如果ret:

  文件路径=。/html/ ret.group(1)

  if file_path==。/html/:

  文件路径=。/html/index.html

  打印(文件路径*******,文件路径)

  尝试:

  #设置返回的头信息头

  response _ headers= http/1.1 200 ok \ r \ n # 200表示已经找到该资源。

  Response_headers=\r\n #正文中有一个空行

  #读取html文件内容

  File_name=file_path #设置读取文件的路径

  F=open(文件名, rb) #以二进制读取文件的内容

  response_body=f.read()

  f.close()

  #向浏览器返回数据

  client _ socket . send(response _ headers . encode( utf-8 )#对utf-8进行代码转换,并将数据发送到浏览器。

  client _ socket . send(response _ body)#转码utf-8并将数据发送到浏览器

  除了:

  #如果文件未找到,则打印404未找到

  #设置返回的头信息头

  Response _ headers= http/1.1 404未找到\ r \ n # 200表示已找到该资源

  Response_headers=\r\n #正文中有一个空行

  response_body= h1对不起,找不到文件/h1

  response=response_headers响应_正文

  client _ socket . send(response . encode( utf-8 ))

  # client _套接字. close()

  def main():

  #创建套接字

  服务器套接字=套接字(AF_INET,SOCK_STREAM)

  #先设置服务器关闭后立即释放资源,即服务器波动4次,保证下次运行程序时能立即绑定7788端口。

  server _ SOCKET . setsockopt(SOL _ SOCKET,SO_REUSEADDR,1)

  #设置服务器提供的服务的端口号。

  server_socket.bind(( ,7788))

  #使用socket创建的socket的默认属性是active,使用listen监听连接将其更改为passive。

  Server_socket.listen(128) #最多可以监听128个连接

  #打开while循环以处理访问请求。

  虽然正确:

  #如果有一个新的客户端链接到服务器,那么将创建一个新的套接字来服务这个客户端。

  # client_socket用于服务此客户端。

  # server_socket可以被保存以等待其他新的客户端连接,而True:

  client_socket,client addr=server _ socket . accept()

  # handle_client(客户端套接字)

  #设置子流程

  new_process=多重处理。进程(target=handle_client,args=(client_socket,))

  New_process.start() #启动子进程

  #因为子进程已经复制了父进程的套接字和其他资源,所以父进程不会通过调用close来关闭它们对应的链接。

  client _套接字. close()

  if __name__==__main__ :

  Main()我们先来回顾一下操作:

  好了,看到操作也正常了,我们来分析一下如何把代码封装成对象。

  封装分析首先我需要定义一个webServer类,然后封装访问静态资源的功能。

  #编码=utf-8

  从套接字导入*

  进口re

  导入多重处理

  类web服务器:

  def __init__(self):

  #创建套接字

  self . server _ socket=socket(AF _ INET,SOCK_STREAM)

  #先设置服务器关闭后立即释放资源,即服务器波动4次,保证下次运行程序时能立即绑定7788端口。

  self . server _ SOCKET . setsockopt(SOL _ SOCKET,SO_REUSEADDR,1)

  #设置服务器提供的服务的端口号。

  self.server_socket.bind(( ,7788))

  #使用socket创建的socket的默认属性是active,使用listen监听连接将其更改为passive。

  self . server _ socket . listen(128)#最多可以侦听128个连接

  def start_http_service(self):

  #打开while循环以处理访问请求。

  虽然正确:

  #如果有一个新的客户端链接到服务器,那么将创建一个新的套接字来服务这个客户端。

  # client_socket用于服务此客户端。

  # self.server_socket可以被保存以等待其他新的客户端连接,而True:

  client_socket,client addr=self . server _ socket . accept()

  # handle_client(客户端套接字)

  #设置子流程

  new_process=多重处理。进程(target=self.handle_client,args=(client_socket,))

  New_process.start() #启动子进程

  #因为子进程已经复制了父进程的套接字和其他资源,所以父进程不会通过调用close来关闭它们对应的链接。

  client _套接字. close()

  定义handle_client(self,client_socket):

  “为客户服务”

  #接收对方发送的数据

  recv _ data=client _ socket . recv(1024)。Decode (UTF-8) # 1024表示这次接收的最大字节数

  #打印从客户端发送的数据内容

  #print(客户端接收:,接收数据)

  request _ header _ lines=recv _ data . split lines()

  对于请求标题行中的行:

  打印(行)

  #返回浏览器数据

  #设置内容正文

  #使用常规匹配来匹配文件路径。

  打印(-,request_header_lines[0])

  打印(文件路径-,。/html/ re.match(r[^/]/([^\s]*),request_header_lines[0]).组(1))

  /([^\s]*),request_header_lines[0 re.match(r[^/])

  如果ret:

  文件路径=。/html/ ret.group(1)

  if file_path==。/html/:

  文件路径=。/html/index.html

  打印(文件路径*******,文件路径)

  尝试:

  #设置返回的头信息头

  response _ headers= http/1.1 200 ok \ r \ n # 200表示已经找到该资源。

  Response_headers=\r\n #正文中有一个空行

  #读取html文件内容

  File_name=file_path #设置读取文件的路径

  F=open(文件名, rb) #以二进制读取文件的内容

  response_body=f.read()

  f.close()

  #向浏览器返回数据

  client _ socket . send(response _ headers . encode( utf-8 )#对utf-8进行代码转换,并将数据发送到浏览器。

  client _ socket . send(response _ body)#转码utf-8并将数据发送到浏览器

  除了:

  #如果文件未找到,则打印404未找到

  #设置返回的头信息头

  Response _ headers= http/1.1 404未找到\ r \ n # 200表示已找到该资源

  Response_headers=\r\n #正文中有一个空行

  response_body= h1对不起,找不到文件/h1

  response=response_headers响应_正文

  client _ socket . send(response . encode( utf-8 ))

  def main():

  webserver=WebServer()

  webserver.start_http_service()

  if __name__==__main__ :

  Main()好了,从上面的代码来看,我已经把之前面向过程的代码修改成面向对象的了。

  运行它以查看是否有任何错误:

  安静地坐着。正常请求成功。

  思考:所以,它被封装成了一个对象。接下来应该优化什么?

  好了,请求静态资源的页面已经准备好了,那么如果请求动态页面呢?

  如果web服务器是用java写的,那么http请求通常是http: xxxx/xxx.jsp,如果web服务器是用php写的,那么http请求通常是http: xxxx/xxx.php,所以,既然我这次用python写,那么我可以把动态资源的请求定义为http: xxxx/xxx.py。

  如何识别和执行http:xxxx/xxx.py的请求?

  增加识别动态资源请求的功能需求:识别并返回http: xxxx/xxx.py的请求。

  让我考虑一下。让我先做一些简单的事情。比如我请求一个http请求,http:xxxx/time.py向浏览器返回当前服务器的时间。

  那么如果http请求一个以py结尾的请求,我需要在哪里处理呢?

  我还可以用什么方法来判断文件。py后缀?

  使用常规匹配?

  其实可以用endswith(文件后缀)来判断处理。

  识别文件名后缀file_name.endswith()的方法。py’)的测试和使用如下:

  In [1]: file_name=time.py

  #匹配的后缀是。html,结果是假的。

  In [3]: file_name.endswith(。html’)

  Out[3]:假

  #如果匹配后缀是。py,是真的。

  In [4]: file_name.endswith(。py’)

  Out[4]:真,那我们就可以写这里判断的处理分支了。

  运行测试:

  首先请求HTML等静态资源页面。

  请求动态资源页面

  然后在收到动态资源请求时,将返回的数据写入浏览器。

  让我们简单地写一串关于当前服务器时间的HTML。

  if file_path.endswith(。py’):

  #请求动态资源

  Print(这是对动态资源的请求!)

  #设置返回的头信息头

  response _ headers= http/1.1 200 ok \ r \ n # 200表示已经找到该资源。

  Response_headers=\r\n #正文中有一个空行

  #设置返回给浏览器的正文内容。

  response_body= h1你好,我是xxx.py /h1 br

  response_body=time.ctime()

  response=response_headers响应_正文

  #向浏览器返回数据

  client _ socket . send(response . encode( UTF-8 ))运行测试以查看:

  动态页面的内容可以从这里正常返回。

  思考:如果在web服务器上连续编写动态处理页面的代码,代码会很庞大。可以拆分写在另一个模块里吗?

  这就涉及到web服务器和业务处理服务器之间的一个协议,这个业界通用的协议就是WSGI协议。

  为什么需要WSGI协议?在讲WSGI协议之前,我把处理动态页面的功能拆分到另一个模块文件中。

  创建一个处理业务的框架模块,复制刚刚处理好的代码返回浏览器:

  导入时间

  定义应用程序(客户端套接字):

  #请求动态资源

  Print(这是对动态资源的请求!)

  #设置返回的头信息头

  response _ headers= http/1.1 200 ok \ r \ n # 200表示已经找到该资源。

  Response_headers=\r\n #正文中有一个空行

  #设置返回给浏览器的正文内容。

  response_body= h1你好,我是xxx.py /h1 br

  response_body=time.ctime()

  response=response_headers响应_正文

  #向浏览器返回数据

  client _ socket . send(response . encode( UTF-8 ))然后在原来的webserver.py模块中,只需导入模块文件,使用application()方法处理刚才的业务即可。

  web.py模块的操作如下

  好了,完成这个解耦操作后,让我们运行测试:

  从上面的通话结果来看,确实通话成功。理解如下:

  但是可以看出,如果webserver要调用框架处理业务,就得这样写,如下:

  框架。application (client _ socket)方法是可行的,但在业界并不常见。也就是说,这个调用方法是扔给别人写的框架,是不兼容的。

  举个例子,假设我后面用Django和Flask框架处理业务,这绝对不是沟通调用的方式。

  那么以什么方式呢?

  是否可以修改服务器和架构代码,保证多种架构下服务器和web服务器的通信?

  答案是python web服务器网关接口(简称WSGI,读作“wizgy”)。

  WSGI,我来了。

  WSGI协议的引入WSGI允许开发者将web框架的选择与web服务器分开。您可以混合搭配web服务器和web框架,并选择合适的配对。例如,您可以在Gunicorn或Nginx/uWSGI或waste上运行Django、Flask或Pyramid。真正的混合匹配是由于WSGI对服务器和架构的支持:

  web服务器必须有WSGI接口,所有现代的Python Web框架都已经有了WSGI接口,可以让你在不修改代码的情况下,让服务器和特色Web框架协同工作。

  WSGI由web server支持,web framework允许你选择适合自己的一对,但也为服务器和框架开发者提供了便利,让他们专注于自己喜欢的领域和专长,而不是互相牵制。其他语言也有类似的接口:java有Servlet API,Ruby有Rack。

  好像很厉害!

  定义WSGI接口WSGI接口定义非常简单。它只需要Web开发人员实现一个函数来响应HTTP请求。

  我们来看看最简单的网页版《Hello World!

  定义应用程序(环境,启动响应):

  start_response(200 OK ,[(Content-Type , text/html)])

  返回“你好,世界!”上面的application()函数是一个符合WSGI标准的HTTP处理函数。它接收两个参数:

  Environ:包含所有HTTP请求信息的dict对象;Start_response:发送HTTP响应的函数。整个application()函数本身并不涉及任何HTTP解析部分,也就是说,底层web服务器的解析部分与应用逻辑部分是分离的,这样开发者就可以专心于一个领域。

  但是等等,这个application()函数怎么调用呢?如果我们自己调用,就不能提供environ和start_response两个参数,返回的str也不能发送到浏览器。

  因此,application()函数必须由WSGI服务器调用。有许多服务器符合WSGI规范。此时我们的web服务器项目的目的是成为一个可以分析静态和动态web页面的WSGI服务器。

  说了这么多,敢不敢秀一波代码操作?

  写框架支持WSGI协议,实现hello worldframwork.py的浏览器显示:

  直接复制协议规范代码。

  然后在webserver.py的部分,需要接受application返回的信息。

  首先,start_response在framwork中设置http请求头信息。而return是返回http请求体信息。

  所以知道了这两点之后,接下来该怎么做。就是想办法接受这个应用程序的头和主体信息。

  那么如何应对呢?

  webserver.py

  要比较这两个文件的代码,请使用pycharm同时打开两个视图窗口。

  好了,我们继续看。

  让我们创建这两个参数:

  Environ:包含所有HTTP请求信息的dict对象;Start_response:发送HTTP响应的函数。

  写一个空白处,填入WSGI规范要求的参数。

  response_body可以由return的返回值接收。

  那么response_header应该怎么处理呢?

  从代码中可以看出,start_response是在从webserver.py传递到framwork.py的应用程序中调用的

  在应用程序中,报头信息被直接设置到start_response的参数中。那我是不是可以在webserver.py直接拿出来拼接成头信息?

  写入start_response以接收报头信息。

  然后先写一个类变量来保存信息,然后测试并打印出来。

  运行它并查看一下:

  那么只要保存在self.application_header中,我就可以在类方法中的任何地方进行拆分或拼接,形成所需的http头返回值。

  写的如下:

  运行测试看看。

  好了,你已经得到了完整的标题内容。然后拼接正文内容,返回浏览器显示。

  按如下方式运行测试:

  哦,这么说成功了?

  不,我确定。如果我请求返回一个页面,我如何返回一个hello world?

  下一步肯定是能够正常返回到index.py这样的正常页面。但是这一章也很长,

  下一章继续。

  本次开发的完整代码如下:webserver.py

  #编码=utf-8

  从套接字导入*

  进口re

  导入多重处理

  导入时间

  导入框架

  类web服务器:

  def __init__(self):

  #创建套接字

  self . server _ socket=socket(AF _ INET,SOCK_STREAM)

  #先设置服务器关闭后立即释放资源,即服务器波动4次,保证下次运行程序时能立即绑定7788端口。

  self . server _ SOCKET . setsockopt(SOL _ SOCKET,SO_REUSEADDR,1)

  #设置服务器提供的服务的端口号。

  self.server_socket.bind(( ,7788))

  #使用socket创建的socket的默认属性是active,使用listen监听连接将其更改为passive。

  self . server _ socket . listen(128)#最多可以侦听128个连接

  def start_http_service(self):

  #打开while循环以处理访问请求。

  虽然正确:

  #如果有一个新的客户端链接到服务器,那么将创建一个新的套接字来服务这个客户端。

  # client_socket用于服务此客户端。

  # self.server_socket可以被保存以等待其他新的客户端连接,而True:

  client_socket,client addr=self . server _ socket . accept()

  # handle_client(客户端套接字)

  #设置子流程

  new_process=多重处理。进程(target=self.handle_client,args=(client_socket,))

  New_process.start() #启动子进程

  #因为子进程已经复制了父进程的套接字和其他资源,所以父进程不会通过调用close来关闭它们对应的链接。

  client _套接字. close()

  定义handle_client(self,client_socket):

  “为客户服务”

  #接收对方发送的数据

  recv _ data=client _ socket . recv(1024)。Decode (UTF-8) # 1024表示这次接收的最大字节数

  #打印从客户端发送的数据内容

  #print(客户端接收:,接收数据)

  request _ header _ lines=recv _ data . split lines()

  对于请求标题行中的行:

  打印(行)

  #返回浏览器数据

  #设置内容正文

  #使用常规匹配来匹配文件路径。

  打印(-,request_header_lines[0])

  打印(文件路径-,。/html/ re.match(r[^/]/([^\s]*),request_header_lines[0]).组(1))

  /([^\s]*),request_header_lines[0 re.match(r[^/])

  如果ret:

  文件路径=。/html/ ret.group(1)

  if file_path==。/html/:

  文件路径=。/html/index.html

  打印(文件路径*******,文件路径)

  #判断file_path是否是py文件的后缀,如果是,请求动态资源,否则,请求静态资源。

  if file_path.endswith(。py’):

  # framework.application(客户端套接字)

  #支持WGSI协议的呼叫模式

  environ={}

  response _ body=framework . application(environ,self.start_response)

  #设置返回的头信息头

  # 1.拼接第一行HTTP/1.1 200没问题换行符内容

  response _ headers= HTTP/1.1 self。application _ header[0] \ r \ n

  # 2.循环拼接第二行或者多行元组内容:内容类型:文本/html

  对于self.application_header[1]中的var:

  response _ headers=var[0]: var[1] \ r \ n

  # 3.空一行与身体隔开

  response_headers=\r\n

  # 4.打印看看页眉的内容信息

  打印( response_header=)

  打印(响应标题)

  # 设置返回的浏览器的内容

  响应=响应标题响应_正文

  客户端_套接字。发送(响应。编码( utf-8 ))

  否则:

  # 请求静态资源

  尝试:

  # 设置返回的头信息页眉

  response _ headers= HTTP/1.1 200 OK \ r \ n # 200表示找到这个资源

  response_headers=\r\n #空一行与身体隔开

  # 读取超文本标记语言文件内容

  文件名=文件路径#设置读取的文件路径

  f=打开(文件名, rb) #以二进制读取文件内容

  response_body=f.read()

  f.close()

  # 返回数据给浏览器

  客户端_套接字。发送(response _ headers。encode( utf-8 )#转码utf-8并派遣数据到浏览器

  客户端套接字发送(响应正文)#转码utf-8并派遣数据到浏览器

  除了:

  # 如果没有找到文件,那么就打印404未找到

  # 设置返回的头信息页眉

  response_headers=HTTP/1.1 404找不到\r\n # 200表示找到这个资源

  response_headers=\r\n #空一行与身体隔开

  response_body= h1对不起,找不到文件/h1

  响应=响应标题响应_正文

  客户端_套接字。发送(响应。编码( utf-8 ))

  定义开始响应(自身、状态、表头):

  自我。application _ header=[状态,标题]

  print(application_header=,self.application_header)

  def main():

  webserver=WebServer()

  webserver.start_http_service()

  if __name__==__main__ :

  main()framework.py

  # 支撑WGSI协议

  定义应用程序(环境,启动响应):

  start_response(200 OK ,[(Content-Type , text/html)])

  返回"你好,世界!"

  关注微信公众号,回复【资料】、Python、PHP、JAVA、web、则可获得Python、PHP、JAVA、前端等视频资料。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: