bottle - static files causing 303 errors - python

I am experimenting in Bottle/MongoDB extending the Blog Engine from one of the MongoDB University Courses.
I have added a header & footer template and added some simple JS, CSS and an image. These work fine on all the templates other than show_post view. The way that it seems to be set up is that the value passed in to the function includes the static files (css, js & png) as well as the URL variable for the blog post itself.
The values from the python console are:
about to query on permalink = TLxrBfyxTZjqOKqxgnUP
1.0.0.127.in-addr.arpa - - [25/Feb/2014 15:08:09] "GET /post/TLxrBfyxTZjqOKqxgnUP HTTP/1.1" 200 37682
about to query on permalink = style.css
1.0.0.127.in-addr.arpa - - [25/Feb/2014 15:08:09] "GET /post/style.css HTTP/1.1" 303 0
about to query on permalink = blog.js
1.0.0.127.in-addr.arpa - - [25/Feb/2014 15:08:09] "GET /post/blog.js HTTP/1.1" 303 0
about to query on permalink = BL_logo.png
1.0.0.127.in-addr.arpa - - [25/Feb/2014 15:08:09] "GET /post/BL_logo.png HTTP/1.1" 303 0
1.0.0.127.in-addr.arpa - - [25/Feb/2014 15:08:09] "GET /post_not_found HTTP/1.1" 200 21
1.0.0.127.in-addr.arpa - - [25/Feb/2014 15:08:09] "GET /post_not_found HTTP/1.1" 200 21
1.0.0.127.in-addr.arpa - - [25/Feb/2014 15:08:09] "GET /post_not_found HTTP/1.1" 200 21
The code in question is:
# Displays a particular blog post
#bottle.get("/post/<permalink>")
def show_post(permalink="notfound"):
cookie = bottle.request.get_cookie("session")
username = sessions.get_username(cookie)
permalink = cgi.escape(permalink)
print "about to query on permalink = ", permalink
post = posts.get_post_by_permalink(permalink)
if post is None:
bottle.redirect("/post_not_found")
# init comment form fields for additional comment
comment = {'name': "", 'body': "", 'email': ""}
return bottle.template("entry_template", dict(post=post, username=username, errors="", comment=comment))
How can I prevent the function being called at all if the 'permalink' is a file rather than a value from a query string?
Thanks,
Mark
ANSWER
With many thanks to Ron's answer below for pointing me in the right direction.
My error was down to the path to the static files being incorrect. I made the path dynamic by importing os and then changed the root value to os.path.join(os.getcwd(), 'static/js') and made the filepaths in my header.tpl absolute, e.g."/style.css".
#bottle.get('/<filename:re:.*\.css>')
def stylesheets(filename):
rootPath=os.path.join(os.getcwd(), 'static/css')
return bottle.static_file(filename, root=rootPath)

Because you've placed your assets under /post, they're conflicting with your /post/<permalink> route.
Typically, you would serve your assets (css, js, png) from their own directory; something like /static/css/foo.css. That's what I'd recommend here.
Something along the lines of this:
#bottle.get("/post/<permalink>")
def show_post(permalink="notfound"):
# code to generate a post page goes here
#bottle.get("/static/<file_name:path>"):
def static_file(file_name):
return bottle.static_file(file_name, "/full/path/to/your/static/file/root/")
The relevant Bottle docs are here; or see one of the many posts describing how to serve static file from Bottle.

Related

Cherrypy logingg POST body

I'am using default cherrypy logger.
I have log every access request to my server. For GET request i have full info, like
127.0.0.1 - - [06/Jul/2021:16:10:28] "GET /test/?contract_id=228322 HTTP/1.0" 200 33
But for POST request i can't log query params or body.
127.0.0.1 - - [06/Jul/2021:13:21:03] "POST /test HTTP/1.0" 201 169
So how can i log POST query's body?
You can create tool like
def response_logging():
cherrypy.log.access_log.info(
'[{time}] {ip} "{user_agent}" {request_line} "{query_params}" "{body}" "{status}" {response}'.format(
time=datetime.now().strftime("%d/%m/%Y:%H:%M:%S"),
ip=cherrypy.request.remote.ip,
user_agent=cherrypy.request.headers["User-Agent"],
request_line=cherrypy.request.request_line,
query_params=cherrypy.request.body.request_params or "",
body=cherrypy.request.json if hasattr(cherrypy.request, "json") else "",
status=cherrypy.response.status,
response=cherrypy.response.body
)
)
cherrypy.tools.response_logging = cherrypy.Tool('on_end_request', response_logging)
print(cherrypy.request.params) # Prints params on querystring
print(cherrypy.request.headers) # Prints received headers on request
print(cherrypy.request.body) # Prints Body received

Why does Flask redirect to "GET /? HTTP/1.1"

