一 Web应用的组成



接下来我们学习的目的是为了开发一个Web应用程序,而Web应用程序是基于B/S架构的,其中B指的是浏览器,负责向S端发送请求信息,而S端会根据接收到的请求信息返回相应的数据给浏览器,需要强调的一点是:S端由server和application两大部分构成,如图所示:

上图:Web应用组成

二 开发一个Web应用

我们无需开发浏览器(本质即套接字客户端),只需要开发S端即可,S端的本质就是用套接字实现的,如下

  1. # S端
  2. import socket
  3. def make_server(ip, port, app): # 代表server
  4. sock = socket.socket()
  5. sock.bind((ip, port))
  6. sock.listen(5)
  7. print('Starting development server at http://%s:%s/' %(ip,port))
  8. while True:
  9. conn, addr = sock.accept()
  10. # 1、接收浏览器发来的请求信息
  11. recv_data = conn.recv(1024)
  12. # print(recv_data.decode('utf-8'))
  13. # 2、将请求信息直接转交给application
  14. res = app(recv_data)
  15. # 3、向浏览器返回消息(此处并没有按照http协议返回)
  16. conn.send(res)
  17. conn.close()
  18. def app(environ): # 代表application
  19. # 处理业务逻辑
  20. return b'hello world'
  21. if __name__ == '__main__':
  22. make_server('127.0.0.1', 8008, app) # 在客户端浏览器输入:http://127.0.0.1:8008 会报错(注意:请使用谷歌浏览器)

目前S端已经可以正常接收浏览器发来的请求消息了,但是浏览器在接收到S端回复的响应消息b'hello world'时却无法正常解析 ,因为浏览器与S端之间收发消息默认使用的应用层协议是HTTP,浏览器默认会按照HTTP协议规定的格式发消息,而S端也必须按照HTTP协议的格式回消息才行,所以接下来我们详细介绍HTTP协议

HTTP协议详解链接地址:https://www.cnblogs.com/Du704/p/11713916.html



S端修订版本:处理HTTP协议的请求消息,并按照HTTP协议的格式回复消息

  1. # S端
  2. import socket
  3. def make_server(ip, port, app): # 代表server
  4. sock = socket.socket()
  5. sock.bind((ip, port))
  6. sock.listen(5)
  7. print('Starting development server at http://%s:%s/' %(ip,port))
  8. while True:
  9. conn, addr = sock.accept()
  10. # 1、接收并处理浏览器发来的请求信息
  11. # 1.1 接收浏览器发来的http协议的消息
  12. recv_data = conn.recv(1024)
  13. # 1.2 对http协议的消息加以处理,简单示范如下
  14. ll=recv_data.decode('utf-8').split('\r\n')
  15. head_ll=ll[0].split(' ')
  16. environ={}
  17. environ['PATH_INFO']=head_ll[1]
  18. environ['method']=head_ll[0]
  19. # 2:将请求信息处理后的结果environ交给application,这样application便无需再关注请求信息的处理,可以更加专注于业务逻辑的处理
  20. res = app(environ)
  21. # 3:按照http协议向浏览器返回消息
  22. # 3.1 返回响应首行
  23. conn.send(b'HTTP/1.1 200 OK\r\n')
  24. # 3.2 返回响应头(可以省略)
  25. conn.send(b'Content-Type: text/html\r\n\r\n')
  26. # 3.3 返回响应体
  27. conn.send(res)
  28. conn.close()
  29. def app(environ): # 代表application
  30. # 处理业务逻辑
  31. return b'hello world'
  32. if __name__ == '__main__':
  33. make_server('127.0.0.1', 8008, app)

此时,重启S端后,再在客户端浏览器输入:http://127.0.0.1:8008 便可以看到正常结果hello world了。



