I'm developing an app on iOS requesting a web service with SOAP, and I have an issue with the datatype xsd:base64Binary (XML).
I build my SOAP envelope according to the expected fields from the web service. When the types are xsd:string or xsd:integer or any other simple type, I have no problem.
But when I try to add an xsd:base64Binary type to the SOAP envelope, the web service cannot receive the data correctly; it seems to be an encoding issue but I can't figure it out.
For example, the envelope looks like :
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema- to instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<hereTheMethod xmlns="http://heretheaddress/">
<aBase64>/9j/4AAQSkZJRgAB...
...RRQAUUUUAf/Z</aBase64>
<aBool>false</aBool>
<anInt>89</anInt>
<aDouble>0.0</aDouble>
<aString>Hello</aString>
</hereTheMethod>
</soap12:Body>
</soap12:Envelope>
Then the web service reads "false", "89", "0.0" or "Hello" without any problem. But with base64, I have something like "����JFIF��XExifMM*�i&..." as an encoding problem.
In Objective-C, I proceed like :
NSMutableString *envelopeText = [NSMutableString stringWithFormat:#"(header)\n"
"<aBase64>%#</aBase64>\n"
"(footer)", base64String];
// Some code...
NSData *envelope = [envelopeText dataUsingEncoding:NSUTF8StringEncoding];
// I launch the connection.
Lastly, when I build the base64 string, I take care to encode it in UTF-8 like :
NSString *base64String = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
Notice that the app also exists for Android (Java), and works with the same web service correctly.
Thanks for your help !
PS : Sorry for my English :/
try use CDATA for Base64 string like this:
<aBase64><![CDATA[%#]]></aBase64>\n
Related
I have applied for slideshare API and i got the API and the secret. All i have did is a simple GET request to slideshare which gives me results with the help of a tag.
This is my deluge script which i have tried to call the url using the API.
As per the documentation, i have got the unix time stamp and SHA1 hash.
param = Map();
param.put("api_key","XYZ");
param.put("ts","1565085930");
param.put("hash","xxxxxxxxxxxxxxxxxxxxxxxxx");
param.put("tag","cricket");
request = invokeurl
[
url :"https://www.slideshare.net/api/2/get_slideshows_by_tag"
type : GET
parameters: param
];
info request;
This is the response error i get:
<?xml version="1.0" encoding="UTF-8"?>
<SlideShareServiceError>
<Message ID="1">Failed API validation</Message>
</SlideShareServiceError>
Thank you.
Looks like the API does not work with a GET request. Try the same using a POST request and it should work. The same failed with POSTMAN and it worked only after the request type was changed to POST.
param = Map();
head = Map();
param.put("api_key","XXXXXXXX");
param.put("ts",1577955246);
param.put("hash","b3f3f803XXXXXXXXXXXXXXXX8be21d");
param.put("tag","cricket");
request = invokeurl
[
url :"https://www.slideshare.net/api/2/get_slideshows_by_tag"
type : POST
parameters: param
];
info request;
Response:
<?xml version="1.0" encoding="UTF-8"?>
<Tag>
<Name>cricket</Name>
<Count>0</Count>
</Tag>
I'm using AFNetworking's latest version in a project that connects to an API that returns both text responses and JSON responses so I use AFCompoundResponseSerializer to handle both response.
Request that return a JSON file are given to me as a NSString of the JSON response instead of an NSDictionnary that is normaly returned with AFJsonResponseSerializer, it looks like the AFCompoundResponseSerializer takes the response as plain text and doesn't send it to the AFJsonResponseSerializer
My code fore the CompoundSerializer is as follow:
sharedSessionManager = [[self alloc] initWithBaseURL:[NSURL URLWithString:baseURL]];
sharedSessionManager.responseSerializer=[AFCompoundResponseSerializer compoundSerializerWithResponseSerializers:#[[AFJSONResponseSerializer serializer], [AFHTTPResponseSerializer serializer]]];
I found out why this happened:
The server returned JSON as text/html so the serializer sended it directly to AFHTTPResponseSerializer which returned a string instead of parsing the JSON.
To fix this, just add text/html to the JSON serializer's acceptableContentTypes
This is similar to the question asked here, but that question was not exactly answered to what the problem is.
Customer.xml
<?xml version="1.0" encoding="UTF-8"?>
<wl:adapter name="Customer"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:wl="http://www.ibm.com/mfp/integration"
xmlns:http="http://www.ibm.com/mfp/integration/http">
<displayName>Customer</displayName>
<description>Customer</description>
<connectivity>
<connectionPolicy xsi:type="http:HTTPConnectionPolicyType">
<protocol>https</protocol>
<domain>kenatibm.cloudant.com</domain>
<port>443</port>
</connectionPolicy>
</connectivity>
<procedure name="addCustomer"> </procedure>
</wl:adapter>
Customer-impl.js
function addCustomer(param1) {
var input = {
method : 'PUT',
returnedContentType : 'json',
path : 'userInputRequired',
body : {
contentType: 'application/json',
content : param1
}
};
return WL.Server.invokeHttp(input);
}
The issue is that even though I have defined the method as a PUT, when testing using File Run As | Call MobileFirst Adapter the user interface only displays a GET method, there is no option for PUT.
So is the answer that the GET will actually do a PUT or is this a bug or is there a configuration parameter that I am missing?
I think you are confusing how you invoke/test the adapter, with what verb it uses on the back-end system it is calling. You are testing/invoking it using GET, but the adapter is then calling your backend system - http://kenatibm.cloudant.com/backendsystem - using PUT.
This is broadly the same explanation as Dave gave in your previous question.
In short, the answer is that the GET will actually do a PUT.
Parameters are passed to the adapter in a GET request and then the adapter constructs a PUT request to perform the actual procedure. In your code, you can see how the 'param1' is passed by the wizard to the function and then it is set to as the 'content' of the PUT request. It's definitely a little confusing.
I am attempting to follow the discussion here using Ruby and Savon. I am able to retrieve a session ID, but whenever I perform a request from the clients that require authentication (tracker), I receive an Authorization Failed error.
require 'Savon'
tracker_url = 'http://myserver/polarion/ws/services/TrackerWebService?wsdl'
session_url = 'http://myserver/polarion/ws/services/SessionWebService?wsdl'
# todo handle bad login credentials gracefully
session_client = Savon.client(wsdl: session_url)
response = session_client.call(:log_in, message: {user_name: 'lsimons', password: 'mypassword'})
session_id = response.header[:session_id]
puts "Session ID: #{session_id}"
tracker_client = Savon.client(wsdl: tracker_url, soap_header: {"session" => session_id}, headers: {"sessionID" => session_id})
puts "Requesting Workitem"
begin
tracker_client.call(:get_work_item_by_id, message: {project_id: 'myProject', workitem_id: 'myWorkitem'})
rescue
puts "Client call failed"
end
This code creates the following SOAP request for the tracker_client:
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ins0="http://ws.polarion.com/TrackerWebService-impl" xmlns:ins1="http://ws.polarion.com/types" xmlns:ins2="http://ws.polarion.com/TrackerWebService-types" xmlns:ins3="http://ws.polarion.com/ProjectWebService-types" xmlns:tns1="http://ws.polarion.com/TrackerWebService" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Header>
<session>2164640482421325916</session>
</env:Header>
<env:Body>
<tns1:getWorkItemById>
<ins0:projectId>myProject</ins0:projectId>
<ins0:workitemId>myWorkitem</ins0:workitemId>
</tns1:getWorkItemById>
</env:Body>
</env:Envelope>
However, in the forum discussion, the sessionID element occurs before the header. I didn't think this was possible with standard SOAP? Is there a way to achieve this with Savon or am I misinterpreting the forum discussion?
I faced the same problem following the same thread. This is how I made it work (by replicating the response headers of the log_in request):
tracker_client = Savon.client(
wsdl: tracker_url,
soap_header: {
"ns1:sessionID" => session_id,
:attributes! => {
"ns1:sessionID" => {
"env:actor" => "http://schemas.xmlsoap.org/soap/actor/next",
"env:mustUnderstand" => "0",
"xmlns:ns1" => "http://ws.polarion.com/session"
}
}
}
)
Old question but thought I can add some info to hopefully help somebody.
I am using lolsoap to talk to polarion. In the above resulting document, the sessionID element is wiped off any namespaces and attributes. Also the assessment is right that actor and mustUnderstand attributes seem irrelevant.
To add header properly though with all fluff, one needs to get the Nokogiri::XML::Node and dup it, then add it to the header of the doc. This is a bug in nokogiri/libxml2 that adding child elements can often break namespaces unless Node is cloned before adding [1].
In lolsoap it is done something like:
auth_header = login_response.nokogiri_doc.xpath("//*[local-name()='sessionID']")[0].dup
other_request.header.__node__ << auth_header
Please note the dup operation. header.__node__ is just the header Nokogiri::XML::Element of a random SOAP request.
The dup operation makes adding desired element into another one with all necessary namespaces and attributes properly defined.
I don't know if savon allows one to directly touch request XML but I guess it does. Thus HTH
[1] https://github.com/sparklemotion/nokogiri/issues/1200
After some changes in code and going through loads of links I was able to write the following code to call WCF service method with SOAP message. Now I get 400 Bad Request error, whatever I change (tried HttpWebRequest) I still get this error. Not sure what am I missing:
private string WebServiceCall()
{
WebRequest req = WebRequest.Create("http://klo239fu.mass.win.tf.com/WCFTestService/Service.svc");
req.ContentType = "application/soap+xml; charset=utf-8";
req.Method = "POST";
req.Headers.Add("SOAPAction", "http://tempuri.org/GetSimpleType");
using(Stream reqStream = req.GetRequestStream())
{
var bytes = Encoding.UTF8.GetBytes(txtFormattedSoap.Text);
reqStream.Write(bytes, 0, bytes.Length);
reqStream.Close();
}
var wr = (WebRespose)req.GetResponse();
var srd = new StreamReader(wr.GetResponseStream());
txtResponse.Text = srd.ReadToEnd();
}
SOAP Request (from SOAP client)
POST /WCFTestService/Service.svc HTTP/1.1
Host: klo239fu.mass.win.tf.com
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "wsf-test-service/IService/GetSimpleType"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetSimpleType xmlns="wsf-test-service">
<myvalue>int</myvalue>
</GetSimpleType>
</soap:Body>
</soap:Envelope>
Here is the SOAP request that is patterened to above soap enveope that I pass in my code:
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><GetSimpleType xmlns="wcf-test-service"><myvalue>1234</myvalue></GetSimpleType></soap:Body></soap:Envelope>
Earlier I had content type test/xml but code throwed "(415) Cannot process the message because the content type 'text/xml' was not the expected type.". So I changed the content type="application/soap+xml; charset=utf-8" but it fails with (400) Bad request.
I'm saw antoher post you did and I think that you and I are currently working on the same thing. Invoking web services dynamically. Loading WSDLs, using dynamic proxies and reflection and all that fun stuff.
I have a suggestion for this error that you see.
I have had a similar issue with one of the web services I used. Even if the Action in the header is supposted to be optional, it did manage to get my call to fail when I passed in the address (uri) as you have. When I passed in the name of the operation instead it worked. So try and change this:
req.Headers.Add("SOAPAction", "http://tempuri.org/GetSimpleType");
To the name of the method in the WSDL instead. That is the name of the System.ServiceModel.Description.OperationDescription. It is probably "GetSimpleType".
req.Headers.Add("SOAPAction", "GetSimpleType");