框架1

目的

实现一个简单的web框架 只实现rest(有一整套严格的规范)

常见框架有 : Rails pyramid bottle web.py flask

理解框架实现原理

动态网页的发展历程
最早的时候,web就是一些静态文件

最早的动态页面是CGI实现的(动态生成返回的内容)

CGI怎么工作?

当请求一个地址的时候,http服务器就会启动一个外部程序,并且把外部程序的输出返回给用户。

CGI协议规范了http和外部程序的通信方式
通信方式:
输入:通过环境变量   http设置环境变量
输出:通过标准输出   http服务器截获外部程序的输出作为输出的

CGI缺陷:并发模式是多进程(每一次请求都启动一个进程),而且进程由http服务器管理造成效

率低(http不仅要提供服务,还要提供进程管理),崩溃的时候处理起来很麻烦,造成性能低下。

使用CGI开发并不慢

CGI发展--->FastCGI(把进程管理交给其他的后台服务来处理)
所以http只需要和FastCGI 通信就行了

http服务器只需要把http协议转化成fastCGI协议,然后通过socket
发送给fastCGI daemon(后台进程)

例如:php fmp

使得并发模型变得多种多样(perl线程池,php进程池),进程管理由fastCGI实现

http服务器只需要管理http请求和响应就行了

单一原则:只做擅长的事情

PEP3333定义WSGI
另一个发展方向-->wsgi通用web服务网关接口
分成两部分:容器(也有叫网关或服务器) 容器实现协议转换http-->WSGI
                 应用 处理业务逻辑

容器和应用怎么通信? 
FastCGI --> 通过socket通信

uWSGI 是一个WSGI的容器
WSGI的容器和应用通过函数调用通信

什么时候?
同一个进程内才能经过函数调用来通信

也就是说WSGI容器和引用必须是跑在同一进程/线程内的

并不是同一进程就不能实现多进程

类比

FastCGI:可以理解http服务器就是容器,Fast daemon就是应用
二者之间通过socket通信

gunicorn uWSGI 容器 都可以实现多进程
多线程 协程 见WSGI官网或者PEP3333

一个WSGI application 应该是一个callable对象

 1 def application(environ,start_response): # 传递的参数有规定:环境 回调函数
 2     body=‘hello,world‘
 3     status =‘200 ok‘   ---http协议状态码的规范
 4     headers=[
 5        (‘content-type‘,‘text/plain‘),  # 每一项都是一个二元组
 6        (‘content-length‘,str(len(body)))  #头信息必须都是 str
 7     ]
 8     start_response(status,headers)  # 代表开始响应了    返回头部信息
 9     return [body.encode()]  可以传递多行 ---WSGI解析的时候是迭代的,所以返回列表
10                    要是bytes的可迭代对象

http是支持流式处理 使用yield也可以

此时是跑不起来的 因为没有容器
python标准库里面有容器的实现 wsgiref  传递的应用必须要是一个可调用对象

1 if __name__ == ‘__main__‘:
2     from wsgiref.simple_server import make_server 创建容器的实例
3     server=make_server(‘0.0.0.0‘,3000,application)
4     try:
5         server.server_forver()
6     except KeyboardInterrupt:
7         server.shutdown()

environ 请求的内容 是一个字典 本地的和服务端的信息都在里面
start_response 必须是callable对象 代表开始响应

output分为两部分 start_response是返回头部信息

return是返回body体

environ和start_response两个参数都是容器传进来的

gunicron -b 0.0.0.0 文件名:应用名
--threads INT
--help

开发的时候用wsgiref使用方便 生产的用gunicron性能好

QUERY_STRING 里面存储的是用户传递来的参数
127.0.0.1:5000/?name=wanglei&age=15  -->qs = name=wanglei&age=15  解析这些东西就可以得到参数了

environ.get(‘QUERY_STRING‘) # 获得 qs

 

怎么解析?

解析出来的值是list,因为可能存在多个值

from urllib.parse import parse_qs

