web应用与web框架本质

概念

什么是web应用程序呢? Web应用程序就一种可以通过互联网来访问资源的应用程序, 用户可以只需要用一个浏览器而不需要安装其他程序就可以访问自己需要的资源.

应用软件通常有两种架构: B/S架构和传统的C/S架构. C/S架构是客户端/服务端程序, 用户需要访问服务器需要下载单独的客户端, 而B/S则是浏览器/服务端应用程序, 用户只需要选择兼容的合适的浏览器, 如IE, Chrome, Firefox等等来运行即可. Web应用程序通常就是属于B/S架构 ,这也是现在的主流软件架构.

Web应用程序是基于网络进行传输的, 而在网络上传输就需要通过socket, 一般都是通过TCP/IP协议来进行通信的, 因此我们可以这样理解Web应用, 浏览器就是Socket客户端程序, 而Web应用程序就是Socket服务端程序.

最简易socket服务端

有了这个认识, 客户端程序不需要我们负责, 我们就可以基于socket专心搭建一个服务端就可以完成简易版的Web应用了.

import socket

server = socket.socket()
server.bind(('localhost', 8080))
server.listen(5) while True:
# 开启接收客户端的程序
conn, addr = server.accept()
data = conn.recv(1024)
# 这里打印看来自服务端
print(data)
# 返回响应信息
response = 'Hello World!!!'
conn.send(response.encode('utf-8'))
conn.close() # 关闭连接

基于简单的socket, 我们接受了来自服务端的请求, 获得了如下的请求信息

b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36\r\nSec-Fetch-Mode: navigate\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3\r\nSec-Fetch-Site: cross-site\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\nCookie: csrftoken=mUcuvpw1xeYvCqI0VUWuxOEDORAMVOZ1JHAkJXXEiajSi8KisZx6sTRke9H3AEyf\r\n\r\n'

我们返回了信息给浏览器, 浏览器的显示是

HTTP数据格式

这说明我们发送给浏览器的响应式无效的, Web应用程序想要在网络中完整的传输, 就一定需要遵从一定的通信协议,而在Web端这个应用层协议就是HTTP协议.

HTTP协议是有属于它自己的传输数据的格式的, HTTP的数据格式包含以下部分:

  • 请求首行 协议版本, 请求方式
  • 请求头 包含多个键值对形式的请求信息
  • \r\n
  • 请求体

服务端的响应格式也与之相对应:

  • 响应首行 协议版本, 状态码
  • 响应头 包含多个键值对形式的响应信息
  • \r\n
  • 响应体

返回正确响应

了解了HTTP的基本数据格式之后, 只要我们发送合法的响应信息, 就能和浏览器做一个基本的通信了, 然后继续修改上述的代码.

...
# 返回响应头
response = 'HTTP/1.1 200 OK\r\n\r\n'
# 返回响应体
response += '<h1>Hello World!!!</h1>'
...

只需要在响应加上响应首行和响应头信息, 并且我们可以在响应体中添加HTML标签, 这就可以让浏览器接收信息并正常解析出我们的响应信息了.

有了以上基础, 我们可以再修改代码, 让服务端返回一个html文件, 并在浏览器端正确渲染出来

...
# 返回响应头
response = 'HTTP/1.1 200 OK\r\n\r\n'
# 返回响应体
# response += '<h1>Hello World!!!</h1>'
with open('index.html', 'r', encoding='utf-8') as f:
response += f.read()
...
index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
<h1 style="color: red">This is page index!</h1>
</body>
</html>

返回动态的页面

最后浏览器返回了上面的结果. 但是现在又有了新的需求, 这样每次都是返回固定的静态页面, 我们需要返回一个动态的页面. 这就希望页面不要被写死, 而需要动态的获取参数来渲染出响应的页面.就以下面这个动态获取当前的时间页面为例.

...
with open('time.html', 'r', encoding='utf-8') as f:
response += f.read()
response = response.replace('{{ now }}', time.strftime('%Y-%m-%d %X'))
conn.send(response.encode('utf-8'))
...
time.html
        
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>time</title>
</head>
<body>
<h1 style="color: red">This is time page!</h1>
<p>Now time is {{ now }} !</p>
</body>
</html>

