手撸web框架

简单的请求响应实现

要实现最简单的web框架,首先要对网络熟悉,首先HTTP协议是应用层的协议,只要我们给数据加上HTTP格式的响应报头,我们的数据就能基于socket进行实现了

import socket

sever = socket.socket()

sever.bind(('127.0.0.1',10000))

sever.listen(5)

while True:
conn,addr = sever.accept()
data = conn.recv(1024) print(data)
#响应行
conn.send(b'HTTP/1.1 200 OK\n\r')
#响应头
conn.send(b'name:zx\r\n')
conn.send(b'age:23\r\n')
conn.send(b'sex:man\r\n')
#空行!区分头和响应体
conn.send(b'\r\n\r\n')
#响应体
conn.send(b'<h1>Hello world!</h1>')
#关闭连接
conn.close()

web框架的特点

我们熟悉的web框架其实都很类似,基本上也就三大块

路由选择-业务处理-ORM

路由选择

根据客户端的请求,跳转到响应的业务处理

业务处理

业务处理

数据库操作

网页模板渲染

ORM

数据库关系映射

代码实现

路由选择-urls.py

from views import *

urls = [
('/index',index),
('/login',login),
('/xxx',xxx),
('/get_time',get_time),
('/get_db',get_db)
]

业务处理-views.py

from orm import Teacher

def index(env):
return 'index' def login(env):
return 'login' def error(env):
return '404 error' def xxx(env):
return 'xxx' from datetime import datetime def get_time(env):
current_time = datetime.now().strftime('%Y-%m-%d %X')
with open(r'C:\Users\Administrator\Desktop\01python\web\zx_web\time.html','r',encoding='utf-8') as f:
data = f.read()
#模板HTML渲染
data = data.replace('$$time$$',current_time)
return data def get_db(env):
#ORM数据库操作
ret = Teacher.select(tid=1)[0]
print(ret)
return str(ret)

ORM

orm.py

from MySQL import MySQL

# 定义字段类
class Field(object):
def __init__(self, name, column_type, primary_key, default):
self.name = name
self.column_type = column_type
self.primary_key = primary_key
self.default = default class StringField(Field):
def __init__(self,name,
column_type='varchar=(255)',
primary_key=False,
default=None):
super().__init__(name,column_type,primary_key,default) class IntegerField(Field):
def __init__(self,
name,
column_type='int',
primary_key=False,
default=None):
super().__init__(name, column_type, primary_key, default) class ModelMetaClass(type):
print("ModelMetaClass")
def __new__(cls,class_name,class_base,class_attrs):
print("ModelMetaClass_new")
#实例化对象的时候也会执行,我们要把这一次拦截掉
if class_name == 'Models':
#为了能让实例化顺利完成,返回一个空对象就行
return type.__new__(cls,class_name,class_base,class_attrs)
#获取表名
table_name = class_attrs.get('table_name',class_name) #定义一个存主键的的变量
primary_key = None #定义一个字典存储字段信息
mapping = {} #name='tid',primary_key=True
#for来找到主键字段
for k,v in class_attrs.items():
#判断信息是否是字段
if isinstance(v,Field):
mapping[k] = v
#寻找主键
if v.primary_key:
if primary_key:
raise TypeError("主键只有一个")
primary_key=v.name #将重复的键值对删除,因为已经放入了mapping
for k in mapping.keys():
class_attrs.pop(k)
if not primary_key:
raise TypeError("表必须要有一个主键")
class_attrs['table_name']=table_name
class_attrs['primary_key']=primary_key
class_attrs['mapping']=mapping
return type.__new__(cls,class_name,class_base,class_attrs) class Models(dict,metaclass=ModelMetaClass):
print("Models")
def __init__(self,**kwargs):
print(f'Models_init')
super().__init__(self,**kwargs) def __getattr__(self, item):
return self.get(item,"没有该值") def __setattr__(self, key, value):
self[key]=value #查找
@classmethod
def select(cls,**kwargs):
ms=MySQL() #如果没有参数默认是查询全部的
if not kwargs:
sql='select * from %s'%cls.table_name
res=ms.select(sql)
else:
k = list(kwargs.keys())[0]
v = kwargs.get(k)
sql='select * from %s where %s=?'%(cls.table_name,k) #防sql注入
sql=sql.replace('?','%s') res=ms.select(sql,v)
if res:
return [cls(**i) for i in res] #新增
def save(self):
ms=MySQL() #存字段名
fields=[]
#存值
values=[]
args=[] for k,v in self.mapping.items():
#主键自增,不用给他赋值
if not v.primary_key:
fields.append(v.name)
args.append("?")
values.append(getattr(self,v.name)) sql = "insert into %s(%s) values(%s)"%(self.table_name,",".join(fields),",".join((args))) sql = sql.replace('?','%s') ms.execute(sql,values) def update(self):
ms = MySQL()
fields = []
valuse = []
pr = None
for k,v in self.mapping.items():
#获取主键值
if v.primary_key:
pr = getattr(self,v.name,v.default)
else:
fields.append(v.name+'=?')
valuse.append(getattr(self,v.name,v.default))
print(fields,valuse)
sql = 'update %s set %s where %s = %s'%(self.table_name,','.join(fields),self.primary_key,pr) sql = sql.replace('?',"%s") ms.execute(sql,valuse) class Teacher(Models):
print("teacher")
table_name='teacher'
tid = IntegerField(name='tid',primary_key=True)
tname = StringField(name='tname') if __name__ == '__main__':
# tea=Teacher(tname="haha")
tea2=Teacher(tname="haha",tid=5)
# print(Teacher.select(tid=1))
# Teacher.save(tea)
Teacher.update(tea2)