def application(environ, start_response):
    parm = parse_qs(environ.get(‘QUERY_STRING‘)) # 解析出来的是字典
    name = parm.get(‘name‘, [‘unknown‘])[0]  # 因为得到的是一个列表
    body = "hello,world {}".format(name)
    status = ‘200 ok‘
    headers = [(‘content-type‘, ‘text/html‘),
               (‘content-length‘, str(len(body)))
               ]
    start_response(status, headers)
    return [body.encode()]

http://127.0.0.1:9000/hell0/?name=wanglei&age=22

parse_qs(‘name=wanglei&age=13&name=duan‘)
{‘age‘:[13],‘name‘:[‘wanglei‘,‘duan‘]}

结果---> hello,world wanglei

这样是比较危险的 可能产生XSS攻击

http://127.0.0.1:9000/hell0/?name=<script type=‘text/javascript‘>alert(‘fuck it‘);</script>

 1 from urllib.parse import parse_qs
 2 from html import escape
 3
 4
 5 def application(environ, start_response):
 6     parm = parse_qs(environ.get(‘QUERY_STRING‘))
 7     name = parm.get(‘name‘, ‘unknown‘)[0]
 8     body = "hello,world {}".format(escape(name)) # 进行转义
 9     status = ‘200 ok‘
10     headers = [(‘content-type‘, ‘text/html‘),
11                (‘content-length‘, str(len(body)))
12                ]
13     start_response(status, headers)
14     return [body.encode()]

网页结果:

就会进行转义把它当做普通的文本处理

通过qs 就可以实现客户端和服务端通信

层次太低 太底层的系统,需要进行封装

可以将environ 抽象成 request对象
将start_response和return抽象成response对象

封装Request对象

import os

class Request:
    def __init__(self, environ):
        self.params = parse_qs(environ.get(‘QUERY_STRING‘)) # 参数
        self.path = environ.get(‘PATH_INFO‘)
        self.method = environ.get(‘REQUEST_METHOD‘) # 请求方法
        self.body = environ.get(‘wsgi.input‘) # body内容
        self.headers = {}
        server_env = os.environ
        for k, v in environ.items(): # headers
            if k not in server_env.keys(): # 过滤掉服务器端的信息
                self.headers[k.lower()] = v
def application(environ, start_response):
    request = Request(environ)
    name = request.params.get(‘name‘, [‘unknown‘])[0]
    body = "hello,world {}".format(escape(name))
    status = ‘200 ok‘
    headers = [(‘content-type‘, ‘text/html‘),
               (‘content-length‘, str(len(body)))
               ]
    start_response(status, headers)
    return [body.encode()]

封装Response对象

class Response:
    STATUS={
          200:‘ok‘
          404:‘Not found‘
    }
    def __init__(self, body=None):
        if body is None:
            self.body=‘‘
        self.body = body
        self.status=‘200 ok‘
        self.headers={
            ‘content-type‘:‘text/html‘,
             ‘content-length‘:‘str(len(body))‘
        }

    def set_body(self,body):
        self.body=body
        self.headers[‘content-length‘]=str(len(self.body))
  

   def set_header(self,name,value):
      self.headers[name]=value

def set_status(self,status_code):
        if status_code in self.STATUS.keys():
            self.status=‘{} {}‘.format(status_code,self.STATUS[status_code])def set_header(self,name,value):
        self.headers[name]=value

    def __call__(self,start_response):
        start_response(self.status,[(k,v) for k,v in self.headers.items()]) # 列表解析生成 headers
        return [self.body.encode()]
def application(environ, start_response):
    request=Request(environ)
    name = request.parms.get(‘name‘, [‘None‘])[0]
    body = "hello,world %s" % escape(name)
    return Response(body)(start_response) # 返回给容器
def set_status(self,status_code,status_text=‘‘):
    self.status=‘{} {}‘.format(status_code,self.STATUS.get(status_code,status_text))
        允许非标准的状态码

为什么要用__call__?       最后返回给容器要是一个可调用对象

