千家信息网

如何用Python写一个简单的Web框架

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,如何用Python写一个简单的Web框架,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。一、概述在Python中,WSGI
千家信息网最后更新 2026年11月01日如何用Python写一个简单的Web框架

如何用Python写一个简单的Web框架,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。

一、概述

在Python中,WSGI(Web Server Gateway Interface)定义了Web服务器与Web应用(或Web框架)之间的标准接口。在WSGI的规范下,各种各样的Web服务器和Web框架都可以很好的交互。

由于WSGI的存在,用Python写一个简单的Web框架也变得非常容易。然而,同很多其他的强大软件一样,要实现一个功能丰富、健壮高效的Web框架并非易事;如果您打算这么做,可能使用一个现成的Web框架(如 Django、Tornado、web.py 等)会是更合适的选择。

尝试写一个类似web.py的Web框架。好吧,我承认我夸大其辞了:首先,web.py并不简单;其次,只重点实现了 URL调度(URL dispatch)部分。

二、从demo_app开始

首先,作为一个初步体验,我们可以借助 wsgiref.simple_server 来搭建一个简单无比(trivial)的Web应用:

#!/usr/bin/env python  # -*- coding: utf-8 -*-     from wsgiref.simple_server import make_server, demo_app     httpd = make_server('', 8086, demo_app)  sa = httpd.socket.getsockname()  print 'http://{0}:{1}/'.format(*sa)     # Respond to requests until process is killed  httpd.serve_forever()

运行脚本:

$ python code.py  http://0.0.0.0:8086/

打开浏览器,输入http://0.0.0.0:8086/后可以看到:一行"Hello world!" 和 众多环境变量值。

三、WSGI中的application

WSGI中规定:application是一个 可调用对象(callable object),它接受 environ 和 start_response 两个参数,并返回一个 字符串迭代对象。

其中,可调用对象 包括 函数、方法、类 或者 具有__call__方法的 实例;environ 是一个字典对象,包括CGI风格的环境变量(CGI-style environment variables)和 WSGI必需的变量(WSGI-required variables);start_response 是一个可调用对象,它接受两个 常规参数(status,response_headers)和 一个 默认参数(exc_info);字符串迭代对象 可以是 字符串列表、生成器函数 或者 具有__iter__方法的可迭代实例。更多细节参考 Specification Details。

The Application/Framework Side 中给出了一个典型的application实现:

#!/usr/bin/env python  # -*- coding: utf-8 -*-     """application.py"""     def simple_app(environ, start_response):      """Simplest possible application object"""      status = '200 OK'      response_headers = [('Content-type', 'text/plain')]      start_response(status, response_headers)      return ['Hello world!\n']

现在用simple_app来替换demo_app:

#!/usr/bin/env python  # -*- coding: utf-8 -*-     """code.py"""     from wsgiref.simple_server import make_server  from application import simple_app as app     if __name__ == '__main__':      httpd = make_server('', 8086, app)      sa = httpd.socket.getsockname()      print 'http://{0}:{1}/'.format(*sa)         # Respond to requests until process is killed      httpd.serve_forever()

运行脚本code.py后,访问http://0.0.0.0:8086/就可以看到那行熟悉的句子:Hello world!

四、区分URL

倒腾了一阵子后,您会发现不管如何改变URL中的path部分,得到的响应都是一样的。因为simple_app只识别host+port部分。

为了对URL中的path部分进行区分处理,需要修改application.py的实现。

首先,改用 类 来实现application:

#!/usr/bin/env python  # -*- coding: utf-8 -*-     """application.py"""     class my_app:      def __init__(self, environ, start_response):          self.environ = environ          self.start = start_response         def __iter__(self):          status = '200 OK'          response_headers = [('Content-type', 'text/plain')]          self.start(status, response_headers)          yield "Hello world!\n"

然后,增加对URL中path部分的区分处理:

