How to POST form parameters? - jetty

I'm trying to send HTTP POST form parameters. I know the Content-Type has to be application/x-www-form-urlencoded and parameters must be encoded in the request body.
In version 9.0.6, Jetty seems to have done this automatically:
The POST request is sent with the application/x-www-form-urlencoded content type, and POST parameter values are automatically URL-encoded.
But in version 9.4.19, I see the documentation has changed and now it says:
The POST parameter values added via the param() method are automatically URL-encoded.
Sure enough, invoking:
httpClient.POST("http://example.com/entity/1")
.param("p", "value")
.send();
Now treats the parameters as URL query parameters. Nothing gets added to the request body.
What is the correct way to properly POST form parameters?

An HTML Form is body content on a POST request.
So that means you have to use the org.eclipse.jetty.client.api.ContentProvider, and Request.content(ContentProvider) API.
There's 2 ContentProvider implementations for working with HTML Forms.
FormContentProvider
class: org.eclipse.jetty.client.util.FormContentProvider
content-type: application/x-www-form-urlencoded
import org.eclipse.jetty.client.util.FormContentProvider;
import org.eclipse.jetty.util.Fields;
Fields fields = new Fields();
fields.put("fruit", "apple");
httpClient.POST("http://example.com/entity/1")
.content(new FormContentProvider(fields))
.send();
MultiPartContentProvider
class: org.eclipse.jetty.client.util.MultiPartContentProvider
content-type: multipart/form-data
import org.eclipse.jetty.client.util.MultiPartContentProvider;
import org.eclipse.jetty.client.util.StringContentProvider;
MultiPartContentProvider multiPart = new MultiPartContentProvider();
multiPart.addFieldPart("fruit", new StringContentProvider("apple"), null);
multiPart.close();
httpClient.POST("http://example.com/entity/1")
.content(multiPart)
.send();

Related

Tastypie: ending empty POST with no request, and to a path parameter URL

I need to create two POST request with url:
/create with raw body: {some JSON}, I'm able to do this,
with a obj_create method provided by tastypie.
/create/4, I'm stuck at this, which has the same endpoint but with a path parameter and no body.
I've the following questions for which I could not figure out answers from documentation/stack overflow:
Is is possible to send POST request in tastypie without a body
this post request does not goes to obj_create, what is the corresponding method in tastypie to receive such post requests with path parameter and no body.
Thanks for the help.

Where do I define the Content-Type I want to receive in my Django API?

I have a Django app that only needs to receive requests with Content-Type: text/plain.
Is there an option for that when using the GenericViewSet?
I had to write my own Parser for plain text (in my case, the body contains json although the Content-Type is plain text, so I just had to override media_type) like this:
class PlainTextParser(JSONParser):
media_type = "text/plain"
and use it in my ViewSet like this:
parser_classes = [PlainTextParser]

Return data as json from odoo 9

I want get data in JSON format from odoo controllery.py
Example:
import openerp.http as http
from openerp.http import request
class MyController(http.Controller):
#http.route('/test_html', type="http", auth="public")
def some_html(self):
return "<h1>Test</h1>"
#Work fine when open http://localhost:8069/test.html
#http.route('/test_json', type="json", website=True, auth="public")
def some_json(self):
return [{"name": "Odoo", 'website': 'www.123.com'}]
How get data in json format, I want data from json read in other app with ajax.
Is it possible view json after open url http://localhost:8069/test_json ???
The important part is to define the contentType of your request properly.
import json
#http.route('/test_json', type="json", auth="public")
def some_json(self):
return json.dumps({"name": "Odoo", 'website': 'www.123.com'})
In your client using javascript you can request the json like this.
$.ajax({
type: "POST",
url: "/test_json",
async: false,
data: JSON.stringify({}),
contentType: "application/json",
complete: function (data) {
console.log(data);
}
});
Or using requests in python
import requests,json
res = requests.post("http://localhost:8069/test_json",data=json.dumps({}),headers={"Content-Type":"application/json"})
To access the response body
body = res.text
As to whether you can simply open a browser and view the json. No, not by default.
Here is what I get
Bad Request
<function some_json at 0x7f48386ceb90>, /test_json: Function declared as capable of handling request of type 'json' but called with a request of type 'http'
You could probably do something pretty fancy with a controller if you really wanted to be able to view it in a browser as well as make json requests. I would post a second question though.
Your controller endpoint looks ok and should function correctly, so I guess your main question is how to test it.
Once you declare that the endpoint type is json, Odoo will check that the request content type header is in fact JSON, so in order to test it your requests will need to have Content-Type: application/json header set. This is a bit difficult using a regular browser, unless you edit the request headers before seinding or call your JSON endpoint from JavaScript via Ajax.
Alternatively, you can test your API from command line using a tool like curl:
curl 'http://localhost:8069/test_json' -H 'Content-Type: application/json' --data "{}"
--data "{}" here indicates an empty JSON structure which will be passed to your endpoint as request parameters.
Please note that you might also have to pass an additional header containing your session_id cookie if you are using more than one Odoo database.

