避免使用Flask重复 - 但是它太干了吗?

时间:2013-08-21 13:58:41

标签: security flask dry

让我们假设我使用一个小型Flask应用程序向办公室内的同事提供数据,让我们假设这是一个我没有明确“付钱做”的项目,所以我没有所有时间都在世界编写代码。

我在家里进行宠物项目试验时发现,不是用@app.route('/some/local/page')装饰每一条最后一条路线,而是我可以做以下事情:

from flask import Flask, render_template, url_for, redirect, abort
from collections import OrderedDict

goodURLS = OrderedDict([('/index','Home'),    ##can be passed to the template
        ('/about', 'About'),      ##to create the navigation bar
        ('/foo', 'Foo'),
        ('/bar', 'Bar'),          ##hence the use of OrderedDict
        ('/eggs', 'Eggs'),        ##to have a set order for that navibar
        ('/spam', 'Spam')])

app = Flask(__name__)

@app.route('/<destination>')
def goThere(destination):
    availableRoutes = goodURLS.keys():
    if "/" + destination in availableRoutes:
        return render_template('/%s.html' % destination, goodURLS=goodURLS)
else:
    abort(404)

@app.errorhandler(404)
def notFound(e):
    return render_template('/notFound.html'), 404

现在我需要做的就是更新我的一个列表,我的导航栏和路由处理功能都是锁定步骤。

或者,我已经编写了一种方法来确定可行的文件位置,方法是使用os.walkfile.endswith('.aGivenFileExtension')一起找到我想要访问的每个文件。然后可以将用户的请求与此函数返回的列表进行比较(这显然会更改serveTheUser()函数。

from os import path, walk

def fileFinder(directory, extension=".html"):
    """Returns a list of files with a given file extension at a given path.
    By default .html files are returned.
    """
    foundFilesList = []
    if path.exists(directory):
        for p, d, files in walk(directory):
            for file in files:
                if file.endswith(extension):
                    foundFilesList.append(file)
    return foundFilesList

goodRoutes = fileFinder('./templates/someFolderWithGoodRoutes/')

问题是,这是不是很糟糕?

我只是没有使用Flask的许多方面(主要是因为我还不需要了解它们) - 因此,与Flask的内置功能相比,这可能实际上是限制或冗余。我缺乏明确装饰每条路线是否让我失去了Flask的一大特色?

此外,这些方法中的任何一种或多或少比另一种更安全吗?我真的不太了解网络安全 - 就像我说的那样,现在这是所有办公室内的东西,我的数据的安全性由我们的IT专业人员保证,并且没有来自办公室以外的传入请求 - 但是一个现实世界的环境,这些都是有害的吗?特别是,如果我使用后端到os.walk服务器本地磁盘上的某个位置,我不是要求它被一些不好的人滥用吗?

编辑:我提出这是一个赏金,因为如果它不是一种安全或建设性的做法,我想避免将它用于我想要推送给Heroku的东西,或者只是一般公开服务对于家庭等等,似乎用app.route装饰每条可行的路线都是浪费时间。

2 个答案:

答案 0 :(得分:0)

我假设根据你的代码,所有路由都有一个相同名称的相应模板文件(destination.html到destination.html),并且手动更改了goodURL菜单栏。一种更简单的方法是尝试在请求时呈现模板,如果不存在则返回404页面。

from jinja2 import TemplateNotFound
from werkzeug import secure_filename

....

@app.route('/<destination>')
def goThere(destination):
    destTemplate = secure_filename("%s.html" % destination)
    try:
        return render_template(destTemplate, goodURLS=goodURLS)
    except TemplateNotFound:
        abort(404)

@app.errorhandler(404)
def notFound(e):
    return render_template('/notFound.html'), 404

这改编自Stackoverflow: How do I create a 404 page?的答案。

编辑:更新以利用Werkzeug的secure_filename来清理用户输入。

答案 1 :(得分:0)

在我看来,您的解决方案没有任何问题。问题是,通过这种设置,你可以做的事情非常有限。

我不确定你是否简化了要在此处显示的代码,但是如果你在视图函数中所做的只是收集一些数据,然后选择一个模板来渲染它,那么你也可以渲染整个事情在一个页面中,并可能使用Javascript选项卡控件将其分为客户端部分。

如果每个模板需要不同的数据,那么获取和处理每个模板的数据的逻辑必须在您的视图函数中,这看起来会非常混乱,因为您将拥有一长串if语句处理每个模板。在每个模板的单独视图函数和单独的视图函数之间,我认为后者会更快,如果你还考虑维护工作则会更快。

更新:根据评论中的转换,我支持我的回答,并提供一些小的保留。

我认为您的解决方案有效并且没有重大问题。我没有看到安全风险,因为您在使用它之前验证来自客户端的输入。

如果忽略顶部的导航栏,您只是使用Flask来提供可被视为静态的文件。您应该考虑使用像Frozen-Flask这样的扩展将Flask应用程序编译成一组静态文件,然后您只需使用常规Web服务器托管已编译的文件。当您需要添加/删除路线时,您可以修改Flask应用并再次编译。

另一个想法是,如果您需要添加服务器端逻辑,您的Flask应用程序结构将无法很好地扩展。现在你在服务器中没有任何逻辑,一切都是由jQuery在浏览器中处理的,所以只有一个视图功能才能正常工作。如果在某些时候你需要为这些页面添加服务器逻辑,那么你会发现这种结构不方便。

我希望这会有所帮助。