编写一个轻量级的Web应用框架

最近阅读了Flask的源码,弄懂了原理之后就想尝试来实现自己的一个Web框架。

因为大部分的实现思路都参照Flask0.1版本,也就是最初版本的思路。所用的基本库是werkzeug

框架的完整代码都放在了Github上,之后会继续更新:https://github.com/jyz0309/WebFrame
求star,球球了T_T

总体思想:

按照flask的最基本功能的实现思想,首先建立url_map的字典进行url和视图函数的对应关系。

首先,当一个请求进来的时候,优先进入的是App类的__call__()回调函数,然后进入到wsgi_app函数中,在这个函数中进行请求的分发处理以及响应的返回。

具体的实现在代码中的注释。

以下是代码:

import os
from werkzeug.wrappers import BaseRequest, BaseResponse
from werkzeug.exceptions import HTTPException, MethodNotAllowed, \
     NotImplemented, NotFound
from werkzeug.serving import run_simple
from jinja2 import Environment,FileSystemLoader

class Request(BaseRequest):
    """对请求进行包装,可以自定义"""

class Response(BaseResponse):
    """对返回值进行包装,可以自定义."""

class View(object):
    """视图的基本类"""
    def __init__(self):
        self.methods = {
            'GET': self.GET,
            'POST': self.POST,
            'PUT': self.PUT,
            'DELETE': self.DELETE,
        }
    def GET(self):
        raise MethodNotAllowed()
    POST = DELETE = PUT = GET

    def HEAD(self):
        return self.GET()
    def dispatch_request(self, request, *args, **options):
        if request.method in self.methods:
            return self.methods[request.method](request, *args, **options)
        else:
            return '<h1>不支持的请求方式!</h1>'

    @classmethod
    def get_func(cls):
        # 匹配视图函数
        def func(*args, **kwargs):
            obj = func.view_class()
            return obj.dispatch_request(*args, **kwargs)
        func.view_class = cls
        return func

class App(object):
    def __init__(self):
        self.url_map = {} # 记录url和视图函数的对应关系

    def wsgi_app(self,environ,start_response):
        req = Request(environ)
        response = self.dispatch_request(req) #进行请求的分发
        if response:#如果可以找到正确的匹配项
            response = Response(response, content_type='text/html; charset=UTF-8')
        else:#找不到,返回404NotFound
            response = Response('<h1>404 Not Found<h1>', content_type='text/html; charset=UTF-8', status=404)
        return response(environ, start_response)

    def __call__(self, environ, start_response):
        return self.wsgi_app(environ, start_response)

    def dispatch_request(self, req):
        try:
            url = req.path
            view = self.url_map.get(url,None)
            if view:
                response = view(req)
            else:
                response = None
        except HTTPException as e:
            response = e
        return response
    def add_url_rule(self,urls):
         for url in urls:
             self.url_map[url] = urls[url].get_func()

    def run(self, port=8090, ip='127.0.0.1', debug=True):
        #运行使用的是werkzeug的run_simple所创建的服务器
        run_simple(ip, port, self, use_debugger=debug, use_reloader=True)

测试:

from app import App,View,session
import json

class Index(View):
    def GET(self,request):
        return "hello world"
    def POST(self,request):
        # print(json.dumps(request.form['color']))
        return json.dumps({'1':'hello'})

class Test(View):
    def GET(self,request):
        return 'test'
    def POST(self,request):
        return json.dumps({'2':'hello'})

urls = {'/':Index,
        '/test':Test}

app = App()
app.add_url_rule(urls)
app.run()

测试GET方法结果:

在这里插入图片描述
测试POST方法:

在这里插入图片描述
参考:
https://github.com/pallets/werkzeug/blob/0.12.2/examples/webpylike/webpylike.py