I am building on Google App Engine + Python + webapp2. Part of building a modern web app requires a restful API. I know that I can do this with Flask, however I want to explore the possibility of building a REST API on webapp2.
On webapp2, requests are handled like this:
app = webapp2.WSGIApplication([
('/post/new', CreatePost),
('/post/([a-z0-9]+)', ViewPost),
('/post/([a-z0-9]+)/edit', EditPost),
('/post/([a-z0-9]+)/delete', DeletePost)
])
Note: ([a-z0-9]+) is a regex that represents the post_id
The above request handlers do not adhere to a RESTful pattern since the request methods are specified in the path (/delete, /edit, /new) rather than in the request headers.
Is the solution to create a single handler class that receives all request types? For example:
class PostHandler(webapp2.RequestHandler):
def get(self):
# handle GET requests
def post(self):
# handle POST requests
def put(self):
# handle PUT requests
def delete(self):
# handle DELETE requests
app = webapp2.WSGIApplication([
('/post/?', PostHandler)
])
In this case, all /post paths are handled by PostHandler. The post_id is no longer used in this pattern since it would be submitted in the request body instead.
Is this the correct approach to building a REST API with webapp2?
You are on the right path, but you should continue to handle post_id in the url and do it like this:
class PostHandler(webapp2.RequestHandler):
def get(self, post_id=None):
if post_id:
# handle Fetching a single post object
else:
# handle Queries
def post(self, post_id=None):
if post_id:
self.abort(405)
# handle creating a single post object
def put(self, post_id=None):
if post_id:
# handle updating a single post object
else:
self.abort(405)
def delete(self, post_id=None):
if post_id:
# handle deleting a single post object
else:
self.abort(405)
app = webapp2.WSGIApplication([
('/post/<post_id>/', PostHandler),
('/post/', PostHandler),
])
Furthermore putting the HTTP verb within the request payload like voscausa suggested is not inline with RESTful API design.
One should extend webapp2.RequestHandler and use the extension as the new base class for a RESTfulHandler and then maybe also for your specific purpose extend the RESTfulHandler to a RESTfulPostHandler. This way we could also deal with new ways or work such as jwt (JSON web token) in the header, authorization and other properties that you wish to handle in the header.
BTW, "post" might not be the best name for your item/object since it could easily get mixed up with the http verb "post". If I were you, I would rename it into "item" or similar, and use a RESTfulItemHandler which extends the webapp2 request handler. There are many things you would like to share between your handler and like that it is convenient to share the functionality in a base-class.
Related
I am trying to save objects made available by the request.headers in my Flask app.
I want to render my index.html upon page load, but I also want to grab the visiting user's email so I can use it for other functions / processes.
# routes
#app.route('/')
def index():
return render_template('index.html')
def find_aad():
aad_email = request.headers.get('X-MS-CLIENT-PRINCIPAL-NAME') # aad email
return aad_email
If I try to run find_aad() on its own,
user_email = find_aad() # cant run
I will get the typical error: Working outside of request context.
How can I on an initial load of the website secure these headers and save them to an object without having these errors?
You could get at it this way, perhaps:
On that first call to index, you can create a UUID for the "session" and use that as an identifier for the user, then you pass that code back inside the rendered UI elements for stashing on the client-side. Then, on every subsequent call to the backend, you send that UUID with the rest of the request.
On those subsequent requests, you can access the email value via that UUID as the key to the data structure you're using to store client information on the backend.
This concept is the idea of a "session" with a "session id" that is common in client/server communications. Using sockets or possibly even built in or supplemental libraries for Flask would probably be a good idea instead of "rolling your own". Sorry if I'm being unhelpful or stupid - it's late where I'm at.
EDIT:
By request here's some simple pseudocode for this:
from flask import Flask
import uuid
...
uuid_to_email = {}
...
#app.route('/')
def index():
user_id = str(uuid.uuid4())
uuid_to_email[user_id] = request.headers.get('X-MS-CLIENT-PRINCIPAL-NAME')
return render_template('index.html', uuid=user_id) # where it is implied that you would then use the uuid in the client-side code to story it and pass it back to the endpoints you want to do that with
I'm new to Flask and Flask-RestPlus. I'm creating a web api where I want keep my POST urls different from the GET urls which are visible in Swagger. For example in Flask-Restplus
#api.route('/my_api/<int:id>')
class SavingsModeAction(Resource):
#api.expect(MyApiModel)
def post(self):
pass #my code goes here
def get(self, id):
pass #my code goes here
So in swagger for the both apis url would look like
GET: /my_api/{id}
POST: /my_api/{id}
But so far I have absolutely no use of {id} part in my post api and it perhaps creates a bit of confusion for an user whether to update an existing record or to create a new, however the purpose of the api is just to create.
It will be better to use query params
like GET: /my_api?id=
your above code will be like
from flask import request
#api.route('/my_api')
class SavingsModeAction(Resource):
#api.expect(MyApiModel)
def post(self):
...
def get(self):
_id = request.args.get("_id", type=int)
...
Consider the following flow:
public client ----> DRF API on Service A ------> DRF API on Service B
Some of the DRF API on Service A merely proxying to Service B, so in the particular API on Service A looks like this:
class SomeServiceAPI(APIView):
def get(request):
resp = requests.get('http://service-b.com/api/...')
return Response(resp.json())
While this works on normal status, but it has a few issues:
It doesn't proxy the actual status code from service b.
Unnecessary round-trip of json serialization within Response()
If service b returns a non-json error, service does not return actual error from service b.
The question is, is there a better way to do it? I had a look at Django Rest Framework Proxy project, but I am not entirely sure if it actually suits my use case here.
You can solve the status code part by modifying your Response:
return Response(resp.json(), status=resp.status_code)
For the second part though, this is the essence of Proxying... (True, sometimes you want to manipulate the request and/or the response in the middleman of the proxy, but what you do is the essence).
Notes:
The DRF Proxy that you are suggesting seems to do the job just
fine, without the need for you to write a specific view just for the
roundtrip.
There exist another tool, DRF Reverse Proxy which is a DRF port of Django Revproxy and you may want to consider.
The general idea of both of the above is that you create a URL path specifically to Proxy the path to another API:
DRF Proxy:
Add your proxy to settings.py:
REST_PROXY = {
'HOST': 'http://service-b.com/api/'
}
In urls.py:
url(
r'^somewere_in_a/$',
ProxyView.as_view(source='somewere_in_b/'),
name='a_name'
)
DRF Reverse Proxy:
Pretty much similar with the above, without the settings part:
url(
r'^(?P<path>.*)$',
ProxyView.as_view(upstream='http://service-b.com/api/somewere_in_b/'),
name='a_name'
)
Opinion: the DRF Proxy seems more solid...
I had a look at both existing packages mentioned in John's answer but they don't seem to perfectly suit in my use case, so I have created a simple wrapper to proxy the requests' response to DRF response.
# encoding: utf-8
from __future__ import unicode_literals
from __future__ import absolute_import
from rest_framework.response import Response
from requests.models import Response as RResponse
class InCompatibleError(Exception):
pass
class DRFResponseWrapper(Response):
"""
Wraps the requests' response
"""
def __init__(self, data, *args, **kwargs):
if not isinstance(data, RResponse):
raise InCompatibleError
status = data.status_code
content_type = data.headers.get('content_type')
try:
content = data.json()
except:
content = data.content
super(DRFResponseWrapper, self).__init__(content, status=status, content_type=content_type)
And use as below:
resp = requests.get(
'{}://{}/api/v5/business/'.format(settings.SEARCH_HOST_SCHEMA, settings.SEARCH_HOST),
params=request.query_params
)
return DRFResponseWrapper(resp)
Can someone show me examples of making a RESTful API which uses database information in Flask? I have no idea how to implement POST, PUT and DELETE and I always get the 405 error where I can't use the method in url.
Have you add request method in your routing? you can following reference from: flask-restful
from flask import Flask, request
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
class TodoSimple(Resource):
def get(self):
# do get something
def put(self):
# do put something
def delete(self):
# do delete something
def post(self):
# do post something
api.add_resource(TodoSimple, '/api/todo')
if __name__ == '__main__':
app.run(debug=True)
in flask-restful the HTTP actions (GET, PUT, POST, DELETE) have their corresponding method in the resource class, so is just a matter of defining those method in the resource (with the corresponding parameter defined in the routing)
I've also built a lightweight framework for building restful apis that makes it super easy to build apis. You can take a look at the code to have an idea of how an API can be built, configured and run, and of course, build on top of it
here's the code: https://github.com/sebastiandev/peach
The Flask documentation shows:
add_url_rule(*args, **kwargs)
Connects a URL rule. Works exactly like the route() decorator.
If a view_func is provided it will be registered with the endpoint.
endpoint – the endpoint for the registered URL rule. Flask itself assumes the name of the view function as endpoint
What exactly is meant by an "endpoint"?
How Flask Routing Works
The entire idea of Flask (and the underlying Werkzeug library) is to map URL paths to some logic that you will run (typically, the "view function"). Your basic view is defined like this:
#app.route('/greeting/<name>')
def give_greeting(name):
return 'Hello, {0}!'.format(name)
Note that the function you referred to (add_url_rule) achieves the same goal, just without using the decorator notation. Therefore, the following is the same:
# No "route" decorator here. We will add routing using a different method below.
def give_greeting(name):
return 'Hello, {0}!'.format(name)
app.add_url_rule('/greeting/<name>', 'give_greeting', give_greeting)
Let's say your website is located at 'www.example.org' and uses the above view. The user enters the following URL into their browser:
http://www.example.org/greeting/Mark
The job of Flask is to take this URL, figure out what the user wants to do, and pass it on to one of your many python functions for handling. It takes the path:
/greeting/Mark
...and matches it to the list of routes. In our case, we defined this path to go to the give_greeting function.
However, while this is the typical way that you might go about creating a view, it actually abstracts some extra info from you. Behind the scenes, Flask did not make the leap directly from URL to the view function that should handle this request. It does not simply say...
URL (http://www.example.org/greeting/Mark) should be handled by View Function (the function "give_greeting")
Actually, it there is another step, where it maps the URL to an endpoint:
URL (http://www.example.org/greeting/Mark) should be handled by Endpoint "give_greeting".
Requests to Endpoint "give_greeting" should be handled by View Function "give_greeting"
Basically, the "endpoint" is an identifier that is used in determining what logical unit of your code should handle the request. Normally, an endpoint is just the name of a view function. However, you can actually change the endpoint, as is done in the following example.
#app.route('/greeting/<name>', endpoint='say_hello')
def give_greeting(name):
return 'Hello, {0}!'.format(name)
Now, when Flask routes the request, the logic looks like this:
URL (http://www.example.org/greeting/Mark) should be handled by Endpoint "say_hello".
Endpoint "say_hello" should be handled by View Function "give_greeting"
How You Use the Endpoint
The endpoint is commonly used for the "reverse lookup". For example, in one view of your Flask application, you want to reference another view (perhaps when you are linking from one area of the site to another). Rather than hard-code the URL, you can use url_for(). Assume the following
#app.route('/')
def index():
print url_for('give_greeting', name='Mark') # This will print '/greeting/Mark'
#app.route('/greeting/<name>')
def give_greeting(name):
return 'Hello, {0}!'.format(name)
This is advantageous, as now we can change the URLs of our application without needing to change the line where we reference that resource.
Why not just always use the name of the view function?
One question that might come up is the following: "Why do we need this extra layer?" Why map a path to an endpoint, then an endpoint to a view function? Why not just skip that middle step?
The reason is because it is more powerful this way. For example, Flask Blueprints allow you to split your application into various parts. I might have all of my admin-side resources in a blueprint called "admin", and all of my user-level resources in an endpoint called "user".
Blueprints allow you to separate these into namespaces. For example...
main.py:
from flask import Flask, Blueprint
from admin import admin
from user import user
app = Flask(__name__)
app.register_blueprint(admin, url_prefix='admin')
app.register_blueprint(user, url_prefix='user')
admin.py:
admin = Blueprint('admin', __name__)
#admin.route('/greeting')
def greeting():
return 'Hello, administrative user!'
user.py:
user = Blueprint('user', __name__)
#user.route('/greeting')
def greeting():
return 'Hello, lowly normal user!'
Note that in both blueprints, the '/greeting' route is a function called "greeting". If I wanted to refer to the admin "greeting" function, I couldn't just say "greeting" because there is also a user "greeting" function. Endpoints allow for a sort of namespacing by having you specify the name of the blueprint as part of the endpoint. So, I could do the following...
print url_for('admin.greeting') # Prints '/admin/greeting'
print url_for('user.greeting') # Prints '/user/greeting'
Endpoint is the name used to reverse-lookup the url rules with url_for and it defaults to the name of the view function.
Small example:
from flask import Flask, url_for
app = Flask(__name__)
# We can use url_for('foo_view') for reverse-lookups in templates or view functions
#app.route('/foo')
def foo_view():
pass
# We now specify the custom endpoint named 'bufar'. url_for('bar_view') will fail!
#app.route('/bar', endpoint='bufar')
def bar_view():
pass
with app.test_request_context('/'):
print url_for('foo_view')
print url_for('bufar')
# url_for('bar_view') will raise werkzeug.routing.BuildError
print url_for('bar_view')
If you have same class name and want to map with multiple routes, then specify the endpoint, so that framework will differentiate between two:
class ClassName(Resource):
def get(self):
if request.endpoint!='hello':
return {"data": "Hello"}
elif:
return {"data" : "World"}
api.add_resource(ClassName, '/rout1', endpoint = "world")
api.add_resource(ClassName, '/rout2', endpoint="hello")
#app.route('/') #Endpoint
def a_function(): #View function
return 'view'
Inside Flask, every endpoint with its request methods mapped to a view function. When you use app.route decorator you are actually adding a URL rule.