PECAN & WSGI

举报
Jiang Yuan 发表于 2019/12/31 16:24:44 2019/12/31
【摘要】 一、PECAN1、简介 pecan是基于python实现的一个轻量化web框架,采用对象分发方式来进行URL路由,其本身不支持session、database等功能,专注于HTTP本身。Pecan也会支持一些扩展属性来建立基于HTTP的应用,包括:基于对象分发的简单路由全面支持REST风格控制器可扩展安全框架可扩展模板语言支持可扩展JSON支持基于Python的简单配置2、框架 ...

一、PECAN

1、简介

    pecan是基于python实现的一个轻量化web框架,采用对象分发方式来进行URL路由,其本身不支持session、database等功能,专注于HTTP本身。Pecan也会支持一些扩展属性来建立基于HTTP的应用,包括:

    • 基于对象分发的简单路由

    • 全面支持REST风格控制器

    • 可扩展安全框架

    • 可扩展模板语言支持

    • 可扩展JSON支持

    • 基于Python的简单配置

2、框架

    以watcher中api组块为例,其代码结构如下图1:

image.png

以此代码结构介绍pecan各模块:

(1)controllers:http路由的核心逻辑,包括一个RootController作为URL路由的根目录

(2)middleware:鉴权相关

(3)acl.py:鉴权相关

(4)app.py:包含get_pecan_config()方法来加载配置和setup_app()方法来创建应用

(5)app.wsgi:None

(6)config.py:pecan配置参数,定义访问入口等信息

(7)hooks.py:钩子类

3、代码示例

(1)controller

class RootController(rest.RestController)
    
    v1 = v1_root.V1Controller()
    
    @wsme_pecan.wsexpose(Root)
    def get(sef):
        return Root.convert()

RootController类继承了pecan.rest.RestController类,在其中定义属性v1,当使用/v1来访问是,即使用该v1对象进行路由,此即对象路由分发。

在get()方法头部使用wsme_pecan.wsexpose注释,将方法暴露出去,才能够外部访问。

(2)app.py

def get_pecan_config():
    filename = api_config.__file__.replace('.pyc', '.py')   # get the absolute path of the pecan config.py
    return pecan.configuration.conf_from_file(filename)


def setup_app(config=None):      # the main function, start listening
    if not config:
        config = get_pecan_config()
    app_conf = dict(config.app)
    app = pecan.make_app(
        app_conf.pop('root'),
        logging=getattr(config, 'logging', {}),
        **app_conf)
    return app


class VersionSelectorApplication(object):
    def __init__(self):
        pc = get_pecan_config()
        self.v1 = setup_app(config=pc)

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

(3)config.py

server = {
    'port': '9323',
    'host': '127.0.0.1'
}

app = {
    'root': 'rtcloud.api.controllers.root.RootController',
    'modules': ['rtcloud.api'],
    'hooks': [
        hooks.ContextHook(),
        hooks.NoExceptionTracebackHook(),
    ],
    'static_root': '%(confdir)s/public',
    'enable_acl': True,
    'acl_public_routes': [
        '/',
    ],
}

# WSME Configurations
# See https://wsme.readthedocs.org/en/latest/integrate.html#configuration
wsme = {
    'debug': cfg.CONF.get("debug") if "debug" in cfg.CONF else False,
}

PECAN_CONFIG = {
    "server": server,
    "app": app,
    "wsme": wsme,
}

(4)hooks.py

class NoExceptionTracebackHook(hooks.PecanHook):
    """Workaround rpc.common: deserialize_remote_exception.

    deserialize_remote_exception builds rpc exception traceback into error
    message which is then sent to the client. Such behavior is a security
    concern so this hook is aimed to cut-off traceback from the error message.
    """
    # NOTE(max_lobur): 'after' hook used instead of 'on_error' because
    # 'on_error' never fired for wsme+pecan pair. wsme @wsexpose decorator
    # catches and handles all the errors, so 'on_error' dedicated for unhandled
    # exceptions never fired.
    def after(self, state):
        # Omit empty body. Some errors may not have body at this level yet.
        if not state.response.body:
            return

        # Do nothing if there is no error.
        # Status codes in the range 200 (OK) to 399 (400 = BAD_REQUEST) are not
        # an error.
        if (http_client.OK <= state.response.status_int <
                http_client.BAD_REQUEST):
            return

        json_body = state.response.json
        # Do not remove traceback when traceback config is set
        if cfg.CONF.debug:
            return

        faultstring = json_body.get('faultstring')
        traceback_marker = 'Traceback (most recent call last):'
        if faultstring and traceback_marker in faultstring:
            # Cut-off traceback.
            faultstring = faultstring.split(traceback_marker, 1)[0]
            # Remove trailing newlines and spaces if any.
            json_body['faultstring'] = faultstring.rstrip()
            # Replace the whole json. Cannot change original one because it's
            # generated on the fly.
            state.response.json = json_body

pecan.hooks提供了有on_route()、before()、after()和on_error()四种方法。

二、WSGI

1、简介

    WSGI(Web Server Gateway Interface),即Web服务器网关接口,是为Python定义的Web服务器和Web应用或框架之间简单而通用的接口。引用一下解释来自知乎网友翻译PEP3333

WSGI 接口有服务端和应用端两部分,服务端也可以叫网关端,应用端也叫框架端。服务端调用一个由应用端提供的可调用对象。如何提供这个对象,由服务端决定。例如某些服务器或者网关需要应用的部署者写一段脚本,以创建服务器或者网关的实例,并且为这个实例提供一个应用实例。另一些服务器或者网关则可能使用配置文件或其他方法以指定应用实例应该从哪里导入或获取。

    接口定义非常简单,以“Hello, web!”为例如下:

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])    
    return [b'<h1>Hello, web!</h1>']

函数必须由WSGI服务器来调用。

2、代码示例

class WSGIService(service.ServiceBase):
    """Provides ability to launch rtcloud API from wsgi app."""

    def __init__(self, service_name, use_ssl=False):
        """Initialize, but do not start the WSGI server.

        :param service_name: The service name of the WSGI server.
        :param use_ssl: Wraps the socket in an SSL context if True.
        """
        self.service_name = service_name
        self.app = app.VersionSelectorApplication()
        self.workers = (CONF.api.workers or
                        processutils.get_worker_count())
        self.server = wsgi.Server(CONF, self.service_name, self.app,
                                  host=CONF.api.host,
                                  port=CONF.api.port,
                                  use_ssl=use_ssl,
                                  logger_name=self.service_name)

在上述代码中,通过self.server.start()即可开启一个基于WSGI的服务。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。