我们不仅可以回复hello world这样的普通字符,还可以夹杂html标签,浏览器在接收到消息后会对解析出的html标签加以渲染

  1. # S端
  2. import socket
  3. def make_server(ip, port, app):
  4. sock = socket.socket()
  5. sock.bind((ip, port))
  6. sock.listen(5)
  7. print('Starting development server at http://%s:%s/' %(ip,port))
  8. while True:
  9. conn, addr = sock.accept()
  10. recv_data = conn.recv(1024)
  11. ll=recv_data.decode('utf-8').split('\r\n')
  12. head_ll=ll[0].split(' ')
  13. environ={}
  14. environ['PATH_INFO']=head_ll[1]
  15. environ['method']=head_ll[0]
  16. res = app(environ)
  17. conn.send(b'HTTP/1.1 200 OK\r\n')
  18. conn.send(b'Content-Type: text/html\r\n\r\n')
  19. conn.send(res)
  20. conn.close()
  21. def app(environ):
  22. # 返回html标签
  23. return b'<h1>hello web</h1><img src="https://www.baidu.com/img/bd_logo1.png"></img>'
  24. if __name__ == '__main__':
  25. make_server('127.0.0.1', 8008, app)

更进一步我们还可以返回一个文件,例如timer.html,内容如下

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <h2>{{ time }}</h2>
  9. </body>
  10. </html>

S端程序如下

  1. # S端
  2. import socket
  3. def make_server(ip, port, app): # 代表server
  4. sock = socket.socket()
  5. sock.bind((ip, port))
  6. sock.listen(5)
  7. print('Starting development server at http://%s:%s/' %(ip,port))
  8. while True:
  9. conn, addr = sock.accept()
  10. recv_data = conn.recv(1024)
  11. ll=recv_data.decode('utf-8').split('\r\n')
  12. head_ll=ll[0].split(' ')
  13. environ={}
  14. environ['PATH_INFO']=head_ll[1]
  15. environ['method']=head_ll[0]
  16. res = app(environ)
  17. conn.send(b'HTTP/1.1 200 OK\r\n')
  18. conn.send(b'Content-Type: text/html\r\n\r\n')
  19. conn.send(res)
  20. conn.close()
  21. def app(environ):
  22. # 处理业务逻辑:打开文件,读取文件内容并返回
  23. with open('timer.html', 'r', encoding='utf-8') as f:
  24. data = f.read()
  25. return data.encode('utf-8')
  26. if __name__ == '__main__':
  27. make_server('127.0.0.1', 8008, app)

上述S端为浏览器返回的都是静态页面(内容都固定的),我们还可以返回动态页面(内容是变化的)

  1. # S端
  2. import socket
  3. def make_server(ip, port, app): # 代表server
  4. sock = socket.socket()
  5. sock.bind((ip, port))
  6. sock.listen(5)
  7. print('Starting development server at http://%s:%s/' %(ip,port))
  8. while True:
  9. conn, addr = sock.accept()
  10. recv_data = conn.recv(1024)
  11. ll=recv_data.decode('utf-8').split('\r\n')
  12. head_ll=ll[0].split(' ')
  13. environ={}
  14. environ['PATH_INFO']=head_ll[1]
  15. environ['method']=head_ll[0]
  16. res = app(environ)
  17. conn.send(b'HTTP/1.1 200 OK\r\n')
  18. conn.send(b'Content-Type: text/html\r\n\r\n')
  19. conn.send(res)
  20. conn.close()
  21. def app(environ):
  22. # 处理业务逻辑
  23. with open('timer.html', 'r', encoding='utf-8') as f:
  24. data = f.read()
  25. import time
  26. now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
  27. data = data.replace('{{ time }}', now) # 字符串替换
  28. return data.encode('utf-8')
  29. if __name__ == '__main__':
  30. make_server('127.0.0.1', 8008, app) # 在浏览器输入http://127.0.0.1:8008,每次刷新都会看到不同的时间

三 Web框架的由来

