在单个视图方法中接受多个请求类型的更好方法是什么?

时间:2013-07-18 06:48:13

标签: python flask

我正在尝试向各种请求方法公开API(GET,url x-www-form-urlencoded POST和json POST):

@app.route('/create', methods=['GET', 'POST'])
def create_file():
    if request.method == 'GET':
        n = request.args.get('n')
        t = request.args.get('t')
    if request.method == 'POST':
        if request.json:
            n = request.json['n']
            t = request.json['t']
        else:
            n = request.form['n']
            t = request.form['t']
    try:
        n = int(n)
    except:
        n = 1
    ...

上面看起来太冗长了。是否有更简单或更好的写作方式?感谢。

4 个答案:

答案 0 :(得分:15)

这看起来更好吗?在我看来,如果你可以接受将JSON POST请求移动到不同的路径(你真的应该这样做),它会更清洁。

def _create_file(n, t):
    try:
        n = int(n)
    except:
        n = 1
    ...

@app.route('/create')
def create_file():
    n = request.args.get('n')
    t = request.args.get('t')
    return _create_file(n, t)

@app.route('/create', methods = ['POST'])
def create_file_form():
    n = request.form.get('n')
    t = request.form.get('t')
    return _create_file(n, t)

@app.route('/api/create', methods = ['POST'])
def create_file_json():
    if not request.json:
        abort(400); # bad request
    n = request.json.get('n')
    t = request.json.get('t')
    return _create_file(n, t)

答案 1 :(得分:4)

没有什么能阻止您将代码重写为:

@app.route('/create', methods=['GET', 'POST'])
def create_file():
    params = None
    if request.method == 'GET':
        params = request.args
    if request.method == 'POST':
        if request.json:
            params = request.json
        else:
            params = request.form

    n = params.get('n')
    t = params.get('t')

    try:
        n = int(n)
    except:
        n = 1
    ...

答案 2 :(得分:0)

按其他人的建议使用Flask-Restful扩展名。然后,您可以执行以下操作:

class CreateFile(Resource):
    def get(self):
       args = parser.parse_args()
       n,t = args['n'], args['t']

    def post(self, todo_id):
       # do post stuff

依旧......

答案 3 :(得分:0)

我不知道这是你正在寻找的,因为你已经接受了答案,但是你可以通过应用标准的重构技术来消除大量的冗长。首先,每项职能单一责任:

@app.route('/create', methods=['GET', 'POST'])
def create_file():
    n, t = get_nt_request_data()
    return process_n(n)

def get_nt_request_data():
    if request.method == 'GET':
        return get_nt_query_params()
    if request.method == 'POST':
        if request.json:
            return get_nt_json()
        else:
            return get_nt_form()
    return n, t

def get_nt_query_params():
    n = request.args.get('n')
    t = request.args.get('t')
    return n, t

def get_nt_json():
    n = request.json['n']
    t = request.json['t']
    return n, t

def get_nt_form():
    n = request.form['n']
    t = request.form['t']
    return n, t

def process_n(n):
    try:
        n = int(n)
    except:
        n = 1

现在,这当然不会更短,但我个人认为它更清晰。每个单独的功能都有明确的目的,不会混乱。我个人会把“n,t”变成一个有两个字段的对象,但这完全取决于你以及什么对你的应用程序有效。第2步:我们在那里有一些非常明显的复制/粘贴。让我们把它清理干净。

@app.route('/create', methods=['GET', 'POST'])
def create_file():
    n, t = get_nt_request_data()
    return process_n(n)

def get_nt_request_data():
    if request.method == 'GET':
        return get_nt_query_params()
    if request.method == 'POST':
        if request.json:
            return get_nt_json()
        else:
            return get_nt_form()
    return n, t

def get_nt_query_params():
    return build_nt(request.args)

def get_nt_json():
    return build_nt(request.json)

def get_nt_form():
    return build_nt(request.form)

def build_nt(resource):
    return resource.get("n"), resource.get("t")

def process_n(n):
    try:
        n = int(n)
    except:
        n = 1

现在我们到了某个地方!但是如果我们为20个不同的资源执行此操作(假设您的不同端点遵循HTTP谓词的类似规则),我们将会有一堆get_xx_request_data函数,它们基本上都做同样的事情。让我们参数化!

@app.route('/create', methods=['GET', 'POST'])
def create_file():
    n, t = get_request_data(build_nt)
    return process_n(n)

def build_nt(resource):
    return resource.get("n"), resource.get("t")

def process_n(n):
    try:
        n = int(n)
    except:
        n = 1

# in a shared module somewhere
def get_request_data(builder):
    if request.method == 'GET':
        return builder(request.args)
    if request.method == 'POST':
        if request.json:
            return builder(request.json)
        else:
            return builder(request.form)
    return n, t

只有11行代码,以及一个可以为其他人重用的共享函数。 (我认为这在概念上类似于可用框架最终做的事情;我没有机会检查它们。)

最后一点注意:创建一个带有GET请求的文件会让你有些兴奋,可能会有一些奇怪的错误,这取决于你对客户端和干预代理的控制程度。 GET应该是idempotent,因此客户端应该能够毫不犹豫地重试它们而不期望服务器上的任何状态更改(并且创建某些东西肯定是状态更改)。理论上,代理应该能够在网络打嗝后重放GET命令,甚至不告诉原始客户端它曾尝试过两次,但实际上这从来没有给我带来麻烦。