Kitware / tangelo

A simple, quick, powerful web framework
http:/tangelohub.org/tangelo/
Apache License 2.0
185 stars 35 forks source link

post method does not correctly receive requests when Content-type is application/json #495

Closed kaneplusplus closed 9 years ago

kaneplusplus commented 9 years ago

When a request is made to tangelo and, in the header I have "Content-type is application/json" the post function receives the request but doesn't include the data.

waxlamp commented 9 years ago

Hi @kaneplusplus! Thanks for the issue report. Would you be able to post a minimal example demonstrating this problem? It will make it easier to debug.

waxlamp commented 9 years ago
kaneplusplus commented 9 years ago

Sure, give me a couple of minutes.

kaneplusplus commented 9 years ago

I have a post_example.py files that looks like:

import tangelo
@tangelo.restful
def post(*arg, **kwargs):
  print arg
  print kwargs
  return {"resp": "ack"}

I have a tangelo.conf file that looks like:

hostname: 0.0.0.0
port: 8080

I start the server with:

tangelo -c tangelo.conf -r /Users/mike/projects/tangelo/

I ping the service from the command line with with:

curl -H "Content-Type: application/json" -X POST http://localhost:8080/post_example -d '{"text" : "ping"}'

and I correctly get the following back from curl:

{"resp": "ack"}

But from the tangelo window I see the following, which indicate both arg and kwargs are empty.

127.0.0.1 - - [19/Feb/2015:17:51:00] "POST /post_example HTTP/1.1" 411 734 "" "curl/7.37.1"
()      
{}
waxlamp commented 9 years ago

Thank you for the detailed example! I will take a close look tomorrow.

kaneplusplus commented 9 years ago

I should add then when I call the following from curl:

curl -X POST http://localhost:8080/post_example -d '{"text" : "ping"}'

the tangelo window reports:

127.0.0.1 - - [19/Feb/2015:17:59:23] "POST /post_example HTTP/1.1" 200 15 "" "curl/7.37.1"
()
{'{"text" : "ping"}': u''}

Thanks for looking into this! It may noteworthy that this is an issue on Microsoft's data market for people that may want to provide restful data services.

waxlamp commented 9 years ago

@kaneplusplus, if you add this line after print kwargs in your example service:

print tangelo.request_body().read()

you will see the data you posted (in the example that uses -H to set the content type) in the request body, rather than in kwargs. You can use the request_body() function to retrieve this data, parse it into JSON, and make use of it from there.

When you omit the -H option, as you demonstrated, then the data is passed as a query argument. Since you passed a string without an equals sign in it, the string is treated as the query argument, mapped to a default value of "".

This seems to be reasonable semantics to me (though I am not an expert on the lower level details of how HTTP requests are set up and answered). If you think things should be different, let's discuss it and consult the standards so we can get Tangelo doing the correct thing. And, more importantly, if this explanation doesn't allow you to get Tangelo to do what you want, we can discuss your particular task in more detail to figure out how to get it done as well.

Let me know, and thanks again for the issue report!

kaneplusplus commented 9 years ago

OK, I'll give it a try and report back. Thanks very much for the response!

kaneplusplus commented 9 years ago

This did it. One thing to note when a request is made with Content-Type: application/json the request is in the request body. When the header type is application/x-www-form-urlencoded the request body is in kwargs.keys()[0]. Could this be documented somewhere? I realize that what is being done is reasonable, if you don't specify you're sending json you shouldn't expect to see it in the request body, but it took a little while to figure out that the problem was actually my request, not how I served it.

waxlamp commented 9 years ago

I'm glad we were able to solve your problem.

I will open an issue to put the discussion of content types, query arguments, and request bodies in the documentation.

Thanks!