URL路线功能的API设计中的代码复制与现实世界对象方法

发布于 2025-01-31 17:00:09 字数 842 浏览 2 评论 0原文

我的API设计中的代码重复对象方法与URL路由功能:

# door_model.py

class Door:                               
    def open(self):                       # "Door.open" written once...
       ...
# http_api.py (the HTTP server is separated from the real-world object models)

@app.route('/api/door/open')              # ... written twice
def dooropen():                           # ... written three times
    d.open()                              # ... written four times!

d = Door()

如何避免在类似的API设计中避免这种不必要的名称重复?(同时保持现实世界对象之间的分离模型与HTTP服务器)。

使用对象模型(使用方法)和URL路由功能时,是否有一个通用模式可以避免名称的不必要重复? (几乎是模型视图控制器模式)

另请参见用Python烧瓶的对象的API URL路由的关联方法

I have code duplication in my API design for the object methods vs. the URL routing functions:

# door_model.py

class Door:                               
    def open(self):                       # "Door.open" written once...
       ...
# http_api.py (the HTTP server is separated from the real-world object models)

@app.route('/api/door/open')              # ... written twice
def dooropen():                           # ... written three times
    d.open()                              # ... written four times!

d = Door()

How to avoid this unnecessary duplication of names in a similar API design? (while keeping a separation between real-world object models vs. HTTP server).

Is there a general pattern to avoid unnecessary duplication of names when using an object model (with methods), and URL routes functions? (nearly a Model View Controller pattern)

See also Associate methods of an object to API URL routes with Python Flask.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

闻呓 2025-02-07 17:00:09

如果我们为每个模型操作声明一条路由,并为每个模型执行相同的操作(在您的情况下,以有或没有参数为单位的相应方法),它将复制代码。通常,人们使用设计模式(主要用于大型项目)和算法来避免代码重复。我想显示一个简单的示例,该示例定义了一个通用路由并在一个处理程序功能中处理所有请求。

假设我们有以下文件结构。

application/
├─ models/
│  ├─ door.py
│  ├─ window.py
├─ main.py

的原型看起来像是

# door.py

class Door:

    def open(self):
        try:
            # open the door
            return 0
        except:
            return 1

    def close(self):
        try:
            # close the door
            return 0
        except:
            return 1

    def openlater(self, waitseconds=2):
        print("Waiting for ", waitseconds)
        try:
            # wait and open the door
            return 0
        except:
            return 1

我有条件地设置了C,0成功的出口代码,1以备错误或失败。

我们必须将模型动作分为一个共同的结构,将模型动作分为一个。

+----------+----------+------------+----------------------+
| API base |  model   | action     | arguments (optional) |
+----------+----------+------------+----------------------+
| /api     | /door    | /open      |                      |
| /api     | /door    | /close     |                      |
| /api     | /door    | /openlater | ?waitseconds=10      |
| /api     | /window  | /open      |                      |
| /api     | /<model> | /<action>  |                      |
+----------+----------+------------+----------------------+

通过使用界面将组分开后,我们可以为每个界面实现一个通用处理程序。

通用处理程序实现,

# main.py

from flask import Flask, Response, request
import json
from models.door import Door
from models.window import Window

app = Flask(__name__)

door = Door()
window = Window()

MODELS = {
    "door": door,
    "window": window,
}

@app.route("/api/<model>/<action>")
def handler(model, action):
    model_ = MODELS.get(model)
    action_ = getattr(model_, action, None)
    if callable(action_):
        try:
            error = action_(**request.args)
            if not error:
                return Response(json.dumps({
                    "message": "Operation succeeded"
                }), status=200, mimetype="application/json")
            return Response(json.dumps({
                "message": "Operation failed"
            }), status=400, mimetype="application/json")
        except (TypeError, Exception):
            return Response(json.dumps({
                "message": "Invalid parameters"
            }), status=400, mimetype="application/json")
    return Response(json.dumps({
        "message": "Wrong action"
    }), status=404, mimetype="application/json")

if __name__ == "__main__":
    app.run()

因此您可以使用不同的API路径和查询参数来控制模型的操作。

If we declare a route for every model action and do the same things for each (in your case, call the corresponding method with or without parameter), it will duplicate the code. Commonly, people use design patterns (primarily for big projects) and algorithms to avoid code duplications. And I want to show a simple example that defines one generic route and handles all requests in one handler function.

Suppose we have the following file structure.

