I want my application to be able to detect the user's language to serve the corresponding page to them. My idea was to use #before.request to read the Accepted-Languages header, match it against the app's supported languages, and set a cookie if needed, but it seems that the last step is not achievable.
Here is the code for that:
#app.before_request
def before_request_callback():
if request.cookies.get('lang'):
pass
else:
lang = request.accept_languages.best_match(supported_languages)
print(lang)
#I would like to set the cookie here
I thought about setting the cookie in the response object directly in the decorator (resp.set_cookie()) and thus reformatted all the returns in my app to look like this
#app.route("/")
def hello():
resp = make_response(render_template('index.html'))
return resp
to maybe be able to fetch the cookie and attach it to this response, but since the response is created afterwards in the endpoitn function, i have no idea how to do that either.
I also thought about creating the response directly in the decorator, but since i need the return condition, i don't know if thats possible
I think you're trying to use a cookie when you don't need to. As you noted, you can only set cookies on the response. You send the browser cookies in the response and then it re-sends them to you in any subsequent request. But it's already sending you the Accept-Language. So there's no point in setting a cookie on the request. It's already arrived, and already contains what you need.
Instead of getting hung up on setting cookies, just consult the request at the point that you're generating the response to ensure that the served content is in the appropriate language.
from flask import request
#app.route("/")
def hello():
lang = request.accept_languages.best_match(supported_languages)
return render_template(f'{lang}/index.html')
Related
I need to setup a /metrics endpoint so that Prometheus can consume statistics about an endpoint. How do I go about doing this?
I have this in my Flask app:
#app.route('/metrics')
def metrics():
def generateMetrics():
metrics = ""
... some string builder logic
return metrics
response = make_response(generateMetrics(), 200)
response.mimetype = "text/plain"
return response
Is this the best way? What is the difference between returning a String (just returning metrics here) and returning plain text? Why do I need the mimetype?
Is this the best way?
There are several ways to set the MIME type, better explained and discussed in this StackOverflow question. Your way works fine and gets the job done.
What is the difference between returning a String and returning plain text?
If you return a string Flask will automatically handle some of the Response logic for you. This means using some default values. If you set up two different endpoints you'll see that the difference turns out to be that your code returns the following header:
Content-Type:"text/plain; charset=utf-8"
While just returning a string (and default MIME type) would return the following header:
Content-Type:"text/html; charset=utf-8"
Why do I need the mimetype?
You might say that it is technically more correct, given that your response is simply plain text, and not HTML. However, a more forcing reason for needing it would be that a third party system you are using (Prometheus) relies on or cares about the contents of the "Content-Type" header. If they do, then you must set it for them to accept it.
Example code
For the Content-Type header demonstration I used the following example Python code:
from flask import Flask, make_response
app = Flask(__name__)
def generateMetrics():
return "hello world"
#app.route('/metrics')
def metrics():
response = make_response(generateMetrics(), 200)
response.mimetype = "text/plain"
return response
#app.route('/metrics2')
def metrics2():
return generateMetrics()
I then viewed the returned body and headers using Postman.
In this way, I want to set my cookie. But it fails to set.
#app.route('/')
def index():
res = flask.make_response()
res.set_cookie("name", value="I am cookie")
When I print res it shows <Response 0 bytes [200 OK] But not set cookie
You have to return the response after setting the cookie.
#app.route('/')
def index():
resp = make_response(render_template(...))
resp.set_cookie('somecookiename', 'I am cookie')
return resp
This way a cookie will be generated in your browser, but you can get this cookie in the next request.
#app.route('/get-cookie/')
def get_cookie():
username = request.cookies.get('somecookiename')
The cookie you set will be visible if you either use other tools to see it (ex: Press F12 for Developer Tools in Firefox or Chrome) or use a piece of code of JavaScript inside the rendered response.
The cookies are set on the browser by either browser itself (JavaScript) or as a response from a server.
The difference is of great importance as even if the cookie is set by the server the cookie might not be set on the browser (ex: cases where cookie are completely disabled or discarded).
So even if the server might tell "I set up the cookie" - the cookie might not be present on the browser.
For the server to be sure that the cookie was set a subsequent request from the browser is needed (with the cookie present in the request's headers).
So even if the Flask's response (res variable) will mention that the cookie is set we can only be sure that it was set by the server but it will have no confirmation about it from the browser.
Advanced
Another aspect is about how Flask or any other API is creating the responses. As the payload (html/template code) and headers (cookie) are set at same time - the "code" inside the payload (html/template code) might not have access to the cookie.
So you might not be able to set a cookie and display it in the response.
An idea might be to (be able to) first set the cookies and THEN to render the context and the order of setup to be important - so that the html/template to be aware of already setup values. But even in this case is only the server's confirmation that it set up the cookie.
A solution
#app.route('/')
def index():
res = flask.make_response()
res.set_cookie("name", value="I am cookie")
# redirect to a page that display the cookie
resp.headers['location'] = url_for('showcookies')
return resp, 302
This response will set cookie in you browser
def func():
response = make_response( render_template() )
response.set_cookie( "name", "value" )
return response
I'm trying to write a Python 3.5 Flask application that redirects a user to an OAuth URL, for authentication / authorization. As part of that redirection, I have to include the Authorization header. The built-in redirect() method in Flask doesn't seem to support adding HTTP headers.
What's the proper way of handling this in such an application?
You will need to build your own response object to add headers. You can check out the docs here: http://docs.python-requests.org/en/master/api/#requests.Response
A simple example for your use case would be something like:
response = Response(headers={'Authorization': 'whatever'},
is_redirect=True,
url="https://your-redirected-url.com")
return response
Edit: Further info
Also, I would check out https://github.com/lepture/flask-oauthlib if you are interested in using a library. It has support for oAuth1 and oAuth2 and it is relatively easy to setup with a standard Flask app.
Edit: Another way of doing it
This morning I remembered a simpler way to do this. You can call the redirect function and it will return a flask Response object. Then you are able to set the headers on that newly created object.
response = redirect('https://url')
response.headers = {'authorization': 'whatever'}
return response
Is it possible to send data while to redirecting to another view?
For example:-
def social_user():
//do something here
return redirect('name of the view',{'variable':'value'})
or any other alternative to exchange data between a function and view function.
There are two ways to send data to the resulting destination, but first you should understand how redirects work.
When the server redirects, it just sets the Location header telling the browser which page to fetch next.
Location: /foo.html
Since the body is completely ignored even if present, you must somehow pass data via the HTTP headers.
1) Query Parameters
The easiest way is to use query (GET) parameters which you can then read from the destination page:
Location: /foo.html?username=bob
The downside is that the user can easily see and modify this value.
2) Cookies
The session, which is determined by cookies, is another place to store temporary data. But this has several downsides:
It is not tied to any request, so you all consequent request will have access to the data until you delete it.
It doesn't work if the user has disabled cookies. On the other hand query parameters always work.
If you choose the first option, you may need to write some of your own code since Django doesn't seem to offer a convenient method to do this (someone correct me if I'm wrong). Here's a sample function that wraps the functionality:
def redirect_with_query(path, query=None, *args, **kwargs):
if query is None:
query = {}
url = resolve_url(path, *args, **kwargs)
if len(query):
q_dict = QueryDict(mutable=True)
q_dict.update(query)
url += '?' + q_dict.urlencode()
return HttpResponseRedirect(url)
First Solution::
If you have access to the request variable in social_user, you can simply make use of sessions. Sessions are the best way to transfer data between 2 views.
def social_user(request):
request.session['variable'] = 'value'
return redirect('name of the view')
Then inside your view, you can access this variable using request.session.get('variable')
Second Solution::
In case you can't set session variables, send them as query parameters.
def social_user():
//do something here
return redirect("your_view/?variable=value")
Build a "mock django request" manually and call the view directly
Use query parameters
Use render() and pass the variable via context:
def social_user(request)
// do something here
return render(request, template_name='template.html', context={'variable': 'value'})
I am writing a Proxy Server where I will intercept a request, and check the header to see if it has a cookie named URID, and if it does not, I want to redirect the request to a fw.asta.com, see if this domain has a cookie called URID, and if it doesn't, I want to set the cookie for fw.cisco.com, and then redirect to the originally intended site, and then set the same cookie for that site as well. If the cookie exists for the fw.asta.com site, I will use that cookie to redirect to the originally intended site, and if the originally intended site has the URID cookie, I will simply serve the request on behalf of the site.
So I have been struggling with this for quite some time. I have a feeling like I don't understand what actually happens when I set a cookie during a redirection.
I have this code right here:
def do_GET(self):
#seperate the path into parts
(scm, netloc, path, params, query, fragment) = urlparse.urlparse(self.path, 'http')
#check the header for the urid cookie, returns the URID if present
urid = self.find_urid()
if netloc == 'fw.asta.com':
if urid:
#redirect to the originally intended site and set the cookie
path = self.path[23:]
self.redirect_with_urid(path, urid)
else:
#redirect to fw.asta.com
urid = 333
self.redirect_with_urid(self.path, urid)
else:
if urid:
#simply serve the site because if has the URID
self.final_destination()
else:
#no urid, so we need to go the fw.asta.com to check if the urid is there
self.redirect('http://fw.asta.com/?url=' + str(self.path))
As you can see the logic makes sense, however when I run the code, I run into an infinite redirect loop to the originally intended site.
When I get the redirect response, I can clearly see that the set-cookie field is there, however, when I get the redirected request, it does not send the cookie back to me.
Is this even the right way to go about the problem? Any advice would be appreciated.