最后的效果如下所示, 每次请求都显示不同的时间. 这里的思想也很容易, 就是用字符串的替换, 把特殊的字符换成我们最后需要

根据不同请求返回不同页面

现在达成了动态网页的需求, 我们的新需求又来了, 需要能根据用户敲入的不同网址返回不同的信息, 而这就需要我们对来自浏览器的请求进行分析, 请求首行包含了我们需要的请求地址的信息. 这就需要我们对来自浏览器的请求体进行字符串的切割处理了, 来获取我们需要的数据.

import socket
import time server = socket.socket()
server.bind(('localhost', 8080))
server.listen(5) def index():
with open('index.html', 'r', encoding='utf-8') as f:
return f.read() def get_time():
with open('time.html', 'r', encoding='utf-8') as f:
html = f.read()
return html.replace('{{ now }}', time.strftime('%Y-%m-%d %X')) while True:
# 开启接收客户端的程序
conn, addr = server.accept()
data = conn.recv(1024)
# 这里打印看来自服务端
print(data)
# 这里进行字符串的切割, 先以换行符来切割, 在以空格切割
path = data.decode('utf-8').split('\n')[0].split(' ')[1]
# print(path)
# 获取了路径之后, 我们就能够根据不同的请求路径返回不同的信息了.
response = 'HTTP/1.1 200 OK\r\n\r\n'
if path == '/index':
response += index()
elif path == '/time':
response += get_time()
else:
response += '404 error' conn.send(response.encode('utf-8'))
conn.close() # 关闭连接

我们的简易web服务端到了这已经有了点雏形了, 但是还不能支持并发, 并且我们还发现了前面的socket程序代码也是固定不变的, 处理浏览器请求头的过程是固定的, 返回的形式也是固定的, 而我们也只是简单的处理了请求路径的信息, 如果还需要其他信息, 就又要进行字符串的切割处理, 因此这样的处理工作是重复的, Python的原则是不要重复造轮子, 接下来就可以利用Python内置的wsgiref模块来完成前面这些固定的处理流程.

wsgi

wsgiref模块是一个遵从WSGI(web server gateway interface, web服务网关接口)协议, 那么什么是WSGI协议呢?在我们真实的生产环境中, 一般分为服务器程序和web应用程序.

  • 服务器程序 封装处理socket层面, 处理的是HTTP协议这一层的, 对请求的数据做处理, 然后交给应用程序处理
  • 应用程序则是负责具体的业务逻辑, 对来自浏览器的请求做业务逻辑层面的处理, 并返回相应的结果或页面返回.

在没有 WSGI 规范之前,一个服务器调度 Python 应用是用这种方式,另一款服务器使用的是那种方式,这样的话,编写出来的应用部署时只能选择局限的某个或某些服务器,达不到通用的效果。

所以,WSGI 的出现就是为了解决上面的问题,它规定了服务器怎么把请求信息告诉给应用,应用怎么把执行情况回传给服务器,这样的话,服务器与应用都按一个标准办事,只要实现了这个标准,服务器与应用随意搭配就可以,灵活度大大提高。

下面的图片说明了wsgi的工作流程.

首先浏览器发送请求到服务端, 服务端对数据进行处理, 并把请求信息封装到environ字典中, 并调用一个应用程序(这通常是一个可调用对象)来处理业务逻辑请求, 当应用程序处理完业务逻辑之后, 会调用start_response这个回调函数来发送状态信息, 响应头部的信息和可能出现的异常错误信息. 发送完这个数据响应信息之后, 最后再返回一个可迭代对象(可以是字符串, 字典, 列表...)的数据信息给服务器程序. 最后再返回给客户端.

基于wsgiref模块的web程序

了解了什么是wsgi协议之后, 我们接下来就可以利用wsgiref模块来修改上面的应用程序了.