I'm writing a simple Flask application in which I want to send some data from the front-end to the Flask app, have it perform some operations, and return new data to the front-end for display. I have made similar applications before, and by returning the POST response object, instead of render_template(), I'm able to simply return the data and do what I want with it on the front-end. However, this time I'm having problems.
I make a POST request from the Jquery in the front-end. Everything seems to work fine, I can see the data being returned in the browser console, except the page reloads before I can display the new data. It reloads to http://xxx.x.x.x:5000/?.
I can see the get request for /? in the Flask console. I'd like to know why it is doing this, and how I can get it to stop.
(I've found this difficult to research because most search engines will silently ignore any question marks in a query.)
Flask app:
import json
from flask import Flask, Response, render_template, request
from src.simple_lookup import analogy_lookup
app = Flask(__name__)
app.config['DEBUG'] = True
#app.route('/')
def hello_world():
return render_template('index.html', results=['a', 'b'])
#app.route('/get_words_simple', methods=['POST'])
def get_words_simple():
print('request.form:', request.form.keys)
data = analogy_lookup(request.form['keyword'], request.form['base'], request.form['goal'])
resp = Response(json.dumps(data), status=200, mimetype='application/json')
print('data:', data)
print('resp:', resp)
return resp
if __name__ == "__main__":
app.run()
Jquery:
$.post('get_words_simple?', data, function(json, status) {
console.log('response:', json);
if (json.hasOwnProperty('error')) {
$('.results').append('<p>' + json.error);
return;
}
var words = json.words;
$.each(words, function(i, text) {
var p = $("<p>");
p.append(text);
$('.results').append(p);
});
});
Flask console:
127.0.0.1 - - [27/Dec/2018 11:12:21] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [27/Dec/2018 11:12:21] "GET /static/js/main.js HTTP/1.1" 200 -
request.form: <bound method MultiDict.keys of ImmutableMultiDict([('keyword', ''), ('base', ''), ('goal', '')])>
127.0.0.1 - - [27/Dec/2018 11:12:23] "GET /? HTTP/1.1" 200 -
127.0.0.1 - - [27/Dec/2018 11:12:23] "GET /static/js/main.js HTTP/1.1" 200 -
data: ['obtuvo', 'takata', 'stadshypotek', 'kriwet', 'shafiee', 'martorell', 'collum', '29,400', 'muteesa', 'patzek']
resp: <Response 111 bytes [200 OK]>
127.0.0.1 - - [27/Dec/2018 11:12:23] "POST /get_words_simple? HTTP/1.1" 200 -
Problem was that Bootstrap overrides type="submit" button functionality if it's in a form group. So my Jquery was doing everything right, but something about the html of the button was screwing everything up and trying to make a post request in a different way

Why does Django's HTTPResponseRedirect use the same HTTP method for PUT but not POST?

I have a Django project where I'm using a view to handle different HTTP methods. The POST handled the creation of an object and then redirected to the same view as a GET (or so I thought), using Django's redirect() shortcut (HTTPResponseRedirect) to return the newly created object. This worked fine. I tried the same thing with a PUT but I fell into a redirect loop. After scratching my head for a while I stumbled across this SO answer and then I inferred that since redirect doesn't handle the POST data, the request turns into a GET.
I confirmed this by watching the logs when I do the redirect from a POST:
[15/Dec/2014 00:47:43] "POST /client/151/ HTTP/1.1" 302 0
[15/Dec/2014 00:47:43] "GET /client/151/ HTTP/1.1" 200 395
However the PUT stays a PUT and throws me into a redirect loop until it errors out.
[14/Dec/2014 23:07:36] "PUT /api/asset/6779 HTTP/1.1" 301 0
[14/Dec/2014 23:07:37] "PUT /api/asset/6779/ HTTP/1.1" 302 0
[14/Dec/2014 23:07:37] "PUT /api/asset/6779 HTTP/1.1" 301 0
[14/Dec/2014 23:07:38] "PUT /api/asset/6779/ HTTP/1.1" 302 0
[14/Dec/2014 23:07:38] "PUT /api/asset/6779 HTTP/1.1" 301 0
[14/Dec/2014 23:07:39] "PUT /api/asset/6779/ HTTP/1.1" 302 0
[14/Dec/2014 23:07:39] "PUT /api/asset/6779 HTTP/1.1" 301 0
[14/Dec/2014 23:07:40] "PUT /api/asset/6779/ HTTP/1.1" 302 0
[14/Dec/2014 23:07:40] "PUT /api/asset/6779 HTTP/1.1" 301 0
[14/Dec/2014 23:07:41] "PUT /api/asset/6779/ HTTP/1.1" 302 0
[14/Dec/2014 23:07:41] "PUT /api/asset/6779 HTTP/1.1" 301 0
[14/Dec/2014 23:07:42] "PUT /api/asset/6779/ HTTP/1.1" 302 0
Shouldn't the redirect use GET? I understand what's going on but not sure why? What gives?
EDIT
# urls.py
url(r'^$', views.put_vs_post_redirect),
# views.py
from django.shortcuts import redirect
def put_vs_post_redirect(request, asset_id):
if request.method == 'GET':
return HTTPResponse('Get request')
elif request.method == 'POST':
return redirect('/')
elif request.method == 'PUT':
return redirect('/')
As mentioned in the comments, this is entirely dependent on the client, and not all clients handle redirects in the same way. You can find a decent explanation of the redirect codes and why a 301 should drop POST data on Stack Overflow.
When working with a 301 (and often a 302) redirect, most browsers will discard POST data and make a GET request. This is mostly because browsers have always done this, and POST requests most commonly come from web forms, so it makes sense that the redirect results in a GET, allowing for the browser to display a different page without interfering. This is not the case for things like PUT or PATCH requests, as they cannot currently be sent by web forms and typically play by different rules.
If you are looking to maintain the POST data on a 302 redirect, you should consider using a 307 redirect instead. A 307 request should maintain the request method, and the request body as a result.
If you are looking to maintain the POST data in a 301 redirect, there is currently a draft for a 308 status code that would work like the 307, but be permanent.
You can force the redirect to use a GET request with a 303 redirect. It works very much like a 302, but it enforces that the request method is always a GET request. It's often used in APIs for asynchronous tasks.

/socket.io/* 404 not found flask

I am trying to get socketio working correctly. When I load my page with following javascript. It tries to pull the socket.io page as expected but get a 404 not found each time.
<div id="result"></div>
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="//code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<script src="https://cdn.socket.io/socket.io-1.2.1.js"></script>
<script>
$(function(){
var socket = io.connect('/endpoint');
// on receive append data.sound and linebreak to result id on page
socket.on('receive', function(data){
$('#result').append(data.mytext);
});
$('form#emit').submit(function(event) {
socket.emit('submit', {account: $('#account').val(),
...
Here is the flask endpoint for the socketIO to use
#app.route('/socket.io/<path:remaining>')
def iocg(remaining):
from websocketinstructions import WebSocketInstructions
socketio_manage(request.environ, {'/endpoint': WebSocketInstructions}, request)
return 'done'
127.0.0.1 - - [2014-11-24 15:29:05] "GET /socket.io/?EIO=3&transport=polling&t=1416864545405-114 HTTP/1.1" 404 342 0.002768
127.0.0.1 - - [2014-11-24 15:29:06] "GET /socket.io/?EIO=3&transport=polling&t=1416864546666-4 HTTP/1.1" 404 342 0.002745
127.0.0.1 - - [2014-11-24 15:29:10] "GET /socket.io/?EIO=3&transport=polling&t=1416864550413-115 HTTP/1.1" 404 342 0.003090
127.0.0.1 - - [2014-11-24 15:29:11] "GET /socket.io/?EIO=3&transport=polling&t=1416864551672-5 HTTP/1.1" 404 342 0.003325
127.0.0.1 - - [2014-11-24 15:29:15] "GET /socket.io/?EIO=3&transport=polling&t=1416864555419-116 HTTP/1.1" 404 342 0.003315
127.0.0.1 - - [2014-11-24 15:29:16] "GET /socket.io/?EIO=3&transport=polling&t=1416864556678-6 HTT P/1.1" 404 342 0.002707
127.0.0.1 - - [2014-11-24 15:29:20] "GET /socket.io/?EIO=3&transport=polling&t=1416864560425-117 HTTP/1.1" 404 342 0.002710
127.0.0.1 - - [2014-11-24 15:29:21] "GET /socket.io/?EIO=3&transport=polling&t=1416864561684-7 HTTP/1.1" 404 342 0.003227
First, check if the link to the CDN is valid. If doesn't work, change it to a new link from here, for example:
https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.5/socket.io.min.js

Get Static Image url access count in Appengine

My appliaction need to save the particular static image accessed count in datastore.
Here is the Current Code:
Image URL: http://my_app.appspot.com/static_image
class Image_Display(webapp2.RequestHandler):
def get(self):
...........
// Increment the count & save it in datastore
self.redirect("/images/static.gif")
app = webapp2.WSGIApplication([('/static_image', Image_Display)], debug=False)
Instead of self.redirect("/images/static.gif"), Is the better way to do this ?
In this case, Always have 2 request for each image request.
"GET /static_image HTTP/1.1" 302 -
"GET /images/static.gif HTTP/1.1" 200 1453
For a redirect you use two requests. You can also read the image from the static project path in a single request:
class Image_Display(webapp2.RequestHandler):
def get(self):
...........
// Increment the count & save it in datastore
path = os.path.join(os.path.dirname(__file__), 'images', 'static.gif')
self.response.headers[b'Content-Type'] = mimetypes.guess_type('static.gif')[0]
self.response.write(file(path, 'rb').read())

Categories