Python中Web框架编写学习心得

学习廖雪峰老师的Python实战教程,在Web框架这部分看了大致一个多礼拜,前面的知识学的不够扎实,由于比较复杂,因此在这里总结下,也算是巩固了。

先看下框架的调用代码:

app = web.Application(loop=loop, middlewares=[logger_factory, response_factory])
init_jinja2(app, filters=dict(datetime=datetime_filter))
add_routes(app, 'handlers')
add_static(app)
  1. 使用web.Application类创建aiohttp server——app,其中loop为Eventloop用来处理HTTP请求,middlewares为中间件,在这里用来记录日志并处理handler返回的数据为web.response对象,这里看下response_factory的代码
async def response_factory(app, handler):
    async def response(request):
        logging.info('Response handler...')
        #获取handler的返回值,根据返回值的不同类型进行处理
        r = await handler(request)
        print(type(r))
        if isinstance(r, web.StreamResponse):
            return r
        if isinstance(r, bytes):
            resp = web.Response(body=r)
            resp.content_type = 'application/octet-stream'
            return resp
        if isinstance(r, str):
            if r.startswith('redirect:'):
                return web.HTTPFound(r[9:])
            resp = web.Response(body=r.encode('utf-8'))
            resp.content_type = 'text/html;charset=utf-8'
            return resp
        if isinstance(r, dict):
            template = r.get('__template__')
            if template is None:
                resp = web.Response(body=json.dumps(r, ensure_ascii=False, default=lambda o: o.__dict__).encode('utf-8'))
                resp.content_type = 'application/json;charset=utf-8'
                return resp
            else:
                resp = web.Response(body=app['__templating__'].get_template(template).render(**r).encode('utf-8'))
                resp.content_type = 'text/html;charset=utf-8'
                return resp
        if isinstance(r, int) and r >= 100 and r < 600:
            return web.Response(r)
        if isinstance(r, tuple) and len(r) == 2:
            t, m = r
            if isinstance(t, int) and t >= 100 and t < 600:
                return web.Response(t, str(m))
        # default:
        resp = web.Response(body=str(r).encode('utf-8'))
        resp.content_type = 'text/plain;charset=utf-8'
        return resp
    return response
  1. 使用jinjia2模板来构建前端页面,这里我们暂时没有用到
  2. 注册处理url的handler,aiohttp中的add_route函数进行注册,我们这里使用add_routes对'handlers'模块的handler进行批量注册
def add_route(app, fn):
    method = getattr(fn, '__method__', None)
    path = getattr(fn, '__route__', None)
    if path is None or method is None:
        raise ValueError('@get or @post not defined in %s.' % str(fn))
    if not asyncio.iscoroutinefunction(fn) and not inspect.isgeneratorfunction(fn):
        fn = asyncio.coroutine(fn)
    logging.info('add route %s %s => %s(%s)' % (method, path, fn.__name__, ', '.join(inspect.signature(fn).parameters.keys())))
    app.router.add_route(method, path, RequestHandler(app, fn))
def add_routes(app, module_name):
    #找到'.'则返回其所在位置,否则返回-1
    n = module_name.rfind('.')
    if n == (-1):
        #mod为包含module_name模块中全部属性和方法的list
        mod = __import__(module_name, globals(), locals())
    else:
        name = module_name[n+1:]
        mod = getattr(__import__(module_name[:n], globals(), locals(), [name]), name)
    for attr in dir(mod):
        #检查handler是否被@get或@post装饰
        if attr.startswith('_'):
            continue
        fn = getattr(mod, attr)
        if callable(fn):
            method = getattr(fn, '__method__', None)
            path = getattr(fn, '__route__', None)
            if method and path:
                add_route(app, fn)

这里出现了一个RequestHandler类,它具有call魔术方法,所以可以像调用函数一样调用其实例,这里RequestHandler类主要是对handler进行封装,获取request中传入的参数并传入handler中。

  1. add_static是用户处理如图片、js、css等静态资源的,仅供开发环境中方便使用,生产环境一般使用nginx或CDN之类的,这块我也还没有搞清楚,没办法梳理

最后把我的handler模块的代码贴出来,供参考:

from coroweb import get, post
from aiohttp import web

@get('/blog')
async def handler_url_blog(request):
    body='<h1>Awesome: /blog</h1>'
    return body

@get('/greeting')
async def handler_url_greeting(*,name,request):
    body='<h1>Awesome: /greeting %s</h1>'%name
    return body

@get('/input')
async def handler_url_input(request):
    body='<form action="/result" method="post">E-mail: <input type="email" name="user_email" /><input type="submit" /></form>'
    return body

@post('/result')
async def handler_url_result(*,user_email,request):
    body='<h1>您输入的邮箱是%s</h1>'%user_email
    return body

@get('/index')
async def handler_url_index(request):
    body='<h1>Awesome: /index</h1>'
    return body

@get('/create_comment')
async def handler_url_create_comment(request):
    body='<h1>Awesome: /create_comment</h1>'
    return body

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 123,218评论 18 134
  • github地址,欢迎大家提交更新。 express() express()用来创建一个Express的程序。ex...
    Programmer客栈阅读 1,592评论 0 1
  • 概念:适应性偏见 适应性偏见,就是人们对好的、坏的环境,最终都能适应的强大的行为心理。 一个人对任何一件事,有了之...
    洋_葱头阅读 284评论 0 0
  • 昨天背的原版书课程金句,今天再回想的时候发现已经遗忘了很多,这让我有些受打击。不过这就是现实——成长的过程不就是...
    木南Ruan阅读 44评论 0 0
  • 前言:吕二狗从小家境贫寒,在他6岁的时候因为敌军入侵了城池,在城中烧杀掳掠,他的父母也因此命归黄泉,只留下了一个无...
    雅俗共赏M阅读 223评论 0 0