#!/usr/bin/env python  # -*- coding: utf-8 -*-     """application.py"""     class my_app:      def __init__(self, environ, start_response):          self.environ = environ          self.start = start_response         def __iter__(self):          path = self.environ['PATH_INFO']          if path == "/":              return self.GET_index()          elif path == "/hello":              return self.GET_hello()          else:              return self.notfound()         def GET_index(self):          status = '200 OK'          response_headers = [('Content-type', 'text/plain')]          self.start(status, response_headers)          yield "Welcome!\n"         def GET_hello(self):          status = '200 OK'          response_headers = [('Content-type', 'text/plain')]          self.start(status, response_headers)          yield "Hello world!\n"         def notfound(self):          status = '404 Not Found'          response_headers = [('Content-type', 'text/plain')]          self.start(status, response_headers)          yield "Not Found\n"

修改code.py中的from application import simple_app as app,用my_app来替换simple_app后即可体验效果。

五、重构

上面的代码虽然奏效,但是在编码风格和灵活性方面有很多问题,下面逐步对其进行重构。

1、正则匹配URL

消除URL硬编码,增加URL调度的灵活性:

#!/usr/bin/env python  # -*- coding: utf-8 -*-     """application.py"""     import re ##########修改点     class my_app:         urls = (          ("/", "index"),          ("/hello/(.*)", "hello"),      ) ##########修改点         def __init__(self, environ, start_response):          self.environ = environ          self.start = start_response         def __iter__(self): ##########修改点          path = self.environ['PATH_INFO']          method = self.environ['REQUEST_METHOD']             for pattern, name in self.urls:              m = re.match('^' + pattern + '$', path)              if m:                  # pass the matched groups as arguments to the function                  args = m.groups()                  funcname = method.upper() + '_' + name                  if hasattr(self, funcname):                      func = getattr(self, funcname)                      return func(*args)             return self.notfound()         def GET_index(self):          status = '200 OK'          response_headers = [('Content-type', 'text/plain')]          self.start(status, response_headers)          yield "Welcome!\n"         def GET_hello(self, name): ##########修改点          status = '200 OK'          response_headers = [('Content-type', 'text/plain')]          self.start(status, response_headers)          yield "Hello %s!\n" % name         def notfound(self):          status = '404 Not Found'          response_headers = [('Content-type', 'text/plain')]          self.start(status, response_headers)          yield "Not Found\n"

2、DRY

消除GET_*方法中的重复代码,并且允许它们返回字符串:

#!/usr/bin/env python  # -*- coding: utf-8 -*-     """application.py"""     import re     class my_app:         urls = (          ("/", "index"),          ("/hello/(.*)", "hello"),      )         def __init__(self, environ, start_response): ##########修改点          self.environ = environ          self.start = start_response          self.status = '200 OK'          self._headers = []         def __iter__(self): ##########修改点          result = self.delegate()          self.start(self.status, self._headers)             # 将返回值result(字符串 或者 字符串列表)转换为迭代对象          if isinstance(result, basestring):              return iter([result])          else:              return iter(result)         def delegate(self): ##########修改点          path = self.environ['PATH_INFO']          method = self.environ['REQUEST_METHOD']             for pattern, name in self.urls:              m = re.match('^' + pattern + '$', path)              if m:                  # pass the matched groups as arguments to the function                  args = m.groups()                  funcname = method.upper() + '_' + name                  if hasattr(self, funcname):                      func = getattr(self, funcname)                      return func(*args)             return self.notfound()         def header(self, name, value): ##########修改点          self._headers.append((name, value))         def GET_index(self): ##########修改点          self.header('Content-type', 'text/plain')          return "Welcome!\n"         def GET_hello(self, name): ##########修改点          self.header('Content-type', 'text/plain')          return "Hello %s!\n" % name         def notfound(self): ##########修改点          self.status = '404 Not Found'          self.header('Content-type', 'text/plain')          return "Not Found\n"

3、抽象出框架