MYSQL.py

import pymysql

class MySQL:

    #单例模式
__instance = None def __new__(cls, *args, **kwargs):
if not cls.__instance:
cls.__instance = object.__new__(cls)
return cls.__instance def __init__(self):
self.mysql = pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
database='orm_demo',
password='root',
charset='utf8',
autocommit=True
) #获取游标
self.cursor = self.mysql.cursor(
pymysql.cursors.DictCursor
) #查看
def select(self,sql,args=None):
print(sql,args) #提交sql语句
self.cursor.execute(sql,args) #获取查询的结果
res = self.cursor.fetchall()
return res #提交
def execute(self,sql,args):
#提交语句可能会发生异常 print(sql,args)
try:
self.cursor.execute(sql,args)
except Exception as e:
print(e) def close(self):
self.cursor.close()
self.mysql.close()

socket层

import socket
from urls import urls
from views import * sever = socket.socket() sever.bind(('127.0.0.1',10000)) sever.listen(5) while True:
conn,addr = sever.accept()
#获取HTTP请求信息
data = conn.recv(1024)
data = data.decode('utf8')
print(data)
#用户请求的路由
choice = data.split(' ')[1]
#找到路由
func = None
for url in urls:
if choice == url[0]:
func = url[1]
break
if func:
res = func(data)
else:
res = '<h1>404 error</h1>' #响应行
conn.send(b'HTTP/1.1 200 OK\n\r')
#响应头
conn.send(b'name:zx\r\n')
conn.send(b'age:23\r\n')
conn.send(b'sex:man')
#空行!区分头和响应体
conn.send(b'\r\n\r\n')
#响应体
conn.send(res.encode('utf8'))
#关闭连接
conn.close()

总结

其实并不是所有内容都要自己写,Python有很多的模块可以帮我们实现许多的功能

wsgiref模块:封装的一个socket服务,只需要关注数据发送和接收,不需要太多的关注HTTP协议的部分

from wsgiref.simple_server import make_server
from urls import urls
from views import * def run(env,response):
"""
:param env: 请求相关的所有数据
:param response: 响应相关的所有数据
:return:
"""
response('200 OK',[])
# print(env)
current_path = env.get('PATH_INFO') # 先定义一个变量名 用来存储后续匹配到的函数名
func = None
# for循环 匹配后缀
for url in urls:
if current_path == url[0]:
func = url[1] # 一旦匹配成功 就将匹配到的函数名赋值给func变量
break # 主动结束匹配
# 判断func是否有值
if func:
res = func(env)
else:
res = error(env)
return [res.encode('utf-8')] if __name__ == '__main__':
server = make_server('127.0.0.1',8080,run)
# 实时监听该地址 只要有客户端来连接 统一交给run函数去处理
server.serve_forever() # 启动服务端

jinja2模块:模板渲染功能

模板语法(极其贴近python后端语法)
<p>{{ user }}</p>
<p>{{ user.name }}</p>
<p>{{ user['pwd'] }}</p>
<p>{{ user.get('hobby') }}</p> {% for user_dict in user_list %}
<tr>
<td>{{ user_dict.id }}</td>
<td>{{ user_dict.name }}</td>
<td>{{ user_dict.pwd }}</td>
</tr>
{% endfor %}