很显然不能这么简单的封装

使用webob 类似 flask里面的werzeug都是对请求和响应的封装

webob 现在是 pylons的一个子项目 国外流行

webob不是web框架,只是以面向对象的方式封装WSGI
请求和响应 只做这一件事

非标状态码 Nginx 499
request.parms.getall() 返回所有的 而get 返回最后一个

from webob import Request, Response

def application(environ, start_response):
    request = Request(environ)
    name = request.params.get(‘name‘, ‘unknown‘) # 有多个值返回最后一个
    body = "hello,world {}".format(name)
    return Response(body)(environ,start_response) # 使用webob 返回的时候要加入 environ 和start_response 两个参数

webob提供了一个装饰器

以前:

from webob import Request, Response

def application(environ, start_response):
    request = Request(environ)
    name = request.params.get(‘name‘, ‘unknown‘)
    body = "hello,world {}".format(name)
    return Response(body)(environ,start_response)

现在:

from webob import Request, Response
from webob.dec import wsgify

@wsgify
def app1(request: Request) -> Response:
    name = request.params.get(‘name‘, ‘unknown‘)
    body = "hello,world {}".format(name)
    return Response(body)

小图标问题

@wsgify
def app1(request: Request) -> Response:
    if request.path == ‘/favicon.ico‘:
        resp = Response(content_type=‘img/x-icon‘) # 小图标的content_type
        resp.body_file = open(‘./favicon.ico‘, ‘rb‘) # 图片不能使用 utf8来解析 所以图片一定要是rb模式
        return resp
    name = request.params.get(‘name‘, ‘unknown‘)
    body = "hello,world {}".format(escape(name))
    return Response(body)

或者

@wsgify
def app1(request: Request) -> Response:
    if request.path == ‘/favicon.ico‘:
        with open(‘./favicon.ico‘,‘rb‘) as f:
            resp = Response(body=f.read(),content_type=‘img/x-icon‘)
            return resp
    name = request.params.get(‘name‘, ‘unknown‘)
    body = "hello,world {}".format(escape(name))
    return Response(body)

显然以这种方式来匹配url不靠谱

怎么处理路由

@wsgify
def app1(request: Request) -> Response:
    if request.path == ‘/favicon.ico‘:
        return favicon(request)
    if request.path.endswith() == ‘/hello‘:
        return hello(request)
    if request.path.startswith() == ‘/‘:
        return index(request)

def index(request):
    return Response("index page")

def hello(request):
    name = request.params.get(‘name‘, ‘unknown‘)
    body = "hello,world {}".format(escape(name))
    return Response(body)
@wsgify  # 使用正则
def app1(request: Request) -> Response:
    if re.match(r‘^/favicon$‘, request.path):
        return favicon(request)
    if re.match(r‘^/hello/$‘, request.path):
        return hello(request)
    if re.match(r‘/$‘, request.path):
        return index(request)  

用类来重写application

from webob import Response
from webob.dec import wsgify
import re

class Application:
    def __init__(self):
        self.routes = []

    def route(self, rules, handler):
        self.routes.append((rules, handler))

    @wsgify
    def __call__(self, request):
        for rule,handler in self.routes:
            if re.match(rule,request.path):
                return handler(request)

def index(request):
    return Response("index page")

def hello(request):
    name = request.params.get(‘name‘, ‘unknown‘)
    body = "hello,world {}".format(ename)
    return Response(body)

def favicon(request):
    with open(‘./favicon.ico‘, ‘rb‘) as f:
        resp = Response(body=f.read(), content_type=‘img/x-icon‘)
        return resp

if __name__ == ‘__main__‘:
    from wsgiref.simple_server import make_server
    application=Application()
    application.route(r‘/favicon.ico‘,favicon)  # 注册路由
    application.route(r‘/hello‘,hello)
    application.route(r‘/$‘,index)
    server = make_server(‘0.0.0.0‘, 9000, application)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        server.shutdown()
