How to post complex type to WCF using Python's requests? - python

I am trying to query a WCF web service using Python's request package.
I created a very simple web service in WCF, following the default VS template:
[ServiceContract]
public interface IHWService
{
[OperationContract]
[WebInvoke(Method="GET", UriTemplate="SayHello", ResponseFormat=WebMessageFormat.Json)]
string SayHello();
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "GetData", ResponseFormat = WebMessageFormat.Json)]
string GetData(int value);
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "GetData2", BodyStyle=WebMessageBodyStyle.Bare, RequestFormat=WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
CompositeType GetDataUsingDataContract(CompositeType composite);
// TODO: Add your service operations here
}
From Python, I manage to call the first two and get the data back easily.
However, I am trying to call the third one, which adds the concept of complex types.
This is my python code:
import requests as req
import json
wsAddr="http://localhost:58356/HWService.svc"
methodPath="/GetData2"
cType={'BoolValue':"true", 'StringValue':'Hello world'}
headers = {'content-type': 'application/json'}
result=req.post(wsAddr+methodPath,params=json.dumps({'composite':json.dumps(cType)}),headers=headers)
But it does not work, i.e., if I put a breakdown in VS in the GetDataUsingDataContract method, I see that the composite argument is null. I think this comes from a problem in parsing, but I can't quite see what's wrong.
Do you see an obvious mistake there?
Do you know how I can debug inside the parsing mechanism?
EDIT:
Here is the complex type definition:
[DataContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = "Hello ";
[DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}
[DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
}

You need to send JSON in the POST body, but you are attaching it to the query parameters instead.
Use data instead, and only encode the outer structure:
result=req.post(wsAddr+methodPath,
data=json.dumps({'composite': cType}),
headers=headers)
If you encoded cType, you'd send a JSON-encoded string containing another JSON-encoded string, which in turn contains your cType dictionary.

Related

how to get json object from a webservice

i have the below posted json response.as shown below in json section, the parametersobject is emitted in this line (this is an angular application)
this._FromInsToSiteDataService.emitOnSubmitButtonClikedBroadcast(parameters)
and it is received in
this.subscriptionBroadcastEmitterOnSubmitButtonClicked = this._FromInsecticidesToSiteMapDataService.getBroascastEmitterOnSubmitButtonClicked().subscribe((response:Object)=>{
response['siteGeometry'] = this.selectedSite.geometry
console.log("response: ", response)
this.sSProvider.startWebServiceFor(response)
});
in the latter code i want to pass the response which is in json format to the webservice and receive it as show in the websrvicepostedbelow`
when i run the code, i expected to see the contents of the json object which is
{
"dist1": d1,
"dist2": d2,
"date1": date1,
"date2": date2,
"ingredient": activeIngredient
}
but i get NONE
please let me know how can i correctly get a json object from a webservice
json
private submit() {
let parameters = {
"dist1": d1,
"dist2": d2,
"date1": date1,
"date2": date2,
"ingredient": activeIngredient
}
this._FromInsToSiteDataService.emitOnSubmitButtonClikedBroadcast(parameters)
receiving the json object
this.subscriptionBroadcastEmitterOnSubmitButtonClicked = this._FromInsecticidesToSiteMapDataService.getBroascastEmitterOnSubmitButtonClicked().subscribe((response:Object)=>{
response['siteGeometry'] = this.selectedSite.geometry
console.log("response: ", response)
this.sSProvider.startWebServiceFor(response)
});
webservice:
#app.route("/insSpecifications/<parameters>", methods=['GET'] )
def insSpecifications(parameters):
# print(request.json())
print(request.get_json())//returns NONE
return "OK"
Intro
There are two parts to your question -
Making a request from JS
Creating a Flask API to handle the request
Both these have extensively answered on SO hence I will only summarize it here, please follow the links for more information
Answer
REST Method:
When sending JSON data from the front end to the backend, you need to make a POST request or PUT depending on the need. Please read up on REST API concepts to understand the methods and purposes.
https://www.w3schools.in/restful-web-services/rest-methods/
Making a request
Depending on which library you use in the front end, the request might look different, but essentially you need to send a request with JSON in the body and HEADERS set appropriately i.e. Content-Type: application/json
Using FETCH this can be achieved by (auto-generated from postman)
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
var raw = JSON.stringify({
"username": "Sample1",
"email": "test2#test.com"
});
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
fetch("localhost:5000/sample", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
But most libraries would have wrappers around this, please look into making a POST request for your respective JS library
Creating Flask API
Finally, you need a Flask API to consume this request. Assuming it's a POST request. You need to create a route with method as POST and get the JSON data using get_json() : https://stackoverflow.com/a/20001283/5236575
So once the HEADERS are correctly set and a post request is made, your code should work fine by changing GET to POST
Note: The parameters field is captured correctly hence I'm leaving it as is, but that is not where your JSON body comes from
#app.route("/insSpecifications/<parameters>", methods=['POST'] )
def insSpecifications(parameters):
# print(request.json())
print(request.get_json())
return "OK"
Testing
You can test your API using Postman or any other API testing tool to see how the API behaves and validate if the issue you have is in the API or in the front-end code.

How can I upload a file with JSON data to django rest api?

I am using angular as the front-end scope and Django Rest as the back-end. Now I am facing a situation where I want to create a Model. The structure of a model is really complex in nature, I could use some other simple way outs but using JSON and passing the files with that can really simplify the logic and also make process really efficient.
I am have been trying a lot but none of the ways seem to work.
Can some someone help me with a standard way or tell me even it is possible or not.
This the structure of my Typescript which I want to upload.
import { v4 as uuid4 } from 'uuid';
export interface CompleteReport{
title: string;
description: string;
author: string;
article_upload_images: Array<uuid4>,
presentation_upload_images: Array<uuid4>,
report_article: ReportArticle,
report_image: ReportImage,
report_podcast: ReportPodcast,
report_presentation: ReportPresentation,
report_video: ReportVideo,
}
export interface ReportArticle{
file: File;
body: string;
}
export interface ReportPodcast{
file: any;
}
export interface ReportVideo{
file: Array<File>;
}
export interface ReportImage{
file: File;
body: string;
}
export interface ReportPresentation{
body: string;
}
export interface UploadImage{
file: File;
}
I don't see how you wanna send data, but if you wanna send data with multipart/data-form, I think you should make small changes to your report structure.
JSON doesn't supports binary. So, you can't put files on it. You need to split file and report JSON.
(async () => {
let formData = new FormData();
// here's how to send file on multipart/data-form via fetch
let reportFile = document.querySelector('#file');
formData.append("file", imagefile.files[0]);
// here's your report json
let report = {
...
};
formData.append("report", JSON.stringify(report));
// send request and upload
let response = await fetch(url, {
method: 'POST',
body: formData
});
// do something with response
let responseText = await response.text();
console.log(responseText)
})();
And I see UUID on your frontend code, I think it's better to put that kinda stuff on backend to prevent manipulated request. I think it's better to put complicated stuff, and any server data related on your backend. Just my opinion.

How can I make a Swift HTTP POST hit a Flask server?

I am trying to post some data to a Flask server, whose code is the following:
#app.route('/tasks', methods=['POST'])
def create_task():
if not request.json or not 'title' in request.json:
abort(400)
task = {
'title': request.json['title'],
'description': request.json.get('description', ""),
}
return jsonify({'task' : task}), 201
When I run this, it works fine, and I can make POST requests successfully using curl, with the expected behavior on the back end above and the expected return value in command line. I want to make a post to this server using Swift, however, and am having trouble with that. I have followed the tutorial detailing this behavior here. In particular, I put the code in my AppDelegate.swift so it is executed as soon as the app launches. The full code is in the link posted, but for reference I am also posting it below:
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
var request = NSMutableURLRequest(URL: NSURL(string: "http://localhost:4567/login"))
var session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
var params = ["username":"jameson", "password":"password"] as Dictionary<String, String>
var err: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(params, options: nil, error: &err)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
println("Response: \(response)")
var strData = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Body: \(strData)")
var err: NSError?
var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves, error: &err) as? NSDictionary
// Did the JSONObjectWithData constructor return an error? If so, log the error to the console
if(err != nil) {
println(err!.localizedDescription)
let jsonStr = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Error could not parse JSON: '\(jsonStr)'")
}
else {
// The JSONObjectWithData constructor didn't return an error. But, we should still
// check and make sure that json has a value using optional binding.
if let parseJSON = json {
// Okay, the parsedJSON is here, let's get the value for 'success' out of it
var success = parseJSON["success"] as? Int
println("Succes: \(success)")
}
else {
// Woa, okay the json object was nil, something went worng. Maybe the server isn't running?
let jsonStr = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Error could not parse JSON: \(jsonStr)")
}
}
})
task.resume()
return true
}
However, when I launch this app, I have the following logged in my xcode
Response: <NSHTTPURLResponse: 0x7fc4dae218a0> { URL: http://localhost:5000/task } { status code: 404, headers {
"Content-Length" = 26;
"Content-Type" = "application/json";
Date = "Tue, 07 Oct 2014 19:22:57 GMT";
Server = "Werkzeug/0.9.6 Python/2.7.5";
} }
Body: {
"error": "Not found"
}
Succes: nil
I've been working with this and tinkering with the input, it seems that the back end is fine, but I'm wondering what is wrong with this front end, unfortunately Swift documentation is for the moment fairly moot on this point and seems to be the only solution floating around for RESTful API calls at the moment.
Your flask route is '/tasks' and you're trying to post to http://localhost:5000/task. Is that a typo, or are you a victim of failure to pluralize?

AngularJS $http.post and Python CGI FieldStorage

I have a problem with $https.post parameters on AngularJS. I read that AngularJS is creating a JSON for the params.
So I applied the solution found here:
AngularJS - Any way for $http.post to send request parameters instead of JSON?
http://victorblog.com/2012/12/20/make-angularjs-http-service-behave-like-jquery-ajax/
But I still have problem with Python CGI.
[Edit]
Output of the console.log for objData:
Object {hostname: "test", id_venue: 20}
inspecting Request Payload:
action: "insert",
objData: { first: "test", second:"test2" }
[/Edit]
Website call
angular.module('myApp.services', [])
.service('MyService', ['$http', '$q', function ($http, $q) {
this.insert = function (objData){
console.log(objData);
var deferred = $q.defer();
$http.post('api/api.py', { action: 'insert', objData: objData }).success(function (data, status, headers, config) {
deferred.resolve(data);
}).error(function (response) {
deferred.reject("error");
});
return deferred.promise;
};
}])
Server side code is made with Python and CGI. It is unfortunately a constraint that I cannot use web frameworks.
api/api.py
import cgi
params = cgi.FieldStorage()
print 'Content-type: application/json'
print
print params
print params['objData'].value
Inspective the request headers and response, and I have obviously a KeyError:
FieldStorage(None, None, [MiniFieldStorage('action', 'insert'), MiniFieldStorage('objData[first]', 'test'), MiniFieldStorage('objData[second]', 'test2')])
KeyError: 'objData'
Any solution on how to correctly read params on Python CGI FieldStorage.
Or any way to send them correctly with AngularJS?
With $http.get I don't have any problem.
Maybe one solution could be to handle POST request directly in Python without using cgi.FieldStorage.
As far as I can see, on the js side, objData seems to contain {"first": "test", "second": "test2"}.
As a 1st recommendation, I would recommend to make your life easier by using objData keys as POST keys by doing sth like this on the js client side (I'm assuming objData keys don't start with a underscore so that on the server side, you can make a difference between action and obj keys) :
params = {_action:'insert'};
_.extend(params, objData); // using underscore or lodash
[...]
$http.post('api/api.py', params)
The potential problem is that your insert function is quite generic, objData could be anything (so sth else than a js dictionnary), my second recommendation would be to first serialize your objData as a json string (you can find js libs for this), then send {action: 'insert', 'jsondata': json_string}.
On the python side, you will have to deserialize the json_string which is quite easy with json module.
Thus, you can both use a classical POST (<-> not full json encoded form data) and some json stuff.
HTH

Calling a python function using dojo/request

Firstly, I'm very new to the world of web development, so sorry if this question is overly simple. I'm trying to use python to handle AJAX requests. From reading the documentation it seems as though Dojo/request should be able to do this form me, however I've not found any examples to help get this working.
Assuming I've got a Python file (myFuncs.py) with some functions that return JSON data that I want to get from the server. For this call I'm interested in a particular function inside this file:
def sayhello():
return simplejson.dumps({'message':'Hello from python world'})
What is not clear to me is how to call this function using Dojo/request. The documentation suggests something like this:
require(["dojo/dom", "dojo/request", "dojo/json", "dojo/domReady!"],
function(dom, request, JSON){
// Results will be displayed in resultDiv
var resultDiv = dom.byId("resultDiv");
// Request the JSON data from the server
request.get("../myFuncs.py", {
// Parse data from JSON to a JavaScript object
handleAs: "json"
}).then(function(data){
// Display the data sent from the server
resultDiv.innerHTML = data.message
},
function(error){
// Display the error returned
resultDiv.innerHTML = error;
});
}
);
Is this even close to what I'm trying to achieve? I don't understand how to specify which function to call inside myFuncs.py?
What you could also do is to create a small jsonrpc server and use dojo to do a ajax call to that server and get the json data....
for python side you can follow this
jsonrpclib
for dojo you could try something like this..
<script>
require(['dojox/rpc/Service','dojox/rpc/JsonRPC'],
function(Service,JsonRpc)
{
function refreshContent(){
var methodParams = {
envelope: "JSON-RPC-2.0",
transport: "POST",
target: "/jsonrpc",
contentType: "application/json-rpc",
services:{}
};
methodParams.services['myfunction'] = { parameters: [] };
service = new Service(methodParams);
function getjson(){
dojo.xhrGet({
url: "/jsonrpc",
load : function(){
var data_list = [];
service.myfunction().then(
function(data){
dojo.forEach(data, function(dat){
data_list.push(dat);
});
console.log(data_list)
},
function(error) {
console.log(error);
}
);
}
});
}
getjson();
}
refreshContent();
});
});
</script>
I've used this approach with django where i am not creating a different server for the rpc calls but using django's url link to forward the call to my function.. But you can always create a small rpc server to do the same..

Categories