Why does this default route redirect? - python

While testing the API functionality with curl, i tried sending a post data to the route below. while watching the debug, the views rather responded to a 301 redirect preventing to grab the needed data. what am i doing wrong?
here is my current views.
from flask import Flask, jsonify, render_template, request
from flask_cors import CORS
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route("/api/user/login/", methods=["GET", "POST"])
def login(*args, **kwargs):
print 'Got request for login'
print args
print kwargs
print request.args
print request.args.get("username")
print request.values.get("username")
print request.method
response = {'username': 'Erik'}
dict = request.args
for key in dict:
print 'form key ' + dict[key]
# return jsonify(response)
return response
if __name__ == "__main__":
app.run(host='0.0.0.0', port=8080)
Calling the following code,
curl -d "username=Flash" http://0.0.0.0:8080/api/user/login
initiates a redirection
/home/user/fab/bin/python2.7 /home/user/PycharmProjects/myelm/server.py
* Running on http://0.0.0.0:8080/ (Press CTRL+C to quit)
127.0.0.1 - - [06/Sep/2017 23:04:25] "POST /api/user/login HTTP/1.1" 301 -

Here is the documentation about this behavior:
Unique URLs / Redirection Behavior
Flask’s URL rules are based on Werkzeug’s routing module. The idea behind that module is to ensure beautiful and unique URLs based on precedents laid down by Apache and earlier HTTP servers.
Take these two rules:
#app.route('/projects/')
def projects():
return 'The project page'
#app.route('/about')
def about():
return 'The about page'
Though they look rather similar, they differ in their use of the trailing slash in the URL definition. In the first case, the canonical URL for the projects endpoint has a trailing slash. In that sense, it is similar to a folder on a filesystem. Accessing it without a trailing slash will cause Flask to redirect to the canonical URL with the trailing slash.
In the second case, however, the URL is defined without a trailing slash, rather like the pathname of a file on UNIX-like systems. Accessing the URL with a trailing slash will produce a 404 “Not Found” error.
This behavior allows relative URLs to continue working even if the trailing slash is omitted, consistent with how Apache and other servers work. Also, the URLs will stay unique, which helps search engines avoid indexing the same page twice.

Related

How to view single, trailing question mark in URL passed to Flask?

I'm writing a Flask application to simulate a piece of equipment that is accessed via HTTP. Queries to the equipment end in a ?; e.g. http://10.10.10.10/:MN? will return the model number.
I wrote the simulator to check the last character of the request.url. However, the question mark is not present in the request.
Test Code:
from flask import Flask, request
app = Flask(__name__)
#app.route("/<command>")
def handle_route(command):
resp = f"request.url: {request.url}"
return resp
if __name__ == "__main__":
app.run("127.0.0.1", 9023)
Checking http://127.0.0.1:9023/:MN? in a browser yields:
request.url: http://127.0.0.1:9023/:MN
Is the question mark in another Flask variable that I can access? I've checked request.url, request.root_url, request.base_url, request.path, and request.query_string and have not found the missing question mark.

Why am I getting a response from the wrong API endpoint?

I am following this tutorial initially I was trying to get a response using postman with the url
ec2-x-x-xxx-xx.eu-west-2.compute.amazonaws.com/:8080
but it would not return a response, so then I tried without the / at the end and it returned what I wanted, why is this happening as my flask route clearly has a / in it
My flask app looks like this
from flask import Flask
application = Flask(__name__)
#application.route("/")
def hello():
return "<h1 style='color:blue'>Hello There!</h1>"
if __name__ == '__main__':
application.run(host="0.0.0.0", port="8080")
The order of the parts of a URL is important.
The URL
ec2-x-x-xxx-xx.eu-west-2.compute.amazonaws.com/:8080
Is going to attempt port 80, and look for a path /:8080.
ec2-x-x-xxx-xx.eu-west-2.compute.amazonaws.com:8080/
Will attempt port 8080 and look for a path /
There are two different concepts that you are getting mixed up on here.
The line #application.route("/") defines the root of your site. That is the default entry point or path if you enter your site address in a browser without e.g /about at the end.
The address ec2-x-x-xxx-xx.eu-west-2.compute.amazonaws.com:8080 is a combination of the web server address and port, separated by a colon. You will not get a response if you alter this address. You could add a "/" after the 8080 to get to a particular page.

