Problem with json response in Python REST server I'm writing - python

For my own education I'm creating a simple daemon that will function as a REST server, listening on socket 9000. For my first test I'm trying to do a REST call of simply doing a GET on http://localhost:9000/getsettings.json.
The simple server I hacked up gets the request OK, but I think there's a problem with the response it's sending. I'm trying to send the response as JSON. The code for the server socket is below. Below that is the jQuery code I'm using to make the JSON request. The print statement in the Python server does print a valid set of settings in JSON format. Neither alert in the Javascript is being called when the page loads. Any ideas?
Python:
def socketThread(self):
while 1:
sock, address = self.serverSocket.accept()
fs = sock.makefile()
line = fs.readline();
if line.startswith('GET /settings.json'):
sock.sendall("HTTP/1.0 200 OK\r\n")
sock.sendall("Content-Type: application/json\r\n")
settings = json.dumps(self.currentSettings) #, indent=4
sock.sendall("Content-Length: " + str(len(settings)) + '\r\n\r\n')
sock.sendall(settings)
print settings
sock.close()
Javascript:
$(document).ready(function() {
$.ajax({
url: 'http://localhost:8080/settings.json',
cache: false,
success: function(json){
alert('json success ' + json);
},
error: function(xhr, textStatus, errorThrown) {
alert(xhr.statusText);
}
});
});

You need to return a 200 OK HTTP response and an empty line before sending your content.
You'll probably need some HTTP headers, too (Content-type would be an obvious one).
I would, however, recommend using one of Python's many HTTP libraries instead of implementing the protocol yourself.