综上案例我们可以发现一个规律,在开发S端时,server的功能是复杂且固定的(处理socket消息的收发和http协议的处理),而app中的业务逻辑却各不相同(不同的软件就应该有不同的业务逻辑),重复开发复杂且固定的server是毫无意义的,有一个wsgiref模块帮我们写好了server的功能,这样我们便只需要专注于app功能的编写即可

  1. # wsgiref实现了server,即make_server
  2. from wsgiref.simple_server import make_server
  3. def app(environ, start_response): # 代表application
  4. # 1、返回http协议的响应首行和响应头信息
  5. start_response('200 OK', [('Content-Type', 'text/html')])
  6. # 2、处理业务逻辑:根据请求url的不同返回不同的页面内容
  7. if environ.get('PATH_INFO') == '/index':
  8. with open('index.html','r', encoding='utf-8') as f:
  9. data=f.read()
  10. elif environ.get('PATH_INFO') == '/timer':
  11. with open('timer.html', 'r', encoding='utf-8') as f:
  12. data = f.read()
  13. import time
  14. now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
  15. data = data.replace('{{ time }}', now) # 字符串替换
  16. else:
  17. data='<h1>Hello, web!</h1>'
  18. # 3、返回http响应体信息,必须是bytes类型,必须放在列表中
  19. return [data.encode('utf-8')]
  20. if __name__ == '__main__':
  21. # 当接收到请求时,wsgiref模块会对该请求加以处理,然后后调用app函数,自动传入两个参数:
  22. # 1 environ是一个字典,存放了http的请求信息
  23. # 2 start_response是一个功能,用于返回http协议的响应首行和响应头信息
  24. s = make_server('', 8011, app) # 代表server
  25. print('监听8011')
  26. s.serve_forever() # 在浏览器输入http://127.0.0.1:8011/index和http://127.0.0.1:8011/timer会看到不同的页面内容

timer.html已经存在了,新增的index.html页面内容如下:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <h1>主页</h1>
  9. </body>
  10. </html>

上述案例中app在处理业务逻辑时需要根据不同的url地址返回不同的页面内容,当url地址越来越多,需要写一堆if判断,代码不够清晰,耦合程度高,所以我们做出以下优化

  1. # 处理业务逻辑的函数
  2. def index(environ):
  3. with open('index.html', 'r', encoding='utf-8') as f:
  4. data = f.read()
  5. return data.encode('utf-8')
  6. def timer(environ):
  7. import datetime
  8. now = datetime.datetime.now().strftime('%y-%m-%d %X')
  9. with open('timer.html', 'r', encoding='utf-8') as f:
  10. data = f.read()
  11. data = data.replace('{{ time }}', now)
  12. return data.encode('utf-8')
  13. # 路径跟函数的映射关系
  14. url_patterns = [
  15. ('/index', index),
  16. ('/timer', timer),
  17. ]
  18. from wsgiref.simple_server import make_server
  19. def app(environ, start_response):
  20. start_response('200 OK', [('Content-Type', 'text/html')])
  21. # 拿到请求的url并根据映射关系url_patters执行相应的函数
  22. reuqest_url = environ.get('PATH_INFO')
  23. for url in url_patterns:
  24. if url[0] == reuqest_url:
  25. data = url[1](environ)
  26. break
  27. else:
  28. data = b'404'
  29. return [data]
  30. if __name__ == '__main__':
  31. s = make_server('', 8011, app)
  32. print('监听8011')
  33. s.serve_forever()

随着业务逻辑复杂度的增加,处理业务逻辑的函数以及url_patterns中的映射关系都会不断地增多,此时仍然把所有代码都放到一个文件中,程序的可读性和可扩展性都会变得非常差,所以我们应该将现有的代码拆分到不同文件中

插图:

  1. mysite # 文件夹
  2. ├── app01 # 文件夹
  3. └── views.py
  4. ├── mysite # 文件夹
  5. └── urls.py
  6. └── templates # 文件夹
  7. ├── index.html
  8. └── timer.html
  9. ├── main.py

