您的位置:首页 > 其它

徒手撸出一个类Flask微框架(二)路由及路由注册的演变

2018-02-19 21:20 501 查看
路由的基本概念:
根据不同的访问路径调用不同的方法或者类

from webob import Response,Request,dec
from wsgiref.simple_server import make_server,demo_app
def index(request:Request):
res = Response()
res.body = 'index.html'.encode()
return res
def showpython(request:Request):
res = Response()
res.body = 'show_python'.encode()
return res
def notfound():
res = Response()
res.status_code = 404
return res
@dec.wsgify
def app(request:Request):
if request.path == '/':
return index(request)
elif request.path == '/python':
return showpython(request)
else:
return notfound()
如果用字典描述它是否更好
如果找不到则调用notfound

@dec.wsgify
def app(request:Request):
ROUTE.get(request.path,notfound)(request)
if __name__ == "__main__":
ip = '127.0.0.1'
port = 9999
server = make_server(ip,port,app)
server.serve_forever()
server.server_close()


实现注册
这样的话就可以将路由表设置为空
用这个路由的话起码保证了已经注册过了

def index(reques:Request):
res = Response()
res.body = 'index.html'.encode()
return res
def showpython(reques:Request):
res = Response()
res.body = 'python.html'.encode()
return res
def notfound():
res = Response()
res.status_code = 404
res.body = 'Not Found'.encode()
return res
# Route_Table
ROUTERTABLE = {}
def register(path,handler):
ROUTERTABLE[path] = handler
register('/',index)
register('/python',showpython)
@wsgify
def app(request:Request):
ROUTE.get(request.path, notfound)(request)
路由的封装

class APP:
def index(self,request:Request):
res = Response()
res.body = 'hello'.encode()
return res
def showpython(self,request:Request):
res = Response()
res.body = 'show python'.encode()
return res


分析好处与瓶颈分别在哪里
对一个web框架来讲,这些方法都应该是用户自己去定义,而并非是框架内去完成的,因为框架是提供用户去使用,所以是由使用者去实现
那么对于对于哪些是放在外面,合理的规划如下:

class APPlication:
def __init__(self,env,start_response):
pass
def notfound(request:Request):
res = Response
res.status_code = 404
res.body = '404'.encode()
return res
#路由表
ROUTERTABLE = {}
def register(path,handler):
ROUTERTAB[path] = handler
@dec.wsgify
def app(request:Request):
return ROUTETABLE.get(request.path,notfound)(request)
#在外部定义执行的函数
def index(self,request:Request):
res = Response()
res.body = 'hello'.encode()
return res


类的实例化
如果通过类进行定义那么必须实例化,因为需要__init__的支撑
对于一个类中要么在init中实现,要么在__call__ 中实现

__init__:如果定义在init中,实例化后则是被写死,显然不符当前调用的灵活性,相当于在调用缺省的两个函数
class APPlication:
def __init__(self,env,start_response):
__call__:如果在__call__中定义,显然调用和传参是比较灵活的

思考一个问题:__call__是如何调用的
改进:
对于server来讲

server = make_server(ip,port,app)
一定是看到两个参数调用,因为有装饰器被装饰一定保证这个__call__ 一定被转化成wsgify

class Application:
def notfound(request:Request):
res = Response()
res.status_code = 404
res.body = 'not found~~'.encode()
return res
ROUTETABLE = {}
# 注册
def register(path,handler):
ROUTETABLE[path] = handler
@dec.wsgify
def __call__(self,request:Request):
return ROUTETABLE.get(request.path, notfound)(request)
def index(request:Request):
res = Response
res.body = 'hh'.encode()
return res


这样的话通过装饰器进行修饰之后,变成了我们想要的方式
最开始的__call__方法必须满足两个参数 request 和 response 必须保证进出,那如果通过wsgify的装饰之后,那么确保这个__call__变成了接口,满足了接口定义的要求



这里request就是业务上的第一个参数
通过request传进之后,返回一个返回值,可以达到最后要求,只不过是用装饰器来完成
返回值需要通过装饰器 start_response 保证最后是一个可迭代对象
简单的来讲,就是将这个例子:

@wsgify
def myfunc(request:Request):
return Response('hey here')
转为了另一个例子:

@wsgify
#def __call__(self, *args, **kwargs):
def __call__(self,request:Request):
return ROUTER.get(request.path, self.notfound)(request)
通过__call__ 方法可以直接拿来做函数使用,这就省去了一些不必要的麻烦
添加异常,避免访问出现问题

@dec.wsgify
def __call__(self,request:Request):
try:
print(request)        #将request 传递给了index(request)
return cls.ROUTE[request.path](request)
except:
return self.notfound(request)


加入注册
倾向于使用类方法,将注册函数加入到类中
先到server中由其交给业务函数管理,业务函数是用户根据自己的需求自己填写内容