application/
├─ models/
│  ├─ door.py
│  ├─ window.py
├─ main.py

The prototype of the Door looks like

# door.py

class Door:

    def open(self):
        try:
            # open the door
            return 0
        except:
            return 1

    def close(self):
        try:
            # close the door
            return 0
        except:
            return 1

    def openlater(self, waitseconds=2):
        print("Waiting for ", waitseconds)
        try:
            # wait and open the door
            return 0
        except:
            return 1

Where I conditionally set exit codes of the C, 0 for success and 1 for error or failure.

We must separate and group the model actions into one as they have a common structure.

+----------+----------+------------+----------------------+
| API base |  model   | action     | arguments (optional) |
+----------+----------+------------+----------------------+
| /api     | /door    | /open      |                      |
| /api     | /door    | /close     |                      |
| /api     | /door    | /openlater | ?waitseconds=10      |
| /api     | /window  | /open      |                      |
| /api     | /<model> | /<action>  |                      |
+----------+----------+------------+----------------------+

After we separate our groups by usage interface, we can implement a generic handler for each.

Generic handler implementation

# main.py

from flask import Flask, Response, request
import json
from models.door import Door
from models.window import Window

app = Flask(__name__)

door = Door()
window = Window()

MODELS = {
    "door": door,
    "window": window,
}

@app.route("/api/<model>/<action>")
def handler(model, action):
    model_ = MODELS.get(model)
    action_ = getattr(model_, action, None)
    if callable(action_):
        try:
            error = action_(**request.args)
            if not error:
                return Response(json.dumps({
                    "message": "Operation succeeded"
                }), status=200, mimetype="application/json")
            return Response(json.dumps({
                "message": "Operation failed"
            }), status=400, mimetype="application/json")
        except (TypeError, Exception):
            return Response(json.dumps({
                "message": "Invalid parameters"
            }), status=400, mimetype="application/json")
    return Response(json.dumps({
        "message": "Wrong action"
    }), status=404, mimetype="application/json")

if __name__ == "__main__":
    app.run()

So you can control the actions of the models by using different API paths and query parameters.

您可以创建动态路线。您的情况的动态路线将是api/door/&lt; action&gt;

创建这样的路由以具有动态URL:

@app.route('api/door/<action:str>')
def door(action):
    
   door = Door()
   if action in door.actions:
       if action.lower() == 'open':
           door.open()
           r = 'oppened door'

       return r
        
   

创建一个名为“操作”的类变量以使代码工作。例如,像这样的action = ['open','Close']

You can create dynamic routes. A dynamic route for your case would be api/door/<action>.

Create a route like this to have a dynamic url:

@app.route('api/door/<action:str>')
def door(action):
    
   door = Door()
   if action in door.actions:
       if action.lower() == 'open':
           door.open()
           r = 'oppened door'

       return r
        
   

Create a class variable called actions to make the code work. For example like this actions = ['open','close']

橘香 2025-02-07 17:00:09

利用烧瓶蓝图模式,

您可以在http_api.py.py

app = Flask(__name__)
# ... configs to app instance ...
app.register_blueprint(door_routes, url_prefix="/api/door")

api/door.py.py

door_routes = Blueprint("door_routes", __name__)

door = Door()
@door_routes.route("/open")
def open():
    d.open() 
    return 

#other routes

或其他情况下使用,您可以使用:

class Door():
     def open(self):
        print("opened")

d = Door()

@app.route("/api/door/<action>", methods=["POST", "GET"])
def catch_all(action):
    try:
       function = getattr(d,action)
       resp = function()
       #return jsonify(resp),200 or the below line
       return f"Executed {action}"
    except Exception as e:
       return f"URL {action} not found" 

    
    

You can leverage the flask blueprint pattern

In your http_api.py use

app = Flask(__name__)
# ... configs to app instance ...
app.register_blueprint(door_routes, url_prefix="/api/door")

In Your api/door.py

door_routes = Blueprint("door_routes", __name__)

door = Door()
@door_routes.route("/open")
def open():
    d.open() 
    return 

#other routes

Or alternatively, you can use:

class Door():
     def open(self):
        print("opened")

d = Door()

@app.route("/api/door/<action>", methods=["POST", "GET"])
def catch_all(action):
    try:
       function = getattr(d,action)
       resp = function()
       #return jsonify(resp),200 or the below line
       return f"Executed {action}"
    except Exception as e:
       return f"URL {action} not found" 

    
    
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文