返回顶部

3- sanic展示一个页面

展示一个页面

前面一章介绍[项目结构]的时候,很粗略地讲了下如何将rss的文章内容在网页上进行展示。

相信你应该已经了解清楚,`sanic`是怎么接收请求并返回被请求的资源的,简单来说概括如下:
- 接收请求
- 找到对应的路由并执行路由对应的视图函数
- [Jinja2]模板渲染返回视图

路由和视图函数

在此我假设你理解 `python` 中的装饰器,如果你并不清楚,可以看[装饰器]的介绍,回归正题,还记得第一节中的代码实例么?

from sanic import Sanic
from sanic.response import text

app = Sanic()

# 此处将路由 / 与视图函数 test 关联起来
@app.route("/")
async def test(request):
    return text('Hello World!')


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000) 

在前言介绍里,出现这几个名词 **路由 视图函数 视图** ,在上面那段代码中,`test` 就是视图函数。这是一段执行逻辑,比如客户端请求 `0.0.0.0:8000/` 此时返回的内容就是由`test` 这个视图函数提供的。

在我看来,视图函数就是一个纽带,它起到承上启下的作用,那么,到底是怎样的承上启下呢?让我们结合代码([sanic0.1.2源码])来分析下:

@app.route("/")
async def test(request):
    return text('Hello World!')

这个路由装饰器的作用很简单,就是将 `/` 这个 `uri` 与视图函数`test`关联起来,或许你可以将路由想象成一个 `dict`,

当客户端若请求 `0.0.0.0:8000/`,路由就会`get` `/` 对应的视图函数`test`,然后执行。其实真实情况和我们想象的差不多,

请看 `sanic.py` 中的第三十行:

def route(self, uri, methods=None):

    def response(handler):
        # 路由类的add 方法将视图函数handler 与uri 关联起来
        # 然后整个路由列表会新增一个 namedtuple 如下:
        # Route(handler=handler, methods=methods_dict, pattern=pattern, parameters=parameters)
        self.router.add(uri=uri, methods=methods, handler=handler)
        return handler

    return response

 

  

此时,路由就和 `uri` 对应的视图函数关联起来了,这就是承上,路由和视图函数就是这样对应的关系。

103行有个`handle_request`函数:

async def handle_request(self, request, response_callback):
    """
    Takes a request from the HTTP Server and returns a response object to be sent back
    The HTTP Server only expects a response object, so exception handling must be done here
    :param request: HTTP Request object
    :param response_callback: Response function to be called with the response as the only argument
    :return: Nothing
    """

当服务器监听到某个请求的时候,`handle_request`可以通过参数中的`request.url` 来找到视图函数并且执行,随即生成视图返回,这便是所谓的启下。

其实浏览器显示的内容就是我们所谓的视图,视图是视图函数生成的,其中Jinja2起到模板渲染的作用。

蓝图