views.py 内容如下:

  1. # 处理业务逻辑的函数
  2. def index(environ):
  3. with open('templates/index.html', 'r',encoding='utf-8') as f: # 注意文件路径
  4. data = f.read()
  5. return data.encode('utf-8')
  6. def timer(environ):
  7. import datetime
  8. now = datetime.datetime.now().strftime('%y-%m-%d %X')
  9. with open('templates/timer.html', 'r',encoding='utf-8') as f: # 注意文件路径
  10. data = f.read()
  11. data=data.replace('{{ time }}',now)
  12. return data.encode('utf-8')

urls.py内容如下:

  1. # 路径跟函数的映射关系
  2. from app01.views import * # 需要导入views中的函数
  3. url_patterns = [
  4. ('/index', index),
  5. ('/timer', timer),
  6. ]

main.py 内容如下:

  1. from wsgiref.simple_server import make_server
  2. from mysite.urls import url_patterns # 需要导入urls中的url_patterns
  3. def app(environ, start_response):
  4. start_response('200 OK', [('Content-Type', 'text/html')])
  5. # 拿到请求的url并根据映射关系url_patters执行相应的函数
  6. reuqest_url = environ.get('PATH_INFO')
  7. for url in url_patterns:
  8. if url[0] == reuqest_url:
  9. data = url[1](environ)
  10. break
  11. else:
  12. data = b'404'
  13. return [data]
  14. if __name__ == '__main__':
  15. s = make_server('', 8011, app)
  16. print('监听8011')
  17. s.serve_forever()

至此,我们就针对application的开发自定义了一个框架,所以说框架的本质就是一系列功能的集合体、不同的功能放到不同的文件中。有了该框架,可以让我们专注于业务逻辑的编写,极大的提高了开发web应用的效率(开发web应用的框架可以简称为web框架),比如我们新增一个业务逻辑,要求为:浏览器输入http://127.0.0.1:8011/home 就能访问到home.html页面,在框架的基础上具体开发步骤如下:

步骤一:在templates文件夹下新增home.html

步骤二:在urls.py的url_patterns中新增一条映射关系

  1. url_patterns = [
  2. ('/index', index),
  3. ('/timer', timer),
  4. ('/home', home), # 新增的映射关系
  5. ]

步骤三:在views.py中新增一个名为home的函数

  1. def home(environ):
  2. with open('templates/home.html', 'r',encoding='utf-8') as f:
  3. data = f.read()
  4. return data.encode('utf-8')

我们自定义的框架功能有限,在Python中我们可以使用别人开发的、功能更强大的Django框架

四 Django框架的安装与使用

在使用Django框架开发web应用程序时,开发阶段同样依赖wsgiref模块来实现Server的功能,我们使用Django框架是为了快速地开发application

4.1 安装

目前在企业开发中Django框架使用的主流版本为1.11.x版本,最新版本为2.x,我们主要讲解1.11版本,同时会涉及2.x的新特性

  1. pip3 install django==1.11.18 # 在命令行执行该命令

4.2 使用

4.2.1 快速创建并启动Django项目



如果使用的是我们自定义的框架来开发web应用,需要事先生成框架包含的一系列基础文件,然后在此基础上进行开发。

如果使用的是Django框架来开发web应用,同样需要事先生成Django框架包含的一系列基础文件,然后在此基础上进行开发。

但Django框架更为方便的地方在于它已经为我们提供了一系列命令来帮我们快速地生成这一系列基础文件

  1. # 在命令行执行以下指令,会在当前目录生成一个名为mysite的文件夹,该文件夹中包含Django框架的一系列基础文件
  2. django-admin startproject mysite

创建功能模块

  1. cd mysite # 切换到mysite目录下,执行以下命令
  2. python manage.py startapp app01 # 创建功能模块app01,此处的startapp代表创建application下的一个功能模块。例如我们要开发application是京东商城,京东商城这个大项目下有一个订单管理模块,我们可以将其命名为app01

运行

  1. python manage.py runserver 8001 # 在浏览器输入:http://127.0.0.1:8001 会看到Django的欢迎页面。

4.2.2 Django项目目录结构



