问题 将REST行为添加到带有烧瓶的类中,用于蓝图的情况?


我正在处理一个python应用程序,它由多个分布式轻量级组件组成,使用它们进行通信 的RabbitMQ & 海带

组件侦听两个队列,并且可以在每个队列上接收多个消息类型。子类可以通过注册自定义处理程序来覆盖每种消息类型的处理方式。 这一切都很好。

我现在要求每个组件必须具有基本的REST / HTML接口。您的想法是将浏览器指向正在运行的组件,并获取有关当前正在执行的操作的实时信息(处理的消息,CPU使用情况,状态信息,日志等)

它需要是轻量级的,所以经过一些研究我已经确定了Flask(但我愿意接受建议)。在伪代码中,这意味着:

class Component:
   Queue A
   Queue B
   ...
   def setup(..):
     # connect to the broker & other initialization

   def start(..):
     # start the event loop and wait for work  

   def handle_msg_on_A(self,msg):
     # dispatch a msg to a handler depending on the msg type

   def handle_msg_on_B(self,msg):
     ...

   ...

并添加了许多视图方法:

   @app.route('/')
   def web_ui(self):
      # render to a template

   @app.route('/state')
   def get_state(self):
      # REST method to return some internal state info as JSON

   ...

但是,将Web UI固定到类似这样的类上会中断 固体 原则并带来继承问题(子类可能希望显示更多/更少的信息)。装饰器不是继承的,因此需要显式覆盖和重新装饰每个视图方法。也许使用mixin +反射可以以某种方式工作,但它感觉hackish。

相反,使用组合可以工作:将Web内容放在一个单独的类中,该类将url路由委托给嵌套组件上的一组固定的预定义多态方法。 这样,组件仍然没有意识到Flask,但代价是一些灵活性损失(可用方法的集合是固定的)。

我现在发现了 烧瓶蓝图 和 应用程序调度 看起来他们可以带来更好,更具扩展性的解决方案。但是,我还没有绕过它们。

我觉得我在这里错过了一个设计模式,并希望有更多烧瓶或有这类问题经验的人可以发表评论。


1259
2017-08-31 12:27


起源



答案:


在Flask 0.7中悄悄地引入了一些可能对你感兴趣的东西 - 可插拔视图。这些是 基于班级 而不是基于功能的端点 - 所以你可以使用 dispatch_request 管理状态转换的方法(仅在需要时覆盖它)。

与使用应用程序调度相反,这样做的好处就是获得 url_for 支持所有应用程序(而不是在跨越应用程序边界的URL中使用硬编码。)您必须确定这是否可能是您的应用程序的问题。

在伪代码中:

# File: Components.py
from flask.views import View

class Component(View):
    # Define your Component-specific application logic here

    dispatch_request(self, *url_args, **url_kwargs):
        # Define route-specific logic that all Components should have here.
        # Call Component-specific methods as necessary

class Tool_1(Component):
    pass

class Tool_2(Component):
    # Override methods here

# File: app.py
from flask import Flask
from yourapplication import Tool_1, Tool_2

app = Flask()

# Assuming you want to pass all additional parameters as one argument
app.add_url_rule("/tool_1/<path:options>", "tool1", view_func=Tool_1.as_view())

# Assuming you want to pass additional parameters separately
tool_2_view = Tool_2.as_view()
app.add_url_rule("/tool_2/", "tool2", view_func=tool_2_view )
app.add_url_rule("/tool_2/<option>", "tool2", view_func=tool_2_view)
app.add_url_rule("/tool_2/<option>/<filter>", "tool2", view_func=tool_2_view)

能够 如果您有一系列逻辑上连接在一起的组件,并且您不想记住放置,请在混合中添加蓝图 /prefix 在每个人的面前 add_url_rule 呼叫。但是如果你只有一系列的组件 大多 彼此独立,这是我使用的模式*。

*。 另一方面,如果他们需要 孤立 我将使用文档中推荐的Application Dispatch模式。


11
2017-08-31 17:23



很酷,谢谢你的答案,肯定会调查并批准答案,如果它成功了 - dgorissen
它有用吗@dgorissen - Sunil D S


答案:


在Flask 0.7中悄悄地引入了一些可能对你感兴趣的东西 - 可插拔视图。这些是 基于班级 而不是基于功能的端点 - 所以你可以使用 dispatch_request 管理状态转换的方法(仅在需要时覆盖它)。

与使用应用程序调度相反,这样做的好处就是获得 url_for 支持所有应用程序(而不是在跨越应用程序边界的URL中使用硬编码。)您必须确定这是否可能是您的应用程序的问题。

在伪代码中:

# File: Components.py
from flask.views import View

class Component(View):
    # Define your Component-specific application logic here

    dispatch_request(self, *url_args, **url_kwargs):
        # Define route-specific logic that all Components should have here.
        # Call Component-specific methods as necessary

class Tool_1(Component):
    pass

class Tool_2(Component):
    # Override methods here

# File: app.py
from flask import Flask
from yourapplication import Tool_1, Tool_2

app = Flask()

# Assuming you want to pass all additional parameters as one argument
app.add_url_rule("/tool_1/<path:options>", "tool1", view_func=Tool_1.as_view())

# Assuming you want to pass additional parameters separately
tool_2_view = Tool_2.as_view()
app.add_url_rule("/tool_2/", "tool2", view_func=tool_2_view )
app.add_url_rule("/tool_2/<option>", "tool2", view_func=tool_2_view)
app.add_url_rule("/tool_2/<option>/<filter>", "tool2", view_func=tool_2_view)

能够 如果您有一系列逻辑上连接在一起的组件,并且您不想记住放置,请在混合中添加蓝图 /prefix 在每个人的面前 add_url_rule 呼叫。但是如果你只有一系列的组件 大多 彼此独立,这是我使用的模式*。

*。 另一方面,如果他们需要 孤立 我将使用文档中推荐的Application Dispatch模式。


11
2017-08-31 17:23



很酷,谢谢你的答案,肯定会调查并批准答案,如果它成功了 - dgorissen
它有用吗@dgorissen - Sunil D S