I've seen the posts on passing GET parameters and hardcoded parameters here and here.
What I am trying to do is pass POST parameters to a custom decorator. The route is not actually rendering a page but rather processing some stuff and sending the results back through an AJAX call.
The decorator looks like this:
# app/util.py
from functools import wraps
from models import data
# custom decorator to validate symbol
def symbol_valid():
def decorator(func):
#wraps(func)
def decorated_function(symbol, *args, **kwargs):
if not data.validate_symbol(symbol):
return jsonify({'status': 'fail'})
return func(*args, **kwargs)
return decorated_function
return decorator
The view looks something like this:
# app/views/matrix_blueprint.py
from flask import Blueprint, request, jsonify
from ..models import data
from ..util import symbol_valid
matrix_blueprint = Blueprint('matrix_blueprint', __name__)
# routing for the ajax call to return symbol details
#matrix_blueprint.route('/route_line', methods=['POST'])
#symbol_valid
def route_line():
symbol = request.form['symbol'].upper()
result = data.get_information(symbol)
return jsonify(**result)
I understand that I can actually call #symbol_valid() when I pass a parameter through GET like this /quote_line/<symbol> but I need to POST.
The question then is how can my decorator access the POSTed variable?
Simple solution. Imported Flask's request module into the util.py module which contains the decorator. Removed the outer function as well.
See code:
# app/util.py
from flask import request # <- added
from functools import wraps
from models import data
# custom decorator to validate symbol
def symbol_valid(func):
#wraps(func)
def decorated_function(*args, **kwargs): # <- removed symbol arg
symbol = request.form['symbol'] # <- paramter is in the request object
if not data.validate_symbol(symbol):
return jsonify({'status': 'fail'})
return func(*args, **kwargs)
return symbol_valid
The decorator accept a func parameter. You must use your decorator like #symbol_valid() or make the function symbol_valid accept a func parameter.
If you are doing it right, you can access the request object anywhere during the request cycle. It just works.
Related
I'm working on flask RESTful application that uses auth0 for authentication and authorization. Then, I wrote a decorator that validates the token and extract user's id from it. My goal is use that id, extracted from token, to used inside the decorated function, and throw an exception if user's id from token and from URL parameter doesn't match. This is aimed to avoid users to change data of another user, with his own token. I'm not sure this is the best practice for a RESTful app, but seems to be needed in my case.
That said, I trying to figure out the best approach pass the user's id from token, to the decorated funcion:
Something like this:
def authorization():
def inner(func):
def wrapper(*args, **kwargs):
try:
"""
token validation stuff
...
"""
wrapper.user_id = token_payload['user_id']
except Exception:
return {"success": False}, 500
return func(*args, **kwargs)
return wrapper
return inner
#authorization()
#app.route('/test', methods=['GET'])
def some_function():
return jsonify({
'success': True,
'user_id': some_function.user_id
})
As you can see, I setting the user_id field to the wrapper function, which seems to not be the best way, to do it. Is there any different approach to this situation? maybe using Flask resources?
You can skip one level of wrapping in your decorator since you're not giving it any parameters.
Also, I'd just pass the extracted id into the wrapped function directly instead of setting an attribute.
And finally, you should add the auth decorater innermost, since the decorated function is what you want to register with Flask.
Extra: use functools.wraps to update the signature of your wrapped function, to make introspection and debugging easier.
Thus:
from functools import wraps
def authorized(func):
#wraps(func)
def wrapper(*args, **kw):
try:
# token stuff
user_id = token_payload['user_id']
except:
return jsonify({"success": False}), 500
return func(user_id, *args, **kw)
return wrapper
#app.route('/test', methods=['GET'])
#authorized
def some_function(user_id):
return jsonify({
'success': True,
'user_id': user_id
})
Now every function you decorate with #authorized will need to have user_id as their first parameter, and everything should work as you expect.
Is there any chances to specify which parameters are required in url query and automatically pass them into view function?
In urls.py I would like to have something like this:
path('get_part_info?<part>', views.get_part_info, name='get_part_info'),
And in views.py to have something like this:
def get_part_info(request, part):
# do something with part
return JsonResponse({'result': part})
Idea is to avoid ugly construction like: part= request.GET.get('part')
URL path is not a solution, because "part" value can have various extra characters like slashes etc.
You can write a decorator:
from functools import wraps
from django.http import HttpResponseBadRequest, JsonResponse
def query_params(*param_names):
def decorator(func):
#wraps(func)
def inner(request, *args, **kwargs):
try:
params = {name: request.GET[name] for name in param_names}
except KeyError:
return HttpResponseBadRequest("Missing Parameter")
kwargs.update(params)
return func(request, *args, **kwargs)
return inner
return decorator
#query_params("part")
def get_part_info(request, part):
# do something with part
return JsonResponse({"result": part})
This decorator returns a 400 if a parameter is missing, but that could be changed any way you want, for example, redirect to another URL or to use default values.
How can I elegantly bind some arbitrary value and flask route? Suppose, I want to access this value in my session interface implementation or in before_request hook.
Now I'm doing it in such way:
#app.route('/foo/<bar>', defaults={'_my_val': True}):
def foo(bar, _my_val): # this is ugly hack
pass
And access this value through request object like this:
class MyRequest(flask.Request):
def get_my_val(self):
return (self.url_rule.defaults or {}).get('_my_val', False)
But it looks like a hack.
UPD: Looks like it can be done by extending werkzeug.routing.Rule class and adding **kwargs to its constructor. Is it ok to override Rule class in flask?
Eventually I ended up overriding flask's Request and Rule classes:
# here app is a Flask current application object
from flask import Request as FlaskRequest
from werkzeug.routing import Rule as FlaskRule
class Request(FlaskRequest):
def is_foo(self):
return bool(self.url_rule._foo) if self.url_rule else False
def get_bar(self):
return getattr(self.url_rule, '_bar')
class Rule(FlaskRule):
def __init__(self, *args, **kwargs):
for param in ('_foo', '_bar'):
setattr(self, param, kwargs.pop(param, None))
super().__init__(*args, **kwargs)
# app initialization
app.request_class = Request
app.url_rule_class = Rule
# route example
#app.route('/path', _foo=True, _bar='baz')
def route():
pass
I'm building a rate-limiting decorator in flask using redis stores that will recognize different limits on different endpoints. (I realize there are a number of rate-limiting decorators out there, but my use case is different enough that it made sense to roll my own.)
Basically the issue I'm having is ensuring that the keys I store in redis are class-specific. I'm using the blueprint pattern in flask, which basically works like this:
class SomeEndpoint(MethodView):
def get(self):
# Respond to get request
def post(self):
# Respond to post request
The issue here is that I want to be able to rate limit the post method of these classes without adding any additional naming conventions. In my mind the best way to do this would be something like this:
class SomeEndpoint(MethodView):
#RateLimit # Access SomeEndpoint class name
def post(self):
# Some response
but within the decorator, only the post function is in scope. How would I get back to the SomeEndpoint class given the post function? This is the basic layout of the decorator. That might be confusing, so here's a more concrete example of the decorator.
class RateLimit(object):
"""
The base decorator for app-specific rate-limiting.
"""
def __call__(self, f):
def endpoint(*args, **kwargs):
print class_backtrack(f) # Should print SomeEnpoint
return f(*args, **kwargs)
return endpoint
basically looking for what that class_backtrack function looks like. I've looked through the inspect module, but I haven't found anything that seems to accomplish this.
You can decorate the entire class instead of just the methods:
def wrap(Class, method):
def wrapper(self, *args, **kwargs):
print Class
return method(self, *args, **kwargs)
return method.__class__(wrapper, None, Class)
def rate_limit(*methods):
def decorator(Class):
for method_name in methods:
method = getattr(Class, method_name)
setattr(Class, method_name, wrap(Class, method))
return Class
return decorator
#rate_limit('post')
class SomeEndpoint(object):
def post(self):
pass
class Subclass(SomeEndpoint):
pass
a = Subclass()
a.post()
# prints <class 'SomeEndpoint'>
I want my authorization decorator to be able to pass a custom user object to the view that it decorates.
Currently, I am having the decorator set an attribute on flask.g to do this. Is this acceptable use of flask.g or is there a better way?
My code looks something like this:
def auth(f):
#wraps(f):
def decorated(*args, **kwargs):
user = getUserObj(request.headers.get('user'), request.headers.get('pass'))
flask.g.user = user
return f(*args, **kwargs)
return decorated
And then the view is:
#api.route('/info')
#auth
def info():
flask.g.user # this contains my user object now