I have simple Resource class which defines some API methods:
class RoomAPI(Resource):
def get(self):
# some code
def post(self):
# some code
def put(self):
# some code
Then I define my routes like this:
api.add_resource(RoomAPI,'/api/rooms/')
So, my question is: how can I make different routes for each HTTP methos using only one Resource class?
I want to get such API:
GET /api/rooms/get/
POST /api/rooms/create/
PUT /api/rooms/update/
The short answer is, you shouldn't. That's not RESTful at all.
However, if you really want to, I think you could do it like so:
api.add_resource(RoomAPI,'/api/rooms/get', methods=['GET'])
api.add_resource(RoomAPI,'/api/rooms/create', methods=['PUT'])
api.add_resource(RoomAPI,'/api/rooms/update', methods=['POST'])
Since the unused **kwargs from add_resource get passed to add_url_rule().
Related
I have a Flask application using flask-restx and flask-login. I would like all routes by default to require login, and explicitly define public routes that require no authentication. I have started using decorators following the example given in this question:
Best way to make Flask-Login's login_required the default
It works for function endpoints, but not for restx resource endpoints.
I have tried adding the function both as a decorator, and using the method_decorators field. For example:
def public_route(decorated_function):
"""
This is a decorator to specify public endpoints in our flask routes
:param decorated_function:
:return:
"""
decorated_function.is_public = True
return decorated_function
class HelloWorld(ConfigurableResource):
method_decorators = {"get": [public_route]}
#public_route
#api.doc('Welcome message')
def get(self):
return {'hello': 'world'}
And this test passes:
def test_hello_world_is_public():
api = Namespace('health', description='Health related operations')
hello = HelloWorld(api, config=None, logger=None)
is_public_endpoint = getattr(hello.get, 'is_public', False)
assert is_public_endpoint
My challenge is I can't see how to access this attribute in my auth logic:
#app.before_request
def check_route_access():
"""
This function decides whethere access should be granted to an endpoint.
This function runs before all requests.
:return:
"""
is_public_endpoint = getattr(app.view_functions[request.endpoint], 'is_public', False)
if person_authorized_for_path(current_user, request.path, is_public_endpoint):
return
# Otherwise access not granted
return redirect(url_for("auth.index"))
This works for plain function endpoints, but not restx resources.
I understand that restx is wrapping my resource class in a function so that flask can do dispatch, but I can't figure out how to access the decorator from here. So I have some questions:
Is it possible to reach the decorator from the view_function?
Is it possible to know whether the endpoint is a restx resource or a plain rest function?
Is there a better way to do what I'm trying to achieve?
Based on this and this, method_decorators variable should be a list of functions, so you should use it like:
def _perform_auth(method):
is_public_endpoint = getattr(method, 'is_public', False)
# place the validation here
class Resource(flask_restx.Resource):
method_decorators = [_perform_auth]
Is it possible to reach the decorator from the view_function?
Well... it is possible, but I wouldn't recommend it. Here's an example
Is it possible to know whether the endpoint is a restx resource or a
plain rest function?
You probably can inspect the func and figure out if it's from restx, maybe looking at __qualname__, but then again, I`d wouldn't recommend it.
Is there a better way to do what I'm trying to achieve?
I would go one of these solutions:
Explicitly decorate view_funcs and resources that do need authentication, instead of the other way around
Create a blueprint for public endpoints, a blueprint for protected endpoints with the before_request decorator for authorization
new to flask , i have web app that containes few parts
for example:
utils/
-- utils.py
db/
-- oracle.py
main.py
My Question is how can i make global_response function/handler that can be called from : utils.py,oracle.py,main.py without threading problem
for example:
in utils.py
clas Utils():
def A(self):
return global_response("error","a")
oracle.py
clas Utils():
def ORA(self):
return global_response("error ora","b")
main.py
def some_fun(self):
return global_response("error main","c")
and here is the response function which was in main.py but now needs to be called from any where :
def global_response(error_msg, title,*options):
res = {"status": error_msg, "title": title}
for option in options:
res.update(option)
return json.dumps(res)
Instead of creating a response function, you can create a custom response class which will replace flask's builtin Response class.
The class will inherit flask's Response class, meaning:
class GlobalResponse(Response):
pass
In order to generate the generic response, you should call the app.make_response method.
You can view This link for a walk through of how to create a response class.
Side notes
I would recommended you to check out flask's jsonify function which wraps the json.dumps and is considered "stronger" (works for more cases).
You may want to look at flask's make_response method.
I am trying to create a simple Rest API.
Even if it is simple, I don't want to mix everything in a single file.
Therefore I have defined separate classes
Here is some of my files
app = Flask(__name__)
if __name__ == '__main__':
api = PostApi(app)
api.setup()
api.set_routes()
app.run(debug=True)
Post API class
class PostApi(object):
BASE_API_ROUTE = '/post'
def __init__(self, app):
super(PostApi, self).__init__()
self.app = app
def setup(self):
self.api = Api(self.app)
self.app.config['SECRET_KEY'] = SECRET['digest_key']
def set_routes(self):
self.api.add_resource(PostCategories, self.BASE_API_ROUTE + "/categories")
self.api.add_resource(PostCatalog, self.BASE_API_ROUTE + "/catalog")
self.api.add_resource(PostTags, self.BASE_API_ROUTE + "/tags")
And for example one of my endpoint classes
class PostTags(Resource):
def __init__(self):
super(PostTags, self).__init__()
def get(self):
return {'hello': 'world'}
It works, but I need to add authentication for my routes.
As you can see I am not using route decorators like app.route instead I am using the library flask_restful.
I need to protect my routes with the Digest Auth in this case. However, I am not sure how to do this, because I am not using decorators
I am a newbie developer. Could you suggest how to keep my endpoints separated and apply some protection to my routes.
You can use before_request. This will be called before every request on every route.
something like this:
#app.before_request
def before_request():
//add your logic here
there's also before_first_request.
visit Flask Documentation for more info.
I want to log (send metrics to statsD ) every GET/POST request in GAE.
So thats my decorator :
def log_request():
def actual_decorator(method):
def wrapper(self, *args, **kwargs):
// send metrics that have the request name as the name space
return method(self, *args, **kwargs)
return wrapper
return actual_decorator
Of course i don't want to go over all my GET requests and put a #log_request() out there. Is there a way to catch them all on a global level ?
(not using Django)
I do not think it's possible (in Python) to inherit a decorated method automatically to all subclasses. You must explicitly declare decorators for each method.
If all you are looking for is analytics, you can use the logging service and a ScheduledTask to accomplish the same.
I have implemented similar thing recently, you can wrap the WSGIApplication in a middleware that log the request. I do not know about statsD, in my case I log the request to datastore.
In a Django application, I have more than a handful of views which return JSON, with an invocation similar to:
return HttpResponse(json.dumps(content), mimetype="application/json")
I want to start creating views that return either HTML or JSON depending on the Accept headers from the request. Possibly other types, too, but those are the main ones. I also want to get multiple URLs routed to this view; the file extensions ".html" and ".json" help tell clients which types they should Accept when making their request, and I want to avoid the "?format=json" antipattern.
What's the correct, blessed way to do this in Django with a minimum of boilerplate or repeated code?
(Edit: Rephrase in order to better follow SO's community guidelines.)
I think a class-based view mixin (django 1.3+) is the easiest way to do this. All your views would inherit from a base class that contains logic to respond with the appropriate content.
I think I may not be seeing your big picture here but this is what I would do:
Have a html template that you render when html is requested and keep your json.dumps(content) for when json is requested. Seems to be obvious but I thought i should mention it anyway.
Set your URLs to send you "json" or 'html'. :
(r'^some/path/(?P<url_path>.*)\.(?P<extension>html|json)$', 'some.redirect.view'),
(r'^/(?P<extension>html|json)/AppName', include(MyApp)),
# etc etc
and your view:
def myRedirectView(request, url_path, extension):
view, args, kwargs = resolve("/" + extension + "/" + urlPath)
kwargs['request'] = request
return view(*args, **kwargs)
I know this is a bit vague because I haven't fully thought it through but its where I would start.
I have addressed this by creating a generic view class, based on Django's own generic.View class, that defines a decorator 'accept_types'. This modifies the view to which it is applied so that it returns None if the indicated content-type is not in the Accept header. Then, the get() method (which is called by the generic.View dispatcher) looks like this:
def get(self, request):
self.request = request # For clarity: generic.View does this anyway
resultdata = { 'result': data, etc. }
return (
self.render_uri_list(resultdata) or
self.render_html(resultdata) or
self.error(self.error406values())
)
The actual view renderers are decorated thus:
#ContentNegotiationView.accept_types(["text/uri-list"])
def render_uri_list(self, resultdata):
resp = HttpResponse(status=200, content_type="text/uri-list")
# use resp.write(...) to assemble rendered response body
return resp
#ContentNegotiationView.accept_types(["text/html", "application/html", "default_type"])
def render_html(self, resultdata):
template = loader.get_template('rovserver_home.html')
context = RequestContext(self.request, resultdata)
return HttpResponse(template.render(context))
The (one-off) generic view class that declares the decorator looks like this:
class ContentNegotiationView(generic.View):
"""
Generic view class with content negotiation decorators and generic error value methods
Note: generic.View dispatcher assigns HTTPRequest object to self.request.
"""
#staticmethod
def accept_types(types):
"""
Decorator to use associated function to render the indicated content types
"""
def decorator(func):
def guard(self, values):
accept_header = self.request.META.get('HTTP_ACCEPT',"default_type")
accept_types = [ a.split(';')[0].strip().lower()
for a in accept_header.split(',') ]
for t in types:
if t in accept_types:
return func(self, values)
return None
return guard
return decorator
(The parameter handling in the decorator should be generalized - this code works, but is still in development as I write this. The actual code is in GitHub at https://github.com/wf4ever/ro-manager/tree/develop/src/roverlay/rovweb/rovserver, but in due course should be separated to a separate package. HTH.)