Python实现简单框架及三大框架对比的更多相关文章

  1. python实现简单表单校验框架

    # encoding=utf-8 from app.models import Student from flask import g import re from flask.ext.wtf imp ...

  2. java利用myeclipse自带三大框架搭建三大框架(Hibernate+Struts2+Spring)过程详解

    搭建过程因人而异,我的搭建过程大致是这样的: 1.创建一个javaweb项目: 2.导入Spring框架,上图: 2.1: 2.2: 2.3: 3.导入struts2框架,上图: 3.1: 3.2: ...

  3. Django,Flask,Tornado三大框架对比,Python几种主流框架,13个Python web框架比较,2018年Python web五大主流框架

    Django 与 Tornado 各自的优缺点Django优点: 大和全(重量级框架)自带orm,template,view 需要的功能也可以去找第三方的app注重高效开发全自动化的管理后台(只需要使 ...

  4. vue、react、angular三大框架对比 && 与jQuery的对比

    前端当前最火的三大框架当属vue.react以及angular了. 但是在做项目的时候,我们怎么去选择呢?  这里做一个比较,希望大家可以有一个比较清晰的认识. vue与react vue和react ...

  5. python三大框架之一(flask介绍)

    Flask , Django,  Tornado 是python中常用的框架,也是python的三大框架.它们的区别是:Flask: 轻量级框架: Django:重量级框架: Tornado:性能最好 ...

  6. Python+selenium之简单介绍unittest单元测试框架

    Python+selenium之简单介绍unittest单元测试框架 一.unittest简单介绍 unittest支持测试自动化,共享测试用例中的初始化和关闭退出代码,在unittest中最小单元是 ...

  7. vue、react、angular三大框架对比

    前端的三大框架当属vue.react以及angular了,个人比较偏向react,它的社区比较繁荣,有很多丰富的组件 .angular的话感觉编译时间有点长,等待很恼火. vue与react vue和 ...

  8. python自动化测试(3)- 自动化框架及工具

    python自动化测试(3) 自动化框架及工具 1   概述 手续的关于测试的方法论,都是建立在之前的文章里面提到的观点: 功能测试不建议做自动化 接口测试性价比最高 接口测试可以做自动化 后面所谈到 ...

  9. SSH三大框架笔面试总结

    Java工程师(程序员)面题 Struts,Spring,Hibernate三大框架 1.Hibernate工作原理及为什么要用? 原理: 1.读取并解析配置文件 2.读取并解析映射信息,创建Sess ...

随机推荐

  1. 什么是浮点型?什么是单精度浮点数(float)以及双精度浮点数(double)?

    前言 作为一名java学习者,怎能不懂这些java基础中的基础呢?本文就带各位温顾温顾java浮点型.单精度浮点数.双精度浮点数. 浮点型 首先明确java中浮点型数据类型主要有:单精度float.双 ...

  2. python中小整数对象池及intern机制

    小整数对象池: Python为了优化速度,使用了小整数对象池,避免为整数频繁申请和销毁 Python 对小整数的定义是 [-5, 256] 这些整数对象是提前建立好的,不会被垃圾回收,所有位于这个范围 ...

  3. 【翻译】Prometheus 2.12.0 新特性

    Prometheus 2.12.0 现在(2019.08.17)已经发布,在上个月的 2.11.0 之后又进行了一些修正和改进. 在当前的 6 周发布周期中,每一个 Prometheus 版本都有比较 ...

  4. 「考试」$5T$

    啊因为最近题实在是好啊,只能四五篇四五篇写了. T1. 括号序列的确简单. 当我们维护左右$cnt$后. 到一个左括号的地方的话. 答案就是:$$\sum\limits_{i=1}^{min(lc,r ...

  5. 理解np.nonzero()函数

    举三个例子,就能清楚的看到 np.nonzero() 这个函数返回值的意义 一. #例1 一维数组 import numpy as np a = [0,1,2,0,3,0] b = np.nonzer ...

  6. Netty启动流程剖析

    编者注:Netty是Java领域有名的开源网络库,特点是高性能和高扩展性,因此很多流行的框架都是基于它来构建的,比如我们熟知的Dubbo.Rocketmq.Hadoop等,针对高性能RPC,一般都是基 ...

  7. 如何修改git上传代码的作者姓名

    随着IT行业的不断壮大,开源的东西越来越多,使用git来进行代码管理的人自然也越来越多,而且很多人的git账户都不是自己的本人名字,但是在公司工作的时候为了遇到问题能够定位责任人和便于区分提交人,通常 ...

  8. webpack的npm扩展使用

    一.NPM的扩展使用 (1)  npm init:初始化一个Node.js项目------创建必须的package.json文件 npm init -y:创建必须的package.json文件 (2) ...

  9. Python之tkinter.messagebox弹窗

    messagebox:tkinter的消息框.对话框 一.messagebox.showinfo(title='提示', message='错误') from tkinter import * fro ...

  10. [java] 集合的架构——1张图表示