Tornado not allowing put request - python

Below is my handler code where tornado is allowing to do get request where as getting method not allowed error.
i am missing something obivious?
class CustomerHandler(web.RequestHandler):
def get(self, customer_id):
data = retrieve_customer_data_from_customer_database(customer_id)
print(data)
self.write(data)
self.finish()
def put(self, data):
customer_data = data
data = json.loads(customer_data)
customer_id = customer_data['id']
update_customer_data(customer_id, data)
result_out = {}
result_out['status'] = True
self.write(json.dumps(result_out))
self.finish()

Check again indentation. Also, the data you are looking for is probably in body of the request. Here's a simple example:
import tornado.ioloop
import tornado.web
import json
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
def put(self):
body = json.loads(self.request.body)
# do some stuff here
self.write("{} your ID is {}".format(body['name'], body['id']))
if __name__ == "__main__":
application = tornado.web.Application([
(r"/", MainHandler),
])
application.listen(8888)
tornado.ioloop.IOLoop.current().start()
And the test:
$ curl http://localhost:8888/ -XPUT -d '{"id": 123, "name": "John"}'
John your ID is 123

The issue was there was extra "/" I was using in put request url while calling from frontend, that's why method not allowed error was there. although error message doesn't suggest what exactly error is.
Hope this will help someone.

Related

Quick Rest API with Python for mocking responses

I am testing a C# application that make requests to another Rest API and I want to mock the server. I known basic python and I was wondering if I can write a simple API Rest server without be involved with large frameworks like Django. It would be a simple server where I receive a json through request body and I have to return another json (with the logic of the return inside, like a view).
Best regards!
Something simple like this:
#path(/api/v1/somepath, GET)
def my_function(request):
json_input = request.body.json()
# My logic here
response.status = 200
response.body = {'some_field': 'something'}
return response
In case you really don't want to use any external frameworks/ libraries, you can create a simple class which extends BaseHTTPRequestHandler, something like this :
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import json
class S(BaseHTTPRequestHandler):
def _set_headers(self):
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
def do_GET(self):
self._set_headers()
self.data_string = self.rfile.read(int(self.headers['Content-Length']))
self.send_response(200)
self.end_headers()
data = json.loads(self.data_string)
# your processing
outJson = {"success": True}
self.wfile.write(json.dumps(outJson))
def do_HEAD(self):
self._set_headers()
self.wfile.write("HEAD")
def do_POST(self):
self._set_headers()
self.wfile.write("POST")
and then start the server locally at the port of your choice(80 by default) simply like this:
def run(port=80):
httpd = HTTPServer(('', port), S)
print 'Starting httpd...'
httpd.serve_forever()
if __name__ == "__main__":
from sys import argv
if len(argv) == 2:
run(port=int(argv[1]))
else:
run()
and if you need to manage lots of routes easily, you can use klein or Flask microframeworks (or even bottle) to make it easier and simpler for you, a simple minimal usage example for using klein would look something like this:
import json
from klein import Klein
class ItemStore(object):
app = Klein()
def __init__(self):
self._items = {}
#app.route('/')
def items(self, request):
request.setHeader('Content-Type', 'application/json')
return json.dumps(self._items)
#app.route('/<string:name>', methods=['PUT'])
def save_item(self, request, name):
request.setHeader('Content-Type', 'application/json')
body = json.loads(request.content.read())
self._items[name] = body
return json.dumps({'success': True})
#app.route('/<string:name>', methods=['GET'])
def get_item(self, request, name):
request.setHeader('Content-Type', 'application/json')
return json.dumps(self._items.get(name))
and then run the server like this:
if __name__ == '__main__':
store = ItemStore()
store.app.run('localhost', 8080)
Also, if you want to use the mock apis remotely and not just locally, you can use tunneling tools like ngrok. It's pretty simple and easy to use.

Bottle/python error handling with default_error_handler