实现装饰器来注册路由

from webob import Response
from collections import namedtuple
from webob.dec import wsgify
import re

Route = namedtuple(‘Route‘, (‘pattern‘, ‘methods‘, ‘handler‘))

class Application:
    def __init__(self):
        self.routes = []

    def _route(self, rules, methods, handler):
        self.routes.append(Route(rules, methods, handler))

    def router(self, pattern, methods=None):
        if methods is None:            # 不传递该参数表示可以是任意方法
            methods = [‘GET‘, ‘POST‘, ‘HEAD‘, ‘OPTION‘, ‘DELETE‘,‘HEAD‘,‘PUT‘]
        def dec(handler):
            self._route(pattern, methods, handler)
            return handler
        return dec

    @wsgify
    def __call__(self, request):
        for route in self.routes:
            if request.method in route.methods or request.method==route.methods:
                if re.match(route.pattern, request.path):
                    return route.handler(request)

app = Application()

@app.router(r‘/‘, methods=‘GET‘)
def index(request):
    return Response("index page")

@app.router(r‘hello‘, methods=‘GET‘)
def hello(request):
    name = request.params.get(‘name‘, ‘unknown‘)
    body = "hello,world {}".format(escape(name))
    return Response(body)

@app.router(r‘favicon‘)
def favicon(request):
    with open(‘./favicon.ico‘, ‘rb‘) as f:
        resp = Response(body=f.read(), content_type=‘img/x-icon‘)
        return resp

if __name__ == ‘__main__‘:
    from wsgiref.simple_server import make_server

    server = make_server(‘0.0.0.0‘, 9000, app)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        server.shutdown()

 

预先将正则表达式编译好from webob import Response
from collections import namedtuple
from webob.dec import wsgify
import re

Route = namedtuple(‘Route‘, (‘pattern‘, ‘methods‘, ‘handler‘))

class Application:
    def __init__(self):
        self.routes = []

    def _route(self, rules, methods, handler):
        self.routes.append(Route(re.compile(rules), methods, handler))

    def router(self, pattern, methods=None):
        if methods is None:
            methods = [‘GET‘, ‘POST‘, ‘HEAD‘, ‘OPTION‘, ‘DELETE‘, ‘HEAD‘]
        def dec(fn):
            self._route(pattern, methods, fn)
            return fn

        return dec

    @wsgify
    def __call__(self, request):
        for route in self.routes:
            if request.method in route.methods or request.method == route.methods:
                if route.pattern.match(request.path):
                    return route.handler(request)

app = Application()

@app.router(r‘^/$‘, methods=‘GET‘)
def index(request):
    return Response("index page")

@app.router(r‘^/hello$‘, methods=‘GET‘)
def hello(request):
    name = request.params.get(‘name‘, ‘unknown‘)
    body = "hello,world {}".format(name)
    return Response(body)

@app.router(r‘favicon‘)
def favicon(request):
    with open(‘./favicon.ico‘, ‘rb‘) as f:
        resp = Response(body=f.read(), content_type=‘img/x-icon‘)
        return resp

if __name__ == ‘__main__‘:
    from wsgiref.simple_server import make_server

    server = make_server(‘0.0.0.0‘, 9000, app)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        server.shutdown()

如果想要将app一些全局的东西传递进来

from webob import Response
from collections import namedtuple
from webob.dec import wsgify
import re

Route = namedtuple(‘Route‘, (‘pattern‘, ‘method‘, ‘handler‘))

class Application:
    def __init__(self, **options):
        self.routes = []
        self.options = options  # 将要传递的参数 保存在 options里面

    def _route(self, rules, methods, handler):
        self.routes.append(Route(re.compile(rules), methods, handler))

    def router(self, pattern, methods=None):
        if methods is None:
            methods = [‘GET‘, ‘POST‘, ‘HEAD‘, ‘OPTION‘, ‘DELETE‘, ‘HEAD‘]
        def dec(fn):
            self._route(pattern, methods, fn)
            return fn

        return dec

    @wsgify
    def __call__(self, request):
        for route in self.routes:
            if request.method in route.methods or request.method == route.methods:
                if route.pattern.match(request.path):
                    return route.handler(request, self) # 将对象本身传递进视图函数