Post Method 405 Error

I have a decently intermediate understanding of APIs, and am not trying to host information I've got from APIs onto the web.
This incredibly simple code:
from flask import Flask, request, render_template
import requests
app = Flask(__name__)
#app.route('/temperature', methods=['POST'])
def temperature():
zipcode = request.form['zip']
r = requests.get('http://api.openweathermap.org/data/2.5/weather?zip=' +zipcode+',us&appid=9b56b06ab4c7f06821ccf55e3e10fce5')
json_obj = r.text
return json_obj
#app.route('/')
def index():
return 'hi'
if __name__ == '__main__':
app.run(debug=True)
Is not giving me anything (except the 405 error).
Can someone please explain why my POST method is not working?
While I'm not entirely sure what you have done, here's what I think you did:
You accessed the /temperature page in your browser which is by default a GET-Request.
You then get returned a 405 Method Not Allowed Error, because your route explicitly requires that the page will be accessed via the HTTP-POST Method.
Modify #app.route('/temperature', methods=['POST']) to #app.route('/temperature', methods=['POST', 'GET']) and you should be fine.
I'll post my answer from /r/ flask here, just for posterity.
the line in your code: zipcode = request.form['zip'] implies that you are submitting data from an HTML form to the view via a POST.
If there is no form, then request.form['zip'] does nothing, and is most likely raising the error. Considering that I see no route in your logic there that points to any HTML, I'm guessing that's the issue.
If you have not built a form for the user to submit the zip code, then you could build it into the route logic. For example, you could do:
#app.route('/temperature/<zipcode>')
def temperature(zipcode):
# add some kind of validation here to make sure it's a valid zipcode
r = requests.get('http://api.openweathermap.org/data/2.5/weather?zip=' +zipcode+',us&appid=DONT_SHARE_YOUR_API_KEY')
json_obj = r.text
return json_obj
You could then use http://127.0.0.1:5000/temperature/20003 to see the data for that zip code.
(as a side note, it's often unwise to publicly share your API keys. you may want to generate a new key.)

Route doesn't exist

I have the following code on my service and when requested the return is always 404.
#app.route('/v1/auth/service', methods=['POST'])
def verifyAuthService():
data = request.get_json()
But in the log file, the service returns 404.
127.0.0.1 - - [TIMEVALUE] "POST /v1/auth/service HTTP/1.1" 404 -
But it works when I use other route. I have checked if the route path or method name are duplicated and didn't find anything.
I request the service method with the following code:
r = requests.post("http://myservice.com:5001/v1/auth/service", json=jPayload)
Maybe was a newbie error, in my init.py file, I haven't imported auth_services.py.
The /v1/auth/service route wasn't interpreted by python so, the route was inaccessible.
Can you try building the URL with below code and match if the route is pointing to exactly same URL which you have called.
from flask import Flask, url_for
app = Flask(__name__)
#app.route('/v1/auth/service', methods=['POST'])
def verifyAuthService():
data = request.get_json()
with app.test_request_context():
print url_for('verifyAuthService')
Hope this helps!

Redirecting to URL in Flask

I'm trying to do the equivalent of Response.redirect as in C# - i.e.: redirect to a specific URL - how do I go about this?
Here is my code:
import os
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello():
return 'Hello World!'
if __name__ == '__main__':
# Bind to PORT if defined, otherwise default to 5000.
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port)
You have to return a redirect:
import os
from flask import Flask,redirect
app = Flask(__name__)
#app.route('/')
def hello():
return redirect("http://www.example.com", code=302)
if __name__ == '__main__':
# Bind to PORT if defined, otherwise default to 5000.
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port)
See the documentation on flask docs. The default value for code is 302 so code=302 can be omitted or replaced by other redirect code (one in 301, 302, 303, 305, and 307).
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
from flask import Flask, redirect, url_for
app = Flask(__name__)
#app.route('/')
def hello():
return redirect(url_for('foo'))
#app.route('/foo')
def foo():
return 'Hello Foo!'
if __name__ == '__main__':
# Bind to PORT if defined, otherwise default to 5000.
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port)
Take a look at the example in the documentation.
From the Flask API Documentation (v. 2.0.x):
flask.redirect(location, code=302, Response=None)
Returns a response object (a WSGI application) that, if called, redirects the client to the target location. Supported codes are 301, 302, 303, 305, and 307. 300 is not supported because it’s not a real redirect and 304 because it’s the answer for a request with a request with defined If-Modified-Since headers.
New in version 0.6: The location can now be a unicode string that is
encoded using the iri_to_uri() function.
Parameters:
location – the location the response should redirect to.
code – the redirect status code. defaults to 302.
Response (class) – a Response class to use when instantiating a response. The default is werkzeug.wrappers.Response if unspecified.
I believe that this question deserves an updated. Just compare with other approaches.
Here's how you do redirection (3xx) from one url to another in Flask (0.12.2):
#!/usr/bin/env python
from flask import Flask, redirect
app = Flask(__name__)
#app.route("/")
def index():
return redirect('/you_were_redirected')
#app.route("/you_were_redirected")
def redirected():
return "You were redirected. Congrats :)!"
if __name__ == "__main__":
app.run(host="0.0.0.0",port=8000,debug=True)
For other official references, here.
flask.redirect(location, code=302)
Docs can be found here.
Flask includes the redirect function for redirecting to any url. Futhermore, you can abort a request early with an error code with abort:
from flask import abort, Flask, redirect, url_for
app = Flask(__name__)
#app.route('/')
def hello():
return redirect(url_for('hello'))
#app.route('/hello'):
def world:
abort(401)
By default a black and white error page is shown for each error code.
The redirect method takes by default the code 302. A list for http status codes here.
its pretty easy if u just want to redirect to a url without any status codes or anything like that u can simple say
from flask import Flask, redirect
app = Flask(__name__)
#app.route('/')
def redirect_to_link():
# return redirect method, NOTE: replace google.com with the link u want
return redirect('https://google.com')
here is the link to the Flask Docs for more explanation
For this you can simply use the redirect function that is included in flask
from flask import Flask, redirect
app = Flask(__name__)
#app.route('/')
def hello():
return redirect("https://www.exampleURL.com", code = 302)
if __name__ == "__main__":
app.run()
Another useful tip(as you're new to flask), is to add app.debug = True after initializing the flask object as the debugger output helps a lot while figuring out what's wrong.
There are two ways you can redirect to a URL in Flask.
You want to for example, redirect a user to another route after he or she login, etc.
You might also want to redirect a user to a route that expect some variable example: #app.route('/post/<string:post_id>')
Well, to implement flask redirect for case # 1, its simple, just do:
from flask import Flask,redirect,render_template,url_for
app = Flask(__name__)
#app.route('/login')
def login():
# if user credentials are valid, redirect to user dashboard
if login == True:
return redirect(url_for(app.dashboard))
else:
print("Login failed !, invalid credentials")
return render_template('login.html',title="Home Page")
#app.route('/dashboard')
def dashboard():
return render_template('dashboard.html',title="Dashboard")
To implement flask redirect for case #2, do the following
from flask import Flask,redirect,render_template,url_for
app = Flask(__name__)
#app.route('/home')
def home():
# do some logic, example get post id
if my_post_id:
# **Note:** post_id is the variable name in the open_post route
# We need to pass it as **post_id=my_post_id**
return redirect(url_for(app.open_post,post_id=my_post_id))
else:
print("Post you are looking for does not exist")
return render_template('index.html',title="Home Page")
#app.route('/post/<string:post_id>')
def open_post():
return render_template('readPost.html',title="Read Post")
Same thing can be done in view
Please Note: when redirecting always use the app.home or app.something.. (route or view function name) instead of using redirect("/home").
Reason is, if you modify the route example from "/home" to "/index/page" for some reason, then your code will break
You can use like this:
import os
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello():
# Redirect from here, replace your custom site url "www.google.com"
return redirect("https://www.google.com", code=200)
if __name__ == '__main__':
# Bind to PORT if defined, otherwise default to 5000.
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port)
Here is the referenced link to this code.
How to Redirect Users / Requests in Flask
Throwing an Error inside of your API handler function will redirect your user to an error handler, which can handle redirection. Alternatively you can just call redirect like everyone else is saying, but this is another way of redirecting unauthorized users. To demonstrate what I mean, I've provided an example below.
In a case where Users should be Authorized
First lets assume you have a protected route of which you protected like this.
def handle_api_auth(func):
"""
**handle_api_auth**
wrapper to handle public api calls authentications
:param func: a function to be wrapped
:return: wrapped function
"""
#functools.wraps(func)
def auth_wrapper(*args, **kwargs):
api_key: Optional[str] = request.headers.get('x-api-key')
secret_token: Optional[str] = request.headers.get('x-secret-token')
domain: Optional[str] = request.base_url
if is_request_valid(api_key=api_key, secret=secret_token, domain=domain):
return func(*args, **kwargs)
# NOTE: throwing an Error Here will redirect your user to an error handler or alteratively you can just call redirect like everyone else is saying, but this is another way of redirecting unathorized users
message: str = "request not authorized"
raise UnAuthenticatedError(status=error_codes.un_auth_error_code, description=message)
return auth_wrapper
Definition of is_request_valid is as follows
#app_cache.cache.memoize(timeout=15 * 60, cache_none=False) # timeout equals fifteen minutes // 900 seconds
def is_request_valid(api_key: str, secret: str, domain: str) -> bool:
"""
**is_api_key_valid**
validates api keys on behalf of client api calls
:param api_key: str -> api_key to check
:param secret: str -> secret token
:param domain: str -> domain registered for the api_key and secret_token
:return: bool -> True if api_key is valid
"""
organization_id: str = config_instance.ORGANIZATION_ID
# NOTE: lets assumy api_keys_view.get_api_key will return the api keys from some database somewhere
response = api_keys_view.get_api_key(api_key=api_key, organization_id=organization_id)
response_data, status_code = response
response_dict = response_data.get_json()
if not response_dict.get('status'):
return False
api_instance: dict = response_dict.get('payload')
if not isinstance(api_instance, dict):
return False
domain: str = domain.lower().strip()
# NOTE accessing the keys this way will throw ValueError if keys are not available which is what we want
# Any Error which gets thrown Ridirects the Users from the path the user is on to an error handler.
is_secret_valid: bool = hmac.compare_digest(api_instance['secret_token'], secret)
is_domain_valid: bool = hmac.compare_digest(api_instance['domain'], domain)
_request_valid: bool = is_secret_valid and is_domain_valid
return not not api_instance.get('is_active') if _request_valid else False
Define your Error Handlers like this
from flask import Blueprint, jsonify, request, redirect
from werkzeug.exceptions Unauthorized
error_handler = BluePrint('error_handlers', __name__)
#error_handler.app_errorhandler(Unauthorized)
def handle_error(e : Unauthorized) -> tuple:
"""default unath handler"""
return jsonify(dict(message=e.description)), e.code if request.headers.get('content-type') == 'application/json' else redirect('/login')
handle other errors the same and note that in-case the request was
not a json the user gets redirected to a login page
if json the user gets sent an unathecated response then its
up to the front end to handle Unath Errors..

Categories

Resources