import time
from wsgiref import simple_server # 视图部分, 具体的处理逻辑
def index():
with open('index.html', 'r', encoding='utf-8') as f:
return f.read() def get_time():
with open('time.html', 'r', encoding='utf-8') as f:
html = f.read()
return html.replace('{{ now }}', time.strftime('%Y-%m-%d %X')) def error():
"""请求路径不能匹配返回的信息"""
return '404 error' urls = [
('/index', index),
('/time', get_time)
] def run(environ, start_response):
"""
我们的app应用的入口函数
:param environ: 服务器处理过后的请求参数都包含在里面了, 包含请求路径信息等
:param start_response: 处理完数据后调用的响应回调函数
""" # 根据请求信息的不同, 来到不同的视图函数进行业务逻辑的处理
path = environ.get('PATH_INFO') # 包含了请求的路径信息
func = None
for url, f in urls:
if url == path:
func = f
break
# 如果url全部不匹配返回错误页面的信息
res = func() if func else error() # 数据处理完毕, 这里要调用响应的回调函数, 进行处理, 响应头信息
start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])
# 最后返回可迭代数据信息
return [res.encode('utf-8'), ] if __name__ == '__main__':
# 创建一个服务器应用程序, 绑定ip端口和要调用的app入口函数
server = simple_server.make_server('localhost', 8080, run)
server.serve_forever() # 服务器永远启动着

上面的wsgiref模块实现的服务器严格的按照了wsgi协议, 我们自己实现的应用程序也遵守了wsgi接口规范, 这就让双方可以完美对接, 服务器程序和应用程序亦可以很大程度的实现解耦.

Django 简介

介绍

Django是一个由Python编写的具有完整架站能力的开源Web框架。使用Django,只要很少的代码,Python的程序开发人员就可以轻松地完成一个正式网站所需要的大部分内容,并进一步开发出全功能的Web服务。

Django本身基于MVC模型,即Model(模型)+View(视图)+ Controller(控制器)设计模式,因此天然具有MVC的出色基因:开发快捷、部署方便、可重用性高、维护成本低等。Python加Django是快速开发、设计、部署网站的最佳组合。

特点

Django具有以下特点:

  • 功能完善、要素齐全:该有的、可以没有的都有,常用的、不常用的工具都用。Django提供了大量的特性和工具,无须你自己定义、组合、增删及修改。但是,在有些人眼里这被认为是‘臃肿’不够灵活,发挥不了程序员的主动能力。
  • 完善的文档:经过十多年的发展和完善,Django有广泛的实践经验和完善的在线文档, 开发者遇到问题时可以搜索在线文档寻求解决方案。
  • 强大的数据库访问组件:Django的Model层自带数据库ORM组件,使得开发者无须学习其他数据库访问技术(SQL、pymysql、SQLALchemy等)。当然你也可以不用Django自带的ORM,而是使用其它访问技术,比如SQLALchemy。
  • 灵活的URL映射:Django使用正则表达式管理URL映射,灵活性高。
  • 丰富的Template模板语言:类似jinjia2模板语言,不但原生功能丰富,还可以自定义模板标签。
  • 自带免费的后台管理系统:只需要通过简单的几行配置和代码就可以实现一个完整的后台数据管理控制平台。
  • 完整的错误信息提示:在开发调试过程中如果出现运行错误或者异常,Django可以提供非常完整的错误信息帮助定位问题。

参考

  1. 参考1

  2. 参考2