Missing form data in request

I have following code
class MyClass(restful.Resource):
def get(self):
headers = {'Content-Type': 'text/html'}
return make_response(render_template('myfile.html'),200,headers)
def post(self):
session['CONSUMER_KEY']=request.form.get('consumer_key')
session['CONSUMER_SECRET']=request.form.get('consumer_secret')
render_template('myfile.html')
api.add_resource(MyClass,"/mag/",endpoint="mag")
I have written following test:
def mytest(self):
content_type={"Content-Type": "application / x - www - form - urlencoded","Content-Disposition": "form-data"}
response = self.client.post(
api.url_for(MyClass), data = json.dumps({'consumer_key':'testconsumerkey',
'consumer_secret':'testconsumersecret'}),
headers=content_type
)
The issue is form data is blank and thats the values are not getting set in session. When i debug i see that request.data is populated but request.form is an empty dictionary. Can someone suggest how I can send form data in a post request from a test
EDIT: Environment details
Python 2.7, Flask web framework, self.client is . I am using flask.ext.testing
You seem to be confused as to what the expected format for the post body should be. Should it be JSON data (which is what you send in the test case), or should it be in the application/x-www-form-urlencoded format (which is what you claim to send in the test case, and what the endpoint will read)?
If you wish to receive JSON data, you'll need to change the endpoint to read the data from request.get_json(). You'll also need to use application/json as the Content-Type header in the test case.
If you wish to receive urlencoded post data, then just simplify the test case by removing the Content-Type header and the json.dumps. Just pass the data dict to the data argument.

How to send data as key - value pairs instead of string via POST using XHR

I'm creating two POST calls. One using a django form and one using angular js via a resource xhr.
The angular setup looks like this:
myModule.factory('gridData', function($resource) {
//define resource class
var root = {{ root.pk }};
var csrf = '{{ csrf_token }}';
return $resource('{% url getJSON4SlickGrid root.pk %}:wpID/', {wpID:'#id'},{
get: {method:'GET', params:{}, isArray:true},
update:{method:'POST', headers: {'X-CSRFToken' : csrf }}
});
});
With creating an xhr post request as such:
item.$update();
This post request is send to the server as expected, but when I want to access the QueryDict I cannot access the data passed using:
name = request.POST.get('name', None)
name is always None like this.
The issue behind this is that the QueryDict object is getting parsed quite strange.
print request.POST
<QueryDict: {u'{"name":"name update","schedule":0"}':[u'']}>
Whereas I would have expected this result, which I got when I send the data via a "normal" Post request:
<QueryDict: {u'name': [u'name update'], u'schedule': [u'0']}>
So it seems to be that Django receives something in the POST request which instructs Django to parse the parameters into one string. Any idea how to circumvent this?
Update:
I found this discussion where they say that the issue is if you provide any content type other than MULTIPART_CONTENT the parameters will be parsed into one string. I checked the content-type send with the POST request and it is really set to 'CONTENT_TYPE': 'application/json;charset=UTF-8'. Thus this is likely the issue. Therefore my question is: How can I set the CONTENT_TYPE for a xhr post request created using angular.js resources to MULTIPART_CONTENT?
you could either:
fiddle with the client to send data instead of json
use json.loads(request.raw_post_data).get('name', None) (django < 1.4)
use json.loads(request.body).get('name', None) (django >= 1.4)
The Angular documentation talks about transforming requests and responses
To override these transformation locally, specify transform functions as transformRequest and/or transformResponse properties of the config object. To globally override the default transforms, override the $httpProvider.defaults.transformRequest and $httpProvider.defaults.transformResponse properties of the $httpProvider.
you can find an example here as was previously pointed at.