到这里,你一定已经很明白sanic框架是怎么处理一个请求并将视图返回给客户端,然后也掌握了如何编写自己定义的模板(html)以及样式(css),通过前面一节[项目结构的介绍,我们可以总结出如下经验:

  - 对于css、js等静态文件,常规操作是将其放在自己建立的statics下面
  - 对于html模板,常规操作是将其放在自己建立的templates下面
  - 视图函数(即服务的逻辑实现)放在自己建立的views下面

 

是时候考虑以下这种情况了,你需要编写一个比较复杂的http服务,你需要定义几百个路由,在编写过程中你会发现有许多不顺心的地方:

- 各种不同类型的路由互相交杂在一起,命名困难,管理困难
- 不同页面的css文件同样堆积在一起,html也是如此

实在是令人烦恼,可能你想要一个模块化的编写方式,一个文件编写后台,url统一是`/admin/***`,一个文件编写发帖,`/post/***` 等等,各个文件下面url自动会带上自定义的前缀,不用考虑命名问题,不用重复写url前缀,多么美好

**Blueprint**,就是sanic为你提供的解决方案,依然是上节rss的例子,让我们利用Blueprint来简单实现一下我们的需求,比如我们要构建的网站分为两个部分:

- `/json/index` 返回json格式的数据
- `/html/index` 返回html视图

我们在上节代码的基础上添加Blueprint,先看看定好的项目结构

.
├── __init__.py
├── config
│   ├── __init__.py
│   ├── config.py
│   ├── dev_config.py
│   └── pro_config.py
├── run.py
├── statics
│   ├── rss_html # rss_html蓝图的 css js 文件存放目录
│   │   ├── css
│   │   │   └── main.css
│   │   └── js
│   │   │   └── main.js
│   └── rss_json # rss_json蓝图的 css js 文件存放目录
│   │   ├── css
│   │   │   └── main.css
│   │   └── js
│   │   │   └── main.js
├── templates
│   ├── rss_html # rss_html蓝图的 html 文件存放目录
│   │   ├── index.html
│   │   └── rss.html
│   └── rss_json # rss_json蓝图的 html 文件存放目录
│   └── index.html
└── views
├── __init__.py
├── rss_html.py # rss_html 蓝图
└── rss_json.py # rss_json 蓝图

 在这里面蓝图放在views的包中,里面的rss_html.py和rss_json.py这两个文件内部分别定义了一个蓝图,在views包中的__init__.py分别导入这两个蓝图的名字,供启动文件去使用

rss_html.py的代码如下所示:

可以通过url_prefix给蓝图加前缀
json_bp = Blueprint('rss_json', url_prefix='json')

在这里可以对蓝图配置静态的文件

#!/usr/bin/env python
import sys

from feedparser import parse
from jinja2 import Environment, PackageLoader, select_autoescape
from sanic import Blueprint
from sanic.response import html

from src.config import CONFIG

# https://github.com/channelcat/sanic/blob/5bb640ca1706a42a012109dc3d811925d7453217/examples/jinja_example/jinja_example.py
# 开启异步特性  要求3.6+
enable_async = sys.version_info >= (3, 6)

html_bp = Blueprint('rss_html', url_prefix='html')
html_bp.static('/statics/rss_html', CONFIG.BASE_DIR + '/statics/rss_html')

# jinjia2 config
env = Environment(
    loader=PackageLoader('views.rss_html', '../templates/rss_html'),
    autoescape=select_autoescape(['html', 'xml', 'tpl']),
    enable_async=enable_async)


async def template(tpl, **kwargs):
    template = env.get_template(tpl)
    rendered_template = await template.render_async(**kwargs)
    return html(rendered_template)

@html_bp.route("/")
async def index(request):
    return await template('index.html')

@html_bp.route("/index")
async def rss_html(request):
    url = "http://blog.howie6879.cn/atom.xml"
    feed = parse(url)
    articles = feed['entries']
    data = []
    for article in articles:
        data.append({
            "title": article["title_detail"]["value"],
            "link": article["link"],
            "published": article["published"]
        })
    return await template('rss.html', articles=data)
View Code

 

 rss_json.py的代码如下所示

 在这里可以对蓝图配置静态的文件

#!/usr/bin/env python
import sys

from feedparser import parse
from jinja2 import Environment, PackageLoader, select_autoescape
from sanic import Blueprint
from sanic.response import html, json

from src.config import CONFIG

# https://github.com/channelcat/sanic/blob/5bb640ca1706a42a012109dc3d811925d7453217/examples/jinja_example/jinja_example.py
# 开启异步特性  要求3.6+
enable_async = sys.version_info >= (3, 6)

json_bp = Blueprint('rss_json', url_prefix='json')

# 下面的第一个参数其实不用写的那么全面
json_bp.static('/statics/rss_json', CONFIG.BASE_DIR + '/statics/rss_json')

# jinjia2 config
env = Environment(
    loader=PackageLoader('views.rss_json', '../templates/rss_json'),
    autoescape=select_autoescape(['html', 'xml', 'tpl']),
    enable_async=enable_async)


async def template(tpl, **kwargs):
    template = env.get_template(tpl)
    rendered_template = await template.render_async(**kwargs)
    return html(rendered_template)


@json_bp.route("/")
async def index(request):
    return await template('index.html')


@json_bp.route("/index")
async def rss_json(request):
    url = "http://blog.howie6879.cn/atom.xml"
    feed = parse(url)
    articles = feed['entries']
    data = []
    for article in articles:
        data.append({"title": article["title_detail"]["value"], "link": article["link"]})
    return json(data)
View Code

 

 views包中的__init__.py的代码如下所示(主要提供给启动文件去调用):

#!/usr/bin/env python
from .rss_json import json_bp
from .rss_html import html_bp

 

 启动文件run.py的代码如下所示:

# !/usr/bin/env python
import sys

sys.path.append('../')
from src.views import app
from src.config import CONFIG


app.static('/statics', CONFIG.BASE_DIR + '/statics')
app.config.from_object(CONFIG)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000,debug=app.config.DEBUG)

不知你是否感受到这样编写服务的好处,让我们列举下:

  - 每个蓝图都有其自己定义的前缀,如 /html/ /json/
  - 不同蓝图下面route可相同且互不冲突
  - html以及css等文件可根据蓝图名称目录引用,条理清晰易扩展
  - 模块化

不多说,看运行效果,现在,请访问:

http://0.0.0.0:8000/html/

http://0.0.0.0:8000/json/

http://0.0.0.0:8000/html/index

http://0.0.0.0:8000/json/index

感觉这个设计方便了全世界

蓝图的具体使用还请诸位多多摸索,说不定能发掘出更多好玩的玩法以及更加优雅的编码方式,以上内容转自 代码地址:[demo04]

 

posted @ 2018-01-19 17:47  Crazymagic  阅读(1186)  评论(0编辑  收藏  举报