截目录树的图(按照下述目录截图)

  1. mysite # 文件夹
  2. ├── app01 # 文件夹
  3. └── migrations # 文件夹
  4. └── admin.py
  5. └── apps.py
  6. └── models.py
  7. └── tests.py
  8. └── views.py
  9. ├── mysite # 文件夹
  10. └── settings.py
  11. └── urls.py
  12. └── wsgi.py
  13. └── templates # 文件夹
  14. ├── manage.py

关键文件介绍

  1. -manage.py---项目入口,执行一些命令
  2. -项目名
  3. -settings.py 全局配置信息
  4. -urls.py 总路由,请求地址跟视图函数的映射关系
  5. -app名字
  6. -migrations 数据库迁移的记录
  7. -models.py 数据库表模型
  8. -views.py 处理业务逻辑的函数,简称视图函数

4.2.3 基于Pycharm创建Django项目

4.2.4 基于Django实现的一个简单示例

(1)url.py
  1. from django.contrib import admin
  2. from django.conf.urls import url
  3. #导入views模块
  4. from app01 import views
  5. urlpatterns = [
  6. url(r'^admin/', admin.site.urls),
  7. # r'^index/$' 会正则匹配url地址的路径部分
  8. url(r'^index/$',views.index), # 新增地址http://127.0.0.1:8001/index/与index函数的映射关系
  9. ]
(2)视图
  1. from django.shortcuts import render
  2. # 必须定义一个request形参,request相当于我们自定义框架时的environ参数
  3. def index(request):
  4. import datetime
  5. now=datetime.datetime.now()
  6. ctime=now.strftime("%Y-%m-%d %X")
  7. return render(request,"index.html",{"ctime":ctime}) # render会读取templates目录下的index.html文件的内容并且用字典中的ctime的值替换模版中的{{ ctime }}
(3)模版

在templates目录下新建文件index.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <h4>当前时间:{{ ctime }}</h4>
  9. </body>
  10. </html>

测试:

  1. python manage.py runserver 8001 # 在浏览器输入:http://127.0.0.1:8001/index/ 会看到当前时间。

4.2.5 Django框架的分层与请求生命周期

综上,我们使用Django框架就是为了开发application,而application的工作过程本质就是根据不同的请求返回不同的数据,Django框架将这个工作过程细分为如下四层去实现

1、路由层(根据不同的地址执行不同的视图函数,详见urls.py)

2、视图层(定义处理业务逻辑的视图函数,详见views.py)

3、模型层 (跟数据库打交道的,详解models.py)

4、模板层(待返回给浏览器的html文件,详见templates)

django请求生命周期



这体现了一种解耦合的思想,下面我们开始详细介绍每一层