app = Application(debug=True)  # 如果想要传递一些全局的变量到 视图里面

@app.router(r‘^/$‘, methods=‘GET‘)
def index(request, ap):
    if ap.options.get(‘debug‘): # 根据传递进来的参数做一些事情
        for k, v in request.headers.items():
            print(‘{}===>{}‘.format(k, v))
    return Response("index page")

@app.router(r‘^/hello$‘, methods=‘GET‘)
def hello(request, ap):
    name = request.params.get(‘name‘, ‘unknown‘)
    body = "hello,world {}".format(name)
    return Response(body)

@app.router(r‘^/favicon$‘)
def favicon(request, ap):
    with open(‘./favicon.ico‘, ‘rb‘) as f:
        resp = Response(body=f.read(), content_type=‘img/x-icon‘)
        return resp

if __name__ == ‘__main__‘:
    from wsgiref.simple_server import make_server

    server = make_server(‘0.0.0.0‘, 9000, app)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        server.shutdown()

是不是单线程看容器  这样写应用就不需要关注多进程 多线程等

gunicron -w 8    8个进程

web框架主要功能
request解析
Response封装
路由

rest 是不处理cookie和session  模板等

路由是web框架最重要的部分

exc 里面都是异常,基本上每一个对应一个状态码

exc.HTTPTemporaryRedirect(location)
临时跳转

时间: 2024-10-26 09:29:58

框架1的相关文章

基于jquery开发的UI框架整理分析

根据调查得知,现在市场中的UI框架差不多40个左右,不知大家都习惯性的用哪个框架,现在市场中有几款UI框架稍微的成熟一些,也是大家比较喜欢的一种UI框架,那应该是jQuery,有部分UI框架都是根据jQuery研发出来的产品,现在也很常见了. 国产jQuery UI框架 (jUI) DWZ DWZ富客户端框架(jQuery RIA framework), 是中国人自己开发的基于jQuery实现的Ajax RIA开源框架.设计目标是简单实用,快速开发,降低ajax开发成本. jQuery 部件布局

静态网页框架设计首次体验(文章改)

根据教材与上网成功解决了Tomcat与Myeclipse的安装,同时熟悉了Java web创建项目到部署运行整个过程.今天起正式开始学习有关Java web的编程部分.Java web静态网页(HTML网页)的标记含义.基本语法的介绍到框架设计基本模板与案例,今天的学习的内容,让网页编程有了一个初步的框架.结合自身所在协会的情况,计划制作一个关于协会的网页,已有初步想法,望通过学习不断完善和修改协会网站.根据今天所学,并参考书本30页框架设计案例对网页进行初步搭建. 具体代码如下 TW.jsp:

.NET深入解析LINQ框架(五:IQueryable、IQueryProvider接口详解)

阅读目录: 1.环路执行对象模型.碎片化执行模型(假递归式调用) 2.N层对象执行模型(纵横向对比链式扩展方法) 3.LINQ查询表达式和链式查询方法其实都是空壳子 4.详细的对象结构图(对象的执行原理) 5.IQueryable<T>与IQueryProvider一对一的关系能否改成一对多的关系 6.完整的自定义查询 1]. 环路执行对象模型.碎片化执行模型(假递归式调用) 这个主题扯的可能有点远,但是它关系着整个LINQ框架的设计结构,至少在我还没有搞懂LINQ的本意之前,在我脑海里一直频

.NET深入解析LINQ框架(一:LINQ优雅的前奏)