from webob import Response,Request,dec,exc
from wsgiref.simple_server import make_server,demo_app
import re
class Application:
def notfound(self,request:Request):
res = Response()
res.status_code = 404
res.body = 'hel'.encode()
return res
ROUTE = { }
@classmethod
def register(cls,path,handler):
cls.ROUTE[path] = handler
@dec.wsgify
def __call__(self, request:Request):
return self.ROUTE.get(request.path, self.notfound)(request)
def index(request:Request):
res = Response()
res.status_code = 200
res.body = 'hhh'.encode()
return res
Application.register('/',index)
if __name__ == "__main__":
ip = '127.0.0.1'
port = 9999
server = make_server(ip,port,Application())
server.serve_forever()
server.server_close()


exc 异常模块及状态码相关处理
exc主要提供了异常模块及状态码相关的处理功能
导入模块
from webob import Response,Request,dec,exc
查看源码:
看到源码内定义了几乎所有的HTTP_Code 每个都是通过单独类来实现,并继承自HTTPClientxxxx
比如:200的类

class HTTPOk(WSGIHTTPException):
"""
Base class for the 200's status code (successful responses)
code: 200, title: OK
"""
code = 200
title = 'OK'


再比如404的类

class HTTPNotFound(HTTPClientError):
"""
subclass of :class:`~HTTPClientError`
This indicates that the server did not find anything matching the
Request-URI.
code: 404, title: Not Found
"""
code = 404
title = 'Not Found'
explanation = ('The resource could not be found.')


发现每个状态码的类都是子类,继续跟进父类查看:
查看HTTPClientError
class HTTPClientError(HTTPError):
code = 400
title = 'Bad Request'
explanation = ('The server could not comply with the request since\r\n'
'it is either malformed or otherwise incorrect.\r\n')
也是一个子类,再次跟进HTTPError
class HTTPError(WSGIHTTPException):
WSGIHTTPException 代码如下
class WSGIHTTPException(Response, HTTPException):
## You should set in subclasses:
# code = 200
# title = 'OK'
# explanation = 'why this happens'
# body_template_obj = Template('response template')
code = 500
title = 'Internal Server Error'
explanation = ''
body_template_obj = Template('''\
${explanation}<br /><br />
${detail}
${html_comment}
''')
它的子类将其方法进行逐层覆盖,并返回其实还是调用的这个方法

并调用了wsgi的接口函数wsgi_response
最后实际调用的是__call__并传递两个参数,environ 请求的所有信息 以及 start_response
所谓http code就是封装一个编号并返回response body的内容
exc是多继承,继承了response,将一个缺省的body可以传递
如果出现某些问题则调用异常,这个异常本身继承resopnse进行raise出去
所以直接调用exc.类名就可以了

@dec.wsgify
def __call__(self,request:Request):
try:
print(request)
return self.ROUTE[request.path](request)
except:
return exc.HTTPNotFound('hahaha')


所以,只要抛异常就是没有正常获取,直接抛异常即可

改进注册过程,使用装饰器进行包装
我们看到效果,当注册路由的时候,使用Application.reg功能进行调用,但是明显是不灵活的
能否像其他框架直接在方法或者函数上面写明要指定的路由路径呢
比如

@xxxxx.register('/',index)
def index(request:Response):
res = Response()
res.body = 'index'.encode()
return res
目前来看,只能使用装饰器来完成当前效果了
等价式:--> index = app.reg('/')(index)
@dec.wsgify
def __call__(self,request:Request):
try:
print(request)
return self.ROUTE[request.path](request)
# return self.ROUTE.get(request.path, self.notfound)(request)
except:
return exc.HTTPNotFound('hahaha')
# @Application.reg('/',index)  带参装饰器   --> Application() -->  func(path)(index)
@Application.reg('/')
def index(request:Response):
res = Response()
res.body = 'index'.encode()
return res


路由主要是解决了后端问题
完整如下:

from webob import Response,Request,exc
from webob.dec import wsgify
from wsgiref.simple_server import make_server,demo_app
import re
# @wsgify
# def index(request:Request):
#     res = Response()
#     res.body = 'index.html'.encode()
#     return res
class Application:
ROUTER_TABLE = {}
@classmethod
def register(cls,path):
def wapper(handler):
cls.ROUTER_TABLE[path] = handler
print(cls.ROUTER_TABLE)
return handler
return wapper
@wsgify
def __call__(self, request):
try:
print(request.path)
print(request)
print(self.ROUTER_TABLE[request.path])
return self.ROUTER_TABLE[request.path](request)
except:
raise exc.HTTPNotFound('not funod~~')
@Application.register('/')  # --> index = app.reg('/')(index)
def index(request:Request):
res = Response()
res.body = 'index.html'.encode()
return res
@Application.register('/python')  # --> index = app.reg('/')(index)
def showpython(request:Request):
res = Response()
res.body = 'python'.encode()
return res
if __name__ == "__main__":
ip = '127.0.0.1'
port = 9999
server = make_server(ip,port,Application())
server.serve_forever()
server.server_close()
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Flask 微框架
相关文章推荐