Check that the server is actually being hit (eg, put an import pdb; pdb.set_trace() in your code. I found that jQuery executes the success callback with empty body when the server is not running (or listening on a different port...)

OK, the problem was on the jQuery side, my request was being thwarted by the cross domain origin protection issue - my request was going to localhost on a different port so jQuery wasn't pulling down the data. Now I need to figure out how to make that work. Thanks for the help guys.

Related

Golang: Get request sends me html contents but works fine from python and curl

I am trying to call a simple api by using golang. But, each time it sends me html content of login page instead of actual data. But same get request works from python and curl.
func main() {
client := &http.Client{}
req, err := http.NewRequest("GET", "https://www.lrn.com", nil)
if err != nil {
os.Exit(1)
}
q := req.URL.Query()
q.Add("phoneList", "XXXXXX")
q.Add("output", "json")
q.Add("version", "5")
//req.URL.RawQuery = q.Encode()
req.Header.Set("loginId", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
fmt.Println(req.URL.String())
resp, err := client.Do(req)
if err != nil {
fmt.Println("Errored when sending request to the server")
return
}
defer resp.Body.Close()
resp_body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(resp.Status)
fmt.Println(string(resp_body))
}
Above script gives me html content of login page. But if i use python, it works just fine.
import requests
r=requests.get("https://www.lrn.com", params = {'version':'5', "phoneList":"XXXXXX", "output":"json"}, headers={"loginId":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"})
print r.text
Could someone please explain me what might be wrong in my golang script.
//req.URL.RawQuery = q.Encode()
req.Header.Set("loginId", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
fmt.Println(req.URL.String())
The last line outputs the URL it uses. With the code above this will be https://www.lrn.com. Given that you want to have query parameters the expected value would be instead https://www.lrn.com?output=json&... though. So something is clearly missing from your construction of the URL.
When activating the line you've explicitly commented out the result is very different:
req.URL.RawQuery = q.Encode() // activate this line
...
fmt.Println(req.URL.String())
In this case I get https://www.lrn.com?output=json&phoneList=XXXXXX&version=5, which is the expected value. And with this value the result from the server is for me the same as for your Python code.
But this seems to be only the first problem. Based on input in the comments I could track down the second problem, which was caused by a broken server together with header normalization by Golang. Based on the comments I've now used a different URL https://www.dncscrub.com/app/main/rpc/scrub where the Python code worked and the Golang code again did not work.
By comparing the HTTP requests used by both and reducing to their essential difference it showed, that the server erroneously interpreted a HTTP request header in a case-sensitive way. This worked
GET /app/main/rpc/scrub?version=5&phoneList=2123727200 HTTP/1.1
Host: www.dncscrub.com
loginId: 0610A62F
And resulted in the expected HTTP/1.1 401 Invalid credentials. While this did not work
GET /app/main/rpc/scrub?version=5&phoneList=2123727200 HTTP/1.1
Host: www.dncscrub.com
Loginid: 0610A62F
but resulted in a redirect HTTP/1.1 302 Found.
The only difference between these requests is the use of loginId: ... vs. Loginid: .... The server is clearly broken by treating HTTP header fields as case-sensitive, see Are HTTP headers case-sensitive?
which also links to the relevant standards.
Since req.Header.Set will normalize the header field one cannot use it. A work around is to manipulate the dictionary instead, which is usually not a good idea:
req.Header["loginId"] = []string{"0610A62F"}
See also GoLang: Case-sensitive HTTP Headers with net/http for a similar problem.

Returning JSON to AJAX after using sys.exit()

I'm working with a local web application which executes, via AJAX, a Python script on the web-server.
I tried to deploy the application on a remote server and since have been facing an AJAX statusText: "parsererror".
I assume this is because it isn't receiving proper JSON as expected when the script fails. How could I make sure the script interruption returns JSON to the browser, so that I can understand the nature of the error?
The AJAX call is the following:
$.ajax({
async: true,
type: "POST",
url: "cgi-bin/myscript.py",
contentType: "application/x-www-form-urlencoded;charset=ISO-8859-15",
data: JSON.stringify(run_details),
dataType: "JSON"
})
.done(function(data) {
console.log(data)
})
.fail(function(xhr) {
console.log('Error in myscript (cf. Ajax error below).');
console.log('error',xhr);
});
In working conditions, I return data to the client using the following approach, which works well on the local server:
success_message = "Some text"
result = {'message': success_message};
#Send JSON result back to Ajax
print("This line allows the following to be received by browser \n")
json.dump(result, sys.stdout)
In case an error in the script occurs, I call this function to interrupt the script:
def interruptScript(a_cursor, a_conn, error_message):
# Close communication with the database
a_cursor.close()
a_conn.close()
#Add error message to object
result = {'message': error_message};
#Send JSON result back to Ajax
print("This lines allows the following to be received by browser \n")
json.dump(result, sys.stdout)
sys.exit(1)
Ideally, I'd like the error_message to be accessible in AJAX's "responseText" (which is now empty "").
So far, I tried including a string (i.e. sys.exit(error_message)) and a JSON string (i.e. sys.exit(json.dumps(result))) in sys.exit(), but debugging in PyDev I learned that TypeError: an integer is required (got type str), and that this might be a bug. For this reason, I finally added the integer 1, with hope that the json.dump content would be passed via AJAX before the sys.exit.
UPDATE:
I found what was causing the error (a missing python package on the remote server's python install). However I'll keep the question open, because in principle I'm still interested to know how to ensure that the error message from Python (whether from my custom interruptScript() function, or directly from Python informing a package is not installed) gets properly pushed to the browser. I'm using cgitb.enable(), so the question is mostly, how to push the error message if AJAX is expecting JSON?

Handle Pyramid HTTP error messages in Javascript

I'm sure there must be an easy solution for this, but I have been unable to find it. I'm using Python Pyramid in my server and handling requests/responses with AngularJS in Javascript. I'm using the Pyramid HTTP errors to handle some cases, and I'm able to catch them using the Angular http.error() statement.
But this is where I'm stuck, I want to do something with the error messages but if I log the response I just see html code.
In Pyramid this is happening:
if exists:
# continue execution
else:
return HTTPNotFound("The object does not exist")
And in Angular:
$http.get(url)
.success(function(data) {
// do something
})
.error(function(data){
// do something with the error message
});
How would I get that specific error message from the HTTP error? (I'm using JSON as my renderer btw)
Your render is not considered if you do not return a value for renderer but return a response or exception - so do not expect json on the output unless your error handler supports that.
You can do something like this instead:
return HTTPUnprocessableEntity(body=json.dumps(yourobj/string))
And then you can retrieve the JSON in your error handler and use angular.fromJson() to read it.
Maybe a little late, but this might interest other users of Angular:
Simply call a modal to get a pop-up of the Pyramid HTML error:
function(error)
{ console.log("error is: " + error);
var modalInstance = $uibModal.open(
{
template: error,
size:'lg'
});
}
(do not forget to include $uibModal).
Very useful for debugging purposes, but not recommended for production.

JSONP web service with python

I'm writing an AJAX function that requests data from my JSON Python webservice. My AJAX request looks like:
url = "http://localhost:8001/blah"
$.ajax({
url: url,
type: 'get',
dataType: 'jsonp',
success: function(data) {
console.log('hi')
}
});
For now, my python web service has a function that handles the request to '/blah' that has the following return statement:
return json.dumps({'a':1, 'b':2 })
My AJAX function is not successfully retrieving a response from my Python Webservice, but I don't get any errors in Firebug. What is my webservice or javascript doing wrong?
What happens when you use Jquery's JSONP datatype, is that a callback function name is sent as a GET param as part of your URL, so you're actually querying something like "http://localhost:8001/blah?callback=json125348274839".
Your response from your web server should look like this:
return "%s({'a':1, 'b':2 })" % _GET_PARAMS('callback')
so your web server will return somthing like "json125348274839({'a':1, 'b':2 })"
Hope that helps!
Zack got it. My javascript was correct. I changed my python return statement to the following:
callback = request.args.get('callback')
return '{0}({1})'.format(callback, {'a':1, 'b':2})
Turn on (or add) logging in your Python web service. Inspect your web server logs... are you receiving the request from your javascript client? Is your web server logging that it is returning a response?
Possibly it depends on the framework that you are using. Is it as simple as returning a string from the handler function? Perhaps the handler is supposed to return a list of strings and, because it is not getting a list, it is returning nothing to the client. Look in your web server logs for errors.
You forgot closing quote and semicolon in the first line =)
If it is not helps, check following:
List item
What are you using as python service? Django, flask, something else? Maybe you can provide provide python code?
Also, look at the 'Net' Firebug's tab and, after ensure that request to 'url' is actually handled (not returned 404 or 50x codes, but 200), check 'Response' sub-tab.
Hope this helps!

Problem with POST request from APE server module

I use Ajax Push Engine as push engine and Django for main site. I wrote the server
module which must send the request to my Django-based application when
new user join the channel by using Http module. My Django-based project runs on the local
machine on local.jjjbbb.org.
Ape.addEvent("join", function(user, channel) {
var request = new Http('http://local.jjjbbb.org/test-
this/'); // this is a my test url
request.set('method', 'POST');
request.writeData('test-message', 'Hello APE!');
request.getContent( function(result) {
Ape.log(result); // this code never work
});
});
But this code doesn't work, request doesn't receive. When I change url
to anything else (like www.google.com or localhost for example) all
works correctly and I have a result. But when I try to send request to
my application request doesn't work. This problem is only when I try
to send request from server side, when I use jQuery for sending from
client side all works correctly.
Why I cannot send POST request from server side to my domain?
Sorry, I already found my problem. APE works fine, it was a little trouble with CSRF protection in Django.

Categories

Resources