Django开篇 - Web应用的更多相关文章

  1. [Python] 利用Django进行Web开发系列(一)

    1 写在前面 在没有接触互联网这个行业的时候,我就一直很好奇网站是怎么构建的.现在虽然从事互联网相关的工作,但是也一直没有接触过Web开发之类的东西,但是兴趣终归还是要有的,而且是需要自己动手去实践的 ...

  2. [Python] 利用Django进行Web开发系列(二)

    1 编写第一个静态页面——Hello world页面 在上一篇博客<[Python] 利用Django进行Web开发系列(一)>中,我们创建了自己的目录mysite. Step1:创建视图 ...

  3. Nginx+Python+uwsgi+Django的web开发环境安装及配置

    Nginx+Python+uwsgi+Django的web开发环境安装及配置 nginx安装 nginx的安装这里就略过了... python安装 通常系统已经自带了,这里也略过 uwsgi安装 官网 ...

  4. 利用Django进行Web开发

    Web就是用来表示Internet主机上供外界访问的资源的.网页也统称为web资源.Internet上供外界访问的Web资源主要分为如下两类: 静态web资源:指web页面中供人们浏览的数据始终是不变 ...

  5. Python第十三天 django 1.6 导入模板 定义数据模型 访问数据库 GET和POST方法 SimpleCMDB项目 urllib模块 urllib2模块 httplib模块 django和web服务器整合 wsgi模块 gunicorn模块

    Python第十三天   django 1.6   导入模板   定义数据模型   访问数据库   GET和POST方法    SimpleCMDB项目   urllib模块   urllib2模块 ...

  6. web理论知识--网页访问过程(附有Django的web项目访问流程)

    当我们闲暇之余想上网看看新闻,或者看个电影,通常的操作是:打开电脑.打开浏览器.输入网址.浏览页面信息.点击自己感兴趣的连接......那么有没有想过,这些网页从哪里来的?过程中计算机又做了什么事情了 ...

  7. Web框架本质及第一个Django实例 Web框架

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

  8. 利用Django构建web应用及其部署

    注:很久之前就有了学习Django的想法,最近终于有机会做了一次尝试.由于Django的详细教程很多,我在这里就不再详述了,只是将整个开发流程以及自己在学习Django中的一些思考记录在此. Syst ...

  9. Nginx+uwsgi+Django 的web应用环境部署-完整记录

    Python作为当前最火爆最热门,也是最主要的Web开发语言之一,在其二十多年的历史中出现了数十种Web框架,比如Django.Tornado.Flask.Twisted.Bottle和Web.py等 ...

随机推荐

  1. MyBatis运行流程及入门第一个程序

    1. mybatis是什么? MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并 ...

  2. centos7上安装redis以及PHP安装redis扩展(一)

    1.关闭防火墙: systemctl stop firewalld.service #停止firewall systemctl disable firewalld.service #禁止firewal ...

  3. WPF中DataTemplateSelector的简单应用

    WPF中DataTemplateSelector的简单应用 DataTemplateSelector中文叫数据模板选择器,根据数据模型内的属性值选择不同的数据模板,多用于容器如listbox中,达到同 ...

  4. 分享一个我自己做的 Excel 万年历

    下载链接在此. 纯 Excel 公式实现,带农历,可自定义节日.配色. 带有紧凑日历和记事日历两种日历,均可直接 Ctrl+P 打印,且打印时不带有顶部的控制栏.

  5. MQ系列(0)——MQ简介

    mq简介 mq 就是消息队列(Message Queue).想必大家对队列的数据结构已经很熟悉了,消息队列可以简单理解为:把要传输的数据放在队列中,mq 就是存放和发送消息的这么一个队列中间件.在消息 ...

  6. MVC、MVP、MVVM模型

    在学习vue.react的过程中,总能看到MVVM模型,那么MVVM究竟是什么,下面将我最近看到的资料以及自己的想法总结一下. 与MVVM相似的,还有MVC.MVP,先从MVC.MVP这两个入手,方面 ...

  7. 3.kubernetes的CNI网络插件-Flannel

    目录 1.1.K8S的CNI网络插件-Flannel 1.1.1.集群规划 1.1.2.下载软件.解压.软链接 1.1.3.最终目录结构 1.1.4.拷贝证书 1.1.5.创建配置 1.1.6.创建启 ...

  8. Linux监控CPU,内存,磁盘I/O

    简单讲讲Linux下监控 [CPU] 监控CPU,top命令能够实时监控系统的运行状态,并且可以按照CPU.内存和执行时间进行排序,同时top命令还可以通过交互式命令进行设定显示,通过top命令可以查 ...

  9. 如何运行Spring Boot项目

    背景 帮别人指导一个Spring Boot项目,它在本地把项目push到git服务器上,然后在部署的服务器上把代码pull下来(我猜应该是这个流程) 然后他问我这项目怎么运行? 我当时就懵了,因为我平 ...

  10. 4 个好用的 Linux 监控工具

    下面是 Linux 下 4 个日常使用率非常高的监控工具,可以帮助我们准确快速的诊断系统问题. 1. iotop 如果你想知道某些进程使用了多少你宝贵的 I/O 资源,那么就使用 iotop 吧. i ...