With bottle/python I'm trying to get a more detailed error handling. There is a page describing a method
How to return error messages in JSON with Bottle HTTPError?, but can't implement it with my project.
ara.hayrabedian's answer on the mentioned page works, but with the hope to get more details for error situations Michael's code has some charm. Only any variation I tested fails. Basically I have (out of a longer coding):
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from bottle import Bottle, run, static_file, view, template, \
get, post, request, debug
from bottle import route, response, error
import json
app = Bottle()
#class JSONErrorBottle(bottle.Bottle): ### just an not working alternative!?
class JSONErrorBottle(Bottle):
def default_error_handler(app, res):
bottle.response.content_type = 'application/json'
print("XXXXXXX " + json.dumps(dict(error=res.body, status_code=res.status_code)))
return json.dumps(dict(error=res.body, status_code=res.status_code))
app.install(JSONErrorBottle)
def main():
app.run(host = prefs['server'], port = prefs['port'], reloader=False)
if __name__ == '__main__':
rcode = main()
Calling an invalid page that 'default_error_handler' isn't called, just the standard bottle html error page with "Error: 404 Not Found"
Solution for micro-services design
def handle_404(error):
return "404 Error Page not Found"
app = bottle.Bottle()
app.error_handler = {
404: handle_404
}
bottle.run(app)
The Michael's way is indeed the most "right" way.
That worked for me as expected (at least with python-3.6.6 and bottle-0.12.13):
from bottle import Bottle, run, abort
import bottle, json
class JSONErrorBottle(Bottle):
def default_error_handler(self, res):
bottle.response.content_type = 'application/json'
return json.dumps(dict(error = res.body, status_code = res.status_code))
app = JSONErrorBottle()
#app.route('/hello')
def hello():
return dict(message = "Hello World!")
#app.route('/err')
def err():
abort(401, 'My Err')
run(app, host='0.0.0.0', port=8080, debug=True)
Now every error() and abort() is json

Handling POST requests using Pythons web.py

I have a very simple server. I use Python 2.7 with web.py.
Basically, my code looks like this:
urls = ("/endpoint", "Endpoint")
class Endpoint(object):
def GET(self):
return "End point"
def POST(self):
data = web.data()
web.header('Content-Type', 'application/json')
result = json.loads(data)
logging.info("[Server] Endpoint POST with payload: " + json.dumps(result))
return "Endpoint POST"
I tested this server by making POST requests like this:
echo '{"field": "test"}' | curl -d #- http://my.ip.number:port/endpoint
I tried server other methods of making POST requests. I also tried making get requests, both from the terminal and the browser.
In all cases, I get this very strange error.
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/web.py-0.37-py2.7.egg/web/application.py", line 239, in process
return self.handle()
File "/usr/local/lib/python2.7/dist-packages/web.py-0.37-py2.7.egg/web/application.py", line 229, in handle
fn, args = self._match(self.mapping, web.ctx.path)
File "/usr/local/lib/python2.7/dist-packages/web.py-0.37-py2.7.egg/web/application.py", line 427, in _match
for pat, what in mapping:
ValueError: need more than 1 value to unpack
Why is this error occurring and what can I do to prevent it?
Thanks!
EDIT 1:
After the traceback is displayed, I also get this:
192.168.46.1:51390 - - [16/Mar/2016 12:54:08] "HTTP/1.1 GET /favicon.ico" - 500 Internal Server Error
Neither the IP nor the port are the ones that I am using.
EDIT 2
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Unicode
from __future__ import unicode_literals
import sys
reload(sys)
# -----
# Logging
import logging
logging.basicConfig(level=logging.INFO)
# -----
# Libs
import web
import json
# -----
urls = ("/", "Index",
"/endpoint1", "EP1"
"/endpoint2", "EP2")
class Index(object):
# In the browser, this displays "Index", but also causes the error on the server side.
def GET(self):
return "Index"
# Doesn't do anything, but causes the error
def POST(self):
data = web.data()
web.header('Content-Type', 'application/json')
result = json.loads(data)
logging.info("[Server] Index " + json.dumps(result))
return "Index POST"
class EP1(object):
def GET(self):
return "EP1"
def POST(self):
data = web.data()
web.header('Content-Type', 'application/json')
result = json.loads(data)
logging.info("[Server] EP1 " + json.dumps(result))
return "EP1 POST"
class EP2(object):
def GET(self):
return "EP2"
def POST(self):
data = web.data()
web.header('Content-Type', 'application/json')
result = json.loads(data)
logging.info("[Server] EP2 " + json.dumps(result))
return "EP2 POST"
if __name__ == "__main__":
logging.info("[Server] Starting server.")
app = web.application(urls, globals())
app.run()
This is how my server looks like.
I start the server like this:
python server.py 0.0.0.0:7331
If I access the server's root endpoint from the browser, I get "Index" and the error still occurs. The other two endpoints don't return anything and cause the error.
You're missing a comma at second line here:
urls = ("/", "Index",
"/endpoint1", "EP1"
"/endpoint2", "EP2")
It should be like this:
urls = ("/", "Index",
"/endpoint1", "EP1",
"/endpoint2", "EP2")
What happens without the comma is that Python concatenates the two strings without a comma in between.
So with your code, urls was actually
("/", "Index", "/endpoint1", "EP1/endpoint2", "EP2")

