I use Flask framework in my project with pure json api. It renders only json responses without html or static files.
I am trying to achieve abort() function with custom http code, in my case 204 (No Content) which isn't defined by default. I have currently code like:
# Error define
class NoContent(HTTPException):
code = 204
description = ('No Content')
abort.mapping[204] = NoContent
def make_json_error(ex):
response = jsonify(error=str(ex))
response.status_code = (ex.code
if isinstance(ex, HTTPException)
else 500)
return response
custom_exceptions = {}
custom_exceptions[NoContent.code] = NoContent
for code in custom_exceptions.iterkeys():
app.error_handler_spec[None][code] = make_json_error
# Route
#app.route("/results/<name>")
def results(name=None):
return jsonify(data=results) if results else abort(204)
It works well I get response like:
127.0.0.1 - - [02/Dec/2014 10:51:09] "GET /results/test HTTP/1.1" 204 -
But without any content. It renders nothing, not even blank white page in browser.
I can use errorhandler
#app.errorhandler(204)
def error204(e):
response = jsonify(data=[])
return response
But it returns 200 http code. In need 204 here. When I add in error204() line like:
response.status_code = 204
It renders nothing once again.
I am stuck and I have no idea where there is an error with this approach. Please help.
If my approach is wrong from design perspective please propose something else.
Thanks in advance.
Remember, HTTP 204 is "No Content". RFC 7231 (and RFC 2616 before it) requires that user-agents ignore everything after the last header line:
The 204 (No Content) status code indicates that the server has successfully fulfilled the request and that there is no additional content to send in the response payload body ... A 204 response is terminated by the first empty line after the header fields because it cannot contain a message body.
~ RFC 7231 (emphasis mine)
The 204 response MUST NOT include a message-body, and thus is always terminated by the first empty line after the header fields.
~ RFC 2616
You need to return the status code in the error handler.
#app.errorhandler(204)
def error204(e):
response = jsonify(data=[])
return response, 204
Leaving off the status code is interpreted as 200 by Flask.
Related
there I am new to the flask.
The scenario:
I am trying to redirect to a certain route after submitting the form.
So I am using flask redirect for this along with code parameter.
#topics_bp.route('/create_topic/',methods =['GET','POST'] )
def create_topic():
if request.method == 'GET':
#send the form for create topic!
formData = TopicForm()
return render_template('create-topic.html',form = formData)
if request.method == 'POST':
# check the post method and redirect
return redirect(url_for('topic.topics'),code=201)
Basically , I want to return HTTP code 201 for a record created and then redirect to the intended route.
But if I do like this, it simply returns a redirection page. Every time I need to click manually.
Is there any workaround to return 201 code and redirect automatically?
Thanks in advance!
I want to return HTTP code 201 for a record created and then redirect to the intended route.
That's not something you can do with HTTP. A redirect is itself a specific HTTP 30x status code:
In HTTP, redirection is triggered by a server sending a special redirect response to a request. Redirect responses have status codes that start with 3, and a Location header holding the URL to redirect to.
Either you return a 201 status code or you return one of the HTTP redirection status codes. You can't do both.
The flask.redirect() function generates a valid 30x response (with the required Location header), and the documentation for the function states what redirect status codes are supported:
Supported codes are 301, 302, 303, 305, 307, and 308. 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.
The function doesn't enforce this, however; the status code is not validated.
You need to distinguish between a browser and other clients here. A 201 Created response is something you typically return from a REST API to a client that expects simple JSON or XML interactions at a programmable level. Redirects, on the other hand, are more typically used in a frontend, in the presentation layer.
If you are coding a HTML-based front-end, just return a redirect. Humans don't read response codes, they read rendered HTML pages. They don't care and don't need to know the exact HTTP codes used to make the browser do the right thing. Given that your route also includes a form, you are almost certainly building a site for humans and not for programmatic clients.
If you are building a REST API, then return a 201 response, and document that the API client will have to make a new request based on the Location header you included, or on something in the body of the response. A HTML browser will not follow the Location header on 201 responses however.
I then would not use the redirect() function for this, even if it does allow you to use 201 as the status code, because it always produces a (simple) HTML body containing the text you see in your browser about an automatic redirect.
I'm using Python 3.7 with urllib.
All work fine but it seems not to athomatically redirect when it gets an http redirect request (307).
This is the error i get:
ERROR 2020-06-15 10:25:06,968 HTTP Error 307: Temporary Redirect
I've to handle it with a try-except and manually send another request to the new Location: it works fine but i don't like it.
These is the piece of code i use to perform the request:
req = urllib.request.Request(url)
req.add_header('Authorization', auth)
req.add_header('Content-Type','application/json; charset=utf-8')
req.data=jdati
self.logger.debug(req.headers)
self.logger.info(req.data)
resp = urllib.request.urlopen(req)
url is an https resource and i set an header with some Authhorization info and content-type.
req.data is a JSON
From urllib documentation i've understood that the redirects are authomatically performed by the the library itself, but it doesn't work for me. It always raises an http 307 error and doesn't follow the redirect URL.
I've also tried to use an opener specifiyng the default redirect handler, but with the same result
opener = urllib.request.build_opener(urllib.request.HTTPRedirectHandler)
req = urllib.request.Request(url)
req.add_header('Authorization', auth)
req.add_header('Content-Type','application/json; charset=utf-8')
req.data=jdati
resp = opener.open(req)
What could be the problem?
The reason why the redirect isn't done automatically has been correctly identified by yours truly in the discussion in the comments section. Specifically, RFC 2616, Section 10.3.8 states that:
If the 307 status code is received in response to a request other
than GET or HEAD, the user agent MUST NOT automatically redirect the
request unless it can be confirmed by the user, since this might
change the conditions under which the request was issued.
Back to the question - given that data has been assigned, this automatically results in get_method returning POST (as per how this method was implemented), and since that the request method is POST, and the response code is 307, an HTTPError is raised instead as per the above specification. In the context of Python's urllib, this specific section of the urllib.request module raises the exception.
For an experiment, try the following code:
import urllib.request
import urllib.parse
url = 'http://httpbin.org/status/307'
req = urllib.request.Request(url)
req.data = b'hello' # comment out to not trigger manual redirect handling
try:
resp = urllib.request.urlopen(req)
except urllib.error.HTTPError as e:
if e.status != 307:
raise # not a status code that can be handled here
redirected_url = urllib.parse.urljoin(url, e.headers['Location'])
resp = urllib.request.urlopen(redirected_url)
print('Redirected -> %s' % redirected_url) # the original redirected url
print('Response URL -> %s ' % resp.url) # the final url
Running the code as is may produce the following
Redirected -> http://httpbin.org/redirect/1
Response URL -> http://httpbin.org/get
Note the subsequent redirect to get was done automatically, as the subsequent request was a GET request. Commenting out req.data assignment line will result in the lack of the "Redirected" output line.
Other notable things to note in the exception handling block, e.read() may be done to retrieve the response body produced by the server as part of the HTTP 307 response (since data was posted, there might be a short entity in the response that may be processed?), and that urljoin is needed as the Location header may be a relative URL (or simply has the host missing) to the subsequent resource.
Also, as a matter of interest (and for linkage purposes), this specific question has been asked multiple times before and I am rather surprised that they never got any answers, which follows:
How to handle 307 redirection using urllib2 from http to https
HTTP Error 307: Temporary Redirect in Python3 - INTRANET
HTTP Error 307 - Temporary redirect in python script
I am sending post request in the body of some json data, to process on server and I want the results back to client(c++ app on phone) in the form of json data and hence parse on mobile.
I have the following code inside handler:
class ServerHandler(tornado.web.RequestHandler):
def post(self):
data = tornado.escape.json_decode(self.request.body)
id = data.get('id',None)
#process data from db (take a while) and pack in result which is dictinary
result = process_data(id)# returns dictionary from db= takes time
print 'END OF HANDLER'
print json.dumps(result)
#before this code below I have tried also
#return result
#return self.write(result)
#return self.write(json.dumps(result))
#return json.dumps(result)
self.set_header('Content-Type', 'application/json')
json_ = tornado.escape.json_encode(result)
self.write(json_)
self.finish()
#return json.dumps(result)
I always get printed 'END OF HANDLER' and valid dictinary/json below on console but when I read at client mobile I always get
<html><title>405: Method Not Allowed</title><body>405: Method Not Allowed</body></html>
Does anyone have any idea what is the bug ?
(I am using CIwGameHttpRequest for sending request and it works when file is static =>name.json but now same content is giving error in post request. )
The error (HTTP 405 Method Not Allowed) means that you have made a request to a valid URL, but you are using an HTTP verb (e.g. GET, POST, PUT, DELETE) that cannot be used with that URL.
Your web service code appears to handle the POST verb, as evidenced by the post method name, and also by the fact that incoming requests appear to have a request body. You haven't shown us your C++ client code, so all I can do is to speculate that it is making a GET request. Does your C++ code call Request->setPOST();? (I haven't worked with CIwGameHttpRequest before, but Googling for it I found this page from which I took that line of code.)
I've not worked with Tornado before, but I imagine that there is some mechanism somewhere that allows you to connect a URL to a RequestHandler. Given that you have a 405 Method Not Allowed error rather than 404 Not Found, it seems that however this is done you've done it correctly. You issue a GET request to Tornado for the URL, it determines that it should call your handler, and only when it tries to use your handler it realises that it can't handle GET requests, concludes that your handler (and hence its URL) doesn't support GETs and returns a 405 error.
In my django app, I have an authentication system. So, If I do not log in and try to access some profile's personal info, I get redirected to a login page.
Now, I need to write a test case for this. The responses from the browsers I get is :
GET /myprofile/data/some_id/ HTTP/1.1 302 0
GET /account/login?next=/myprofile/data/some_id/ HTTP/1.1 301 0
GET /account/login?next=/myprofile/data/some_id/ HTTP/1.1 200 6533
How do I write my test ? This what I have so far:
self.client.login(user="user", password="passwd")
response = self.client.get('/myprofile/data/some_id/')
self.assertEqual(response.status,200)
self.client.logout()
response = self.client.get('/myprofile/data/some_id/')
What could possibly come next ?
Django 1.4:
https://docs.djangoproject.com/en/1.4/topics/testing/#django.test.TestCase.assertRedirects
Django 2.0:
https://docs.djangoproject.com/en/2.0/topics/testing/tools/#django.test.SimpleTestCase.assertRedirects
SimpleTestCase.assertRedirects(response, expected_url, status_code=302, target_status_code=200, msg_prefix='', fetch_redirect_response=True)
Asserts that the response returned a status_code redirect status, redirected to expected_url (including any GET data), and that the final page was received with target_status_code.
If your request used the follow argument, the expected_url and target_status_code will be the url and status code for the final point of the redirect chain.
If fetch_redirect_response is False, the final page won’t be loaded. Since the test client can’t fetch external URLs, this is particularly useful if expected_url isn’t part of your Django app.
Scheme is handled correctly when making comparisons between two URLs. If there isn’t any scheme specified in the location where we are redirected to, the original request’s scheme is used. If present, the scheme in expected_url is the one used to make the comparisons to.
You could also follow the redirect with:
response = self.client.get('/myprofile/data/some_id/', follow=True)
which would mirror the user experience in the browser and make assertions of what you expect to find there, such as:
self.assertContains(response, "You must be logged in", status_code=401)
You can check response['Location'] and see if it matchs with the expected url. Check also that status code is 302.
response['Location'] doesn't exist in 1.9. Use this instead:
response = self.client.get('/myprofile/data/some_id/', follow=True)
last_url, status_code = response.redirect_chain[-1]
print(last_url)
You can use assertRedirects eg:
response = self.client.get('/sekrit/')
self.assertRedirects(response, '/other/login/?next=/sekrit/')
https://docs.djangoproject.com/en/2.0/topics/testing/tools/#django.test.SimpleTestCase.assertRedirects
If you need to get url which redirected
If follow is True
You will get url in
response.redirect_chain[-1]
If follow is False
response.url
I am using urllib2 to post data to a form. The problem is that the form replies with a 302 redirect. According to Python HTTPRedirectHandler the redirect handler will take the request and convert it from POST to GET and follow the 301 or 302. I would like to preserve the POST method and the data passed to the opener. I made an unsuccessful attempt at a custom HTTPRedirectHandler by simply adding data=req.get_data() to the new Request.
I am sure this has been done before so I thought I would make a post.
Note: this is similar to this post and this one but I don't want to prevent the redirect I just want to keep the POST data.
Here is my HTTPRedirectHandler that does not work
class MyHTTPRedirectHandler(urllib2.HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
"""Return a Request or None in response to a redirect.
This is called by the http_error_30x methods when a
redirection response is received. If a redirection should
take place, return a new Request to allow http_error_30x to
perform the redirect. Otherwise, raise HTTPError if no-one
else should try to handle this url. Return None if you can't
but another Handler might.
"""
m = req.get_method()
if (code in (301, 302, 303, 307) and m in ("GET", "HEAD")
or code in (301, 302, 303) and m == "POST"):
# Strictly (according to RFC 2616), 301 or 302 in response
# to a POST MUST NOT cause a redirection without confirmation
# from the user (of urllib2, in this case). In practice,
# essentially all clients do redirect in this case, so we
# do the same.
# be conciliant with URIs containing a space
newurl = newurl.replace(' ', '%20')
return Request(newurl,
headers=req.headers,
data=req.get_data(),
origin_req_host=req.get_origin_req_host(),
unverifiable=True)
else:
raise HTTPError(req.get_full_url(), code, msg, headers, fp)
This is actually a really bad thing to do the more I thought about it. For instance, if I submit a form to
http://example.com/add (with post data to add a item)
and the response is a 302 redirect to http://example.com/add and I post the same data that I posted the first time I will end up in an infinite loop. Not sure why I didn't think of this before. I'll leave the question here just as a warning to anyone else thinking about doing this.