django框架简介及自定义简易版框架的更多相关文章

  1. Layui框架+PHP打造个人简易版网盘系统

    网盘系统   大家应该都会注册过致命的一些网盘~如百度云.百科介绍:网盘,又称网络U盘.网络硬盘,是由互联网公司推出的在线存储服务,服务器机房为用户划分一定的磁盘空间,为用户免费或收费提供文件的存储. ...

  2. 手动搭建简易web框架与django框架简介

    目录 纯手写简易web框架 基于wsgiref模块 动静态网页 简单了解jinja2模块 框架请求流程 python主流web框架 django框架 简介 应用app 命令操作django pycha ...

  3. python 之 Django框架(Django框架简介、视图装饰器、request对象、Response对象)

    12.33 Django框架简介: MVC,全名是Model View Controller,是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model).视图(View)和控制器( ...

  4. python主流框架简介和Django框架的使用

    目录 一.手撸简易web框架 二.动静态网页 1. 静态网页 2. 动态网页 三.jinja2模板语法 1. jinja2的作用 四.python主流web框架 1. django 3. tornad ...

  5. Django框架简介,wsgiref 与 jinja2 模块

    目录 框架简介 wsgiref模块 jinja2 模块 框架简介 Django是一个web开发框架,用来开发web应用,本质就是, web框架+socket服务端 MVC框架和MTV框架 MVC,全名 ...

  6. Django框架简介及模板Template,filter

    Django框架简介 MVC框架和MTV框架 MVC,全名是Model View Controller,是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model).视图(View) ...

  7. 闭关修炼180天--手写持久层框架(mybatis简易版)

    闭关修炼180天--手写持久层框架(mybatis简易版) 抛砖引玉 首先先看一段传统的JDBC编码的代码实现: //传统的JDBC实现 public static void main(String[ ...

  8. C#基于Mongo的官方驱动手撸一个Super简易版MongoDB-ORM框架

    C#基于Mongo的官方驱动手撸一个简易版MongoDB-ORM框架 如题,在GitHub上找了一圈想找一个MongoDB的的ORM框架,未偿所愿,就去翻了翻官网(https://docs.mongo ...

  9. wsgiref模块、web框架、django框架简介

    """web框架:将前端.数据库整合到一起的基于互联网传输的python代码 web框架也可以简单的理解为是软件开发架构里面的'服务端'""" ...

随机推荐

  1. Android H5混合开发(2):自定义Cordova插件

    前言 Cordova虽然定义了很多基础的插件,供H5端使用原生设备的功能. 但是,如果业务相关的功能,需要提供给H5端使用,那么,就需要我们自定义插件了. 这个"自定义"不是指由A ...

  2. Java面试官最爱问的volatile关键字

    在Java的面试当中,面试官最爱问的就是volatile关键字相关的问题.经过多次面试之后,你是否思考过,为什么他们那么爱问volatile关键字相关的问题?而对于你,如果作为面试官,是否也会考虑采用 ...

  3. [考试反思]1018csp-s模拟测试78(lrd day2) :规律

    zkt没素质果然考炸了! 但是他考炸了和我一个分 这场的状态是真的不好,T3比较简单但没有做,一直干T2结果还是跪了 T1的哈希写挂了,模数比int大了结果一乘就炸long long了. 调了一个小时 ...

  4. [考试反思]0908NOIP模拟测试40:颠簸

    怎么说呢?好像也没什么可说的. 把我的优缺点都表现出来了的一场考试. T3是个小的dp想出来就能打,打出来就能A.我上来过了一遍题目觉得T3最简单(然而也并不是很简单) 然后就开始打,交,其实已经A了 ...

  5. NOIP模拟 4

    T1没开longlong T2忘了有向... T3是个好题,可以说将复杂度从N^2优化到NlogN是一个质的飞跃 考虑分治(要想出log可不就要分治么!(segtree也行 但我不会) 对于一个分治区 ...

  6. 通俗易懂了解Vue内置组件keep-alive内部原理

    1. 官方介绍及其用法 1.1 组件介绍 要想搞明白<keep-alive>组件的内部实现原理,首先我们得搞明白这个组件怎么用以及为什么要用它,关于<keep-alive>组件 ...

  7. java.io.StreamCorruptedException: invalid stream header: 00000000

    Caused by: java.io.StreamCorruptedException: invalid stream header: 00000000 at java.io.ObjectInputS ...

  8. .Net Core Vue Qucik Start

    .Net Core Vue Qucik Start This is a ASP.NET Core 3.0 project seamlessly integrationed with Vue.js te ...

  9. 『题解』Codeforces446C DZY Loves Fibonacci Numbers

    更好的阅读体验 Portal Portal1: Codeforces Portal2: Luogu Description In mathematical terms, the sequence \( ...

  10. ELK分布式日志+NLog在.NetCore中的应用

    一.ELK简介 ELK是Elasticsearch.Logstash和Kibana首字母的缩写.这三者均是开源软件,这三套开源工具组合起来形成了一套强大的集中式日志管理平台 Elasticsearch ...