如何正确地将参数从瓶子视图传递到装饰视图?
How to properly pass arguments from a bottle view to a decorated view?
我想用 CSRF 令牌保护我的视图,我采用的方法如下:
from functools import partial
from bottle import jinja2_template as template,
def generate_csrf_token(length):
'''Generate a random string using range [a-zA-Z0-9].'''
chars = string.ascii_letters + string.digits
return ''.join([choice(chars) for i in range(length)])
def require_csrf(callback, *args, **kwargs):
def wrapper(*args, **kwargs):
token = request.params.csrf_token
if not token or token != global_vars['csrf_token']:
abort(400)
body = callback(*args, **kwargs)
return body
return wrapper
global_vars = {'BCC_VERSION': pkg_resources.get_distribution('bcc').version,
'csrf_token': generate_csrf_token(48)}
j2template = partial(template, template_settings={'globals': global_vars})
@app.get("/remove/")
@require_csrf
def remove_device():
ans = {'status': 200,
'body': "csrf_token is: {}".format(global_vars['csrf_token'])}
return HTTPResponse(**ans)
只要视图不需要任何参数,它就可以正常工作。如果视图接受数据库连接(例如使用插件时),事情就会变得棘手:
@app.get("/delete/")
@require_csrf
def delete_device(db): # This causes the require_csrf decorator to fail
ans = {'status': 200,
'body': "csrf_token is: {}".format(global_vars['csrf_token'])}
return HTTPResponse(**ans)
访问 /delete/
时出现以下异常:
Traceback (most recent call last):
File "/home/oznt/.virtualenvs/bcc/lib/python3.4/site-packages/bottle.py", line 979, in _handle
out = route.call(**args)
File "/home/oznt/.virtualenvs/bcc/lib/python3.4/site-packages/bottle.py", line 1949, in wrapper
rv = callback(*a, **ka)
File "/home/oznt/Software/bcc/bcc/views.py", line 32, in wrapper
body = callback(*args, **kwargs)
TypeError: delete_device() missing 1 required positional argument: 'db'
为了解决这个问题,我将 require_csrf
装饰器稍微修改为:
def require_csrf(callback, *args, **kwargs):
import inspect
callback_args = inspect.getargspec(callback)[0]
def wrapper(*args, **kwargs):
token = request.params.csrf_token
if not token or token != global_vars['csrf_token']:
abort(400)
body = callback(*callback_args, **kwargs)
return body
return wrapper
现在一切正常。但是,我不确定这是解决此问题的正确方法。您能否对此发表评论或针对该问题提出更好的解决方案?
更新
我尝试了世界末日的建议,但出现以下错误:
python3 main.py
Traceback (most recent call last):
File "main.py", line 8, in <module>
from bcc.views import app as home_app
File "/home/oznt/Software/controller_configuration/bcc/views.py", line 66, in <module>
@require_csrf()
TypeError: 'NoneType' object is not callable
简单的 def delete_device():
行得通吗? (我不确定你为什么首先将 db
参数放在那里,因为你似乎没有使用它。)
编辑:def require_csrf(callback):
呢? (我在那里删除了 *args
或 **kwargs
,因为你不需要它们。)
您在这里缺少一层函数包装器。下面的代码在没有任何 inspect
hack 的情况下工作。
def require_csrf():
def decorator(callback):
def wrapper(*args, **kwargs):
token = request.params.csrf_token
if not token or token != global_vars['csrf_token']:
abort(400)
return callback(*args, **kwargs)
return wrapper
return decorator
@app.get("/remove/<id>")
@require_csrf()
def remove_device(id):
ans = {'status': 200,
'body': "csrf_token is: {}".format(global_vars['csrf_token'])}
return HTTPResponse(**ans)
另请注意,如果需要,您可以向 require_csrf
装饰器和函数添加参数。
我想用 CSRF 令牌保护我的视图,我采用的方法如下:
from functools import partial
from bottle import jinja2_template as template,
def generate_csrf_token(length):
'''Generate a random string using range [a-zA-Z0-9].'''
chars = string.ascii_letters + string.digits
return ''.join([choice(chars) for i in range(length)])
def require_csrf(callback, *args, **kwargs):
def wrapper(*args, **kwargs):
token = request.params.csrf_token
if not token or token != global_vars['csrf_token']:
abort(400)
body = callback(*args, **kwargs)
return body
return wrapper
global_vars = {'BCC_VERSION': pkg_resources.get_distribution('bcc').version,
'csrf_token': generate_csrf_token(48)}
j2template = partial(template, template_settings={'globals': global_vars})
@app.get("/remove/")
@require_csrf
def remove_device():
ans = {'status': 200,
'body': "csrf_token is: {}".format(global_vars['csrf_token'])}
return HTTPResponse(**ans)
只要视图不需要任何参数,它就可以正常工作。如果视图接受数据库连接(例如使用插件时),事情就会变得棘手:
@app.get("/delete/")
@require_csrf
def delete_device(db): # This causes the require_csrf decorator to fail
ans = {'status': 200,
'body': "csrf_token is: {}".format(global_vars['csrf_token'])}
return HTTPResponse(**ans)
访问 /delete/
时出现以下异常:
Traceback (most recent call last):
File "/home/oznt/.virtualenvs/bcc/lib/python3.4/site-packages/bottle.py", line 979, in _handle
out = route.call(**args)
File "/home/oznt/.virtualenvs/bcc/lib/python3.4/site-packages/bottle.py", line 1949, in wrapper
rv = callback(*a, **ka)
File "/home/oznt/Software/bcc/bcc/views.py", line 32, in wrapper
body = callback(*args, **kwargs)
TypeError: delete_device() missing 1 required positional argument: 'db'
为了解决这个问题,我将 require_csrf
装饰器稍微修改为:
def require_csrf(callback, *args, **kwargs):
import inspect
callback_args = inspect.getargspec(callback)[0]
def wrapper(*args, **kwargs):
token = request.params.csrf_token
if not token or token != global_vars['csrf_token']:
abort(400)
body = callback(*callback_args, **kwargs)
return body
return wrapper
现在一切正常。但是,我不确定这是解决此问题的正确方法。您能否对此发表评论或针对该问题提出更好的解决方案?
更新
我尝试了世界末日的建议,但出现以下错误:
python3 main.py
Traceback (most recent call last):
File "main.py", line 8, in <module>
from bcc.views import app as home_app
File "/home/oznt/Software/controller_configuration/bcc/views.py", line 66, in <module>
@require_csrf()
TypeError: 'NoneType' object is not callable
简单的 def delete_device():
行得通吗? (我不确定你为什么首先将 db
参数放在那里,因为你似乎没有使用它。)
编辑:def require_csrf(callback):
呢? (我在那里删除了 *args
或 **kwargs
,因为你不需要它们。)
您在这里缺少一层函数包装器。下面的代码在没有任何 inspect
hack 的情况下工作。
def require_csrf():
def decorator(callback):
def wrapper(*args, **kwargs):
token = request.params.csrf_token
if not token or token != global_vars['csrf_token']:
abort(400)
return callback(*args, **kwargs)
return wrapper
return decorator
@app.get("/remove/<id>")
@require_csrf()
def remove_device(id):
ans = {'status': 200,
'body': "csrf_token is: {}".format(global_vars['csrf_token'])}
return HTTPResponse(**ans)
另请注意,如果需要,您可以向 require_csrf
装饰器和函数添加参数。