How do I implement dynamic routing in python?

I am attempting to have the url input be (some kind of url)/page/(whatever page number of the template we want). I am having trouble with this, and am not sure what the issue is. The first part of my code goes as follows:
from wsgiref.simple_server import make_server
from wsgiref.util import setup_testing_defaults
routing_table = {}
def route(url, func):
routing_table[url] = func
def find_path(url):
if url in routing_table:
return routing_table[url]
else:
return None
def app(environ, start_response):
setup_testing_defaults(environ)
handler = find_path(environ['PATH_INFO'])
if handler is None:
status = '404 Not Found'
body = "<html><body><h1>Page Not Found</h1></body></html>"
else:
status = '200 OK'
body = handler()
headers = [('Content-type', 'text/html: charset=utf-8')]
start_response(status, headers)
return [body.encode("utf-8")]
def run(ip, port):
myserver = make_server(ip, port, app)
print("Serving testings of wsgi at http://%s:%s" % (ip, port))
myserver.serve_forever()
The next part of the code is where I believe the main issue is occurring at page(page_id):
import test
import re
def index():
return "This is the main page"
def hello():
return "Hi, how are you?"
def page(page_id):
return "This is page number: %d" % page_id
if __name__ == '__main__':
test.route("/", index)
test.route("/Hello", hello)
test.route('/page/<page_id>', page)
test.run("127.0.0.1", 8000)
My thinking is that we need to import the template, and have the logic defined within the templates, themselves. However, when I attempt to do this, I am unable to "from python import Template" and utilize the template(myTemplates.tpl). I believe my syntax may be incorrect, but python.org has shown no suggestions, thus far.
In find_path you are simply comparing the given string to one of the URLs on the routing table
if url in routing_table:
So the only page you could actually reach for the '/page/<page_id>' route is the literal '/page/<page_id>'.
What you would need to do is parse the URL to see if it matches the format you are passing in, not compare the static string. Makes sense right?
In that case you might wanna look into regular expressions: https://docs.python.org/2/library/re.html

Flask equivalent of Sinatra's "passing"

In my Flask application, I want to expose a URI like this:
http://<base_uri>/some_string
and I wish to handle requests to it differently depending on whether some_string is an integer or not.
With Sinatra I can achieve that via "passing" as shown below:
get '/:some_string' do
if is_integer(:some_string)
'Your URI contains an integer'
else
pass # This will pass the request on the the method below which can handle it
end
get '/*' do
'Your URI contains some string'
end
Here the call pass in the first route lets the second route process the request if :some_string is not an integer.
I couldn't find any equivalent functionality in Flask. Can someone please suggest a solution in Flask?
Type conversion in url routes can do this for you:
from flask import Flask
import unittest
app = Flask(__name__)
app.debug = True
#app.route('/<int:thing>')
def num(thing):
return 'INT'
#app.route('/<thing>')
def string(thing):
return 'STR'
class TestDispatch(unittest.TestCase):
def setUp(self):
self.client = app.test_client()
def test_int(self):
resp = self.client.get('/10')
self.assertEqual("INT", resp.data)
def test_str(self):
resp = self.client.get('/hello')
self.assertEqual("STR", resp.data)
if __name__ == '__main__':
unittest.main()

Categories