为了将类my_app抽象成一个独立的框架,需要作出以下修改:

  • 剥离出其中的具体处理细节:urls配置 和 GET_*方法(改成在多个类中实现相应的GET方法)

  • 把方法header实现为类方法(classmethod),以方便外部作为功能函数调用

  • 改用 具有__call__方法的 实例 来实现application

修改后的application.py(最终版本):

#!/usr/bin/env python  # -*- coding: utf-8 -*-     """application.py"""     import re     class my_app:      """my simple web framework"""         headers = []         def __init__(self, urls=(), fvars={}):          self._urls = urls          self._fvars = fvars         def __call__(self, environ, start_response):          self._status = '200 OK' # 默认状态OK          del self.headers[:] # 清空上一次的headers             result = self._delegate(environ)          start_response(self._status, self.headers)             # 将返回值result(字符串 或者 字符串列表)转换为迭代对象          if isinstance(result, basestring):              return iter([result])          else:              return iter(result)         def _delegate(self, environ):          path = environ['PATH_INFO']          method = environ['REQUEST_METHOD']             for pattern, name in self._urls:              m = re.match('^' + pattern + '$', path)              if m:                  # pass the matched groups as arguments to the function                  args = m.groups()                  funcname = method.upper() # 方法名大写(如GET、POST)                  klass = self._fvars.get(name) # 根据字符串名称查找类对象                  if hasattr(klass, funcname):                      func = getattr(klass, funcname)                      return func(klass(), *args)             return self._notfound()         def _notfound(self):          self._status = '404 Not Found'          self.header('Content-type', 'text/plain')          return "Not Found\n"         @classmethod      def header(cls, name, value):          cls.headers.append((name, value))

对应修改后的code.py(最终版本):

#!/usr/bin/env python  # -*- coding: utf-8 -*-     """code.py"""     from application import my_app     urls = (      ("/", "index"),      ("/hello/(.*)", "hello"),  )     wsgiapp = my_app(urls, globals())     class index:      def GET(self):          my_app.header('Content-type', 'text/plain')          return "Welcome!\n"     class hello:      def GET(self, name):          my_app.header('Content-type', 'text/plain')          return "Hello %s!\n" % name     if __name__ == '__main__':      from wsgiref.simple_server import make_server      httpd = make_server('', 8086, wsgiapp)         sa = httpd.socket.getsockname()      print 'http://{0}:{1}/'.format(*sa)         # Respond to requests until process is killed      httpd.serve_forever()

当然,您还可以在code.py中配置更多的URL映射,并实现相应的类来对请求作出响应。

主要参考了 How to write a web framework in Python(作者 anandology 是web.py代码的两位维护者之一,另一位则是大名鼎鼎却英年早逝的 Aaron Swartz),在此基础上作了一些调整和修改,并掺杂了自己的一些想法。

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注行业资讯频道,感谢您对的支持。

方法 框架 字符 字符串 对象 utf-8 部分 迭代 代码 函数 参数 实例 更多 可调 处理 两个 功能 变量 服务器 灵活性 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 广州淘业互联网络科技 科技平台互联网 北京互联网络科技有限公司 亿账通互联网科技 广州善班互联网科技 上海卓越科技 互联网 互联网科技宣传片 互联网科技公司做什么 互联网教育科技公司vipkid 互联网5g黑科技有哪些 恒生互联网科技指数跳水 米聚互联网科技南京有限公司排面 链环科技传统企业怎么转型互联网 山东子淼互联网科技有限公司 中天科技中国工业互联网 吉林省码上互联网科技电话 互联网科技平台产品运营 长沙渺峰互联网科技有限公司 上海旺乐尼互联网科技有限公司 北京瑞云互联网科技有限公司 互联网科技工作委员会副主任毛军 科技创新互联网企业 顺玮阁互联网科技 互联网科技大会案例 开启互联网科技时代小说 中盈科技互联网信息有限公司 语蝶互联网科技有限公司 赶超互联网科技 微信多开 电子科技大学互联网创业大赛 互联网科技创新创业项目
0