阅读目录: 1.LINQ简述 2.LINQ优雅前奏的音符 2.1.隐式类型 (由编辑器自动根据表达式推断出对象的最终类型) 2.2.对象初始化器 (简化了对象的创建及初始化的过程) 2.3.Lambda表达式 (对匿名方法的改进,加入了委托签名的类型推断并很好的与表达式树的结合) 2.4.扩展方法 (允许在不修改类型的内部代码的情况下为类型添加独立的行为) 2.5.匿名类型 (由对象初始化器推断得出的类型,该类型在编译后自动创建) 2.6.表达式目录树(用数据结构表示程序逻辑代码) 3.LINQ

.NET深入解析LINQ框架(二:LINQ优雅的前奏)

阅读目录: 1.LINQ框架的主要设计模型 1.1.链式设计模式 (以流水线般的链接方式设计系统逻辑) 1.2.链式查询方法(逐步加工查询表达式中的每一个工作点) 2.LINQ框架的核心设计原理 2.1.托管语言之上的语言(LINQ查询表达式) 2.2.托管语言构造的基础(LINQ依附通用接口与查询操作符对应的方法对接) 2.3.深入IEnumerable.IEnumerable<T>.Enumerable(LINQ to Object框架的入口) 2.4.深入IQueryable.IQuer

.NET深入解析LINQ框架(四:IQueryable、IQueryProvider接口详解)

阅读目录: 1.开篇介绍 2.扩展Linq to Object (应用框架具有查询功能) 2.1.通过添加IEnumerable<T>对象的扩展方法 2.2.通过继承IEnumerable<T>接口 2.3.详细的对象结构图 3.实现IQueryable<T> .IQueryProvider接口 3.1.延迟加载IEnumertor<T>对象(提高系统性能) 3.2.扩展方法的扩展对象之奥秘(this IQueryable<TSource> so

.NET深入解析LINQ框架(三:LINQ优雅的前奏)

阅读目录: 1.动态LINQ查询(动态构建Expression<T>表达式树) 2.DLR动态语言运行时(基于CLR之上的动态语言运行时) 1].动态LINQ查询(动态构建Expression<T>表达式树) 什么是动态LINQ查询?LINQ的编写是静态的,因为C#是基于静态类型系统原理设计的,在编写时已经确定类型,也就是在编译时就已经知道将要执行什么样的查询,条件是什么.排序方式是什么等等.那么很大一部分应用场合中我们需要根据用户的选择来查询数据源,以往我们都是通过判断的方式来拼

Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案 (精髓)

前言 刚开始创建MVC与Web API的混合项目时,碰到好多问题,今天拿出来跟大家一起分享下.有朋友私信我问项目的分层及文件夹结构在我的第一篇博客中没说清楚,那么接下来我就准备从这些文件怎么分文件夹说起.问题大概有以下几点: 1.项目层的文件夹结构 2.解决MVC的Controller和Web API的Controller类名不能相同的问题 3.给MVC不同命名空间的Area的注册不同的路由 4.让Web API路由配置也支持命名空间参数 5.MVC及Web API添加身份验证及错误处理的过滤器

10大APP界面框架设计模式详解

随着移动互联网的发展,移动app已经成为了每个互联网公司的标配了,那作为产品经理,我们如何设计出更加符合用户体验的app产品呢?今天和大家分享的就是10中最常见的app界面光甲设计模式,一起来看看吧. 1.标签导航 标签导航是十大界面框架设计里最常用的界面框架设计,也是被业界之内公认的一种普遍使用的页面框架设计.那么这种页面框架设计在作业方面对一个用户来说也是最常见的一种页面框架设计,比如说微博.微信.手机百度.支付宝.淘宝,这些我们所谓的超级APP都是运用的标签导航,无一例外.从这个角度也可以

MyBatis框架中Mapper映射配置的使用及原理解析(七) MapperProxy,MapperProxyFactory

从上文<MyBatis框架中Mapper映射配置的使用及原理解析(六) MapperRegistry> 中我们知道DefaultSqlSession的getMapper方法,最后是通过MapperRegistry对象获得Mapper实例: public <T> T getMapper(Class<T> type, SqlSession sqlSession) { final MapperProxyFactory<T> mapperProxyFactory =