How to read data from SOAP message with large XOP attachment using Spring WS and Axiom - attachment

I'm trying to build a web-service, which will receive large files and save them with the name specified in SOAP message.
Here is an example request message
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="http://mywebservice.com.ua/bait/schemas" xmlns:xm="http://www.w3.org/2005/05/xmlmime">
<soapenv:Header/>
<soapenv:Body>
<sch:SubmitProjectFileRequest>
<sch:ProjectName>MyADProject.xml</sch:ProjectName>
<sch:ProjectFile xm:contentType="text/text">cid:710420383131</sch:ProjectFile>
</sch:SubmitProjectFileRequest>
</soapenv:Body>
</soapenv:Envelope>
I've build some stuff already: I can receive large XOP files without OutOfMemoryError.
The problem is that I can't access ProjectName node of the request, as any attempts to get it lead to inlining of an attachment into request. And that itself leads to OutOfMemoryError
Here is the code which I currently use for that purpose
#PayloadRoot(localPart = SUBMIT_PROJECT_FILE_REQUEST, namespace = NAMESPACE_URI)
public void handleSubmitProjectFileRequest(SoapMessage message) throws Exception {
String projectName = getProjectName(message.getDocument());
Attachment attachment = message.getAttachments().next();
projectFileService.storeProjectFile(projectName, attachment.getDataHandler());
}
private String getProjectName(Document xml) throws XPathExpressionException {
String prefix = xml.lookupPrefix(NAMESPACE_URI);
NodeList names = xml.getElementsByTagName(String.format("%s:%s", prefix, "ProjectName"));
String projectName = names.item(0).getTextContent();
return projectName;
}
Could anyone help me to extract both large XOP attachment and ProjectName node content using Spring WS and Axiom?
Thanks in advance

From what I read (Sorry could only post 2 links total):
http://www.java.net/node/690763
markmail.org/message/utd5ineljlvvugse
And by the detailed definition of the MTOM method:
www.crosschecknet.com/intro_to_mtom.php
Although optimized for transport, the base64 encoded data that you attach to your message will still be unmarshalled and put back into the soap message before a basic handler (Like SOAPHandler for example) gets ahold of it. This seems to be a limitation of this methodology.
Using the technologies you mentioned puts you on the right track for a solution (compared to those of us who were cornered into using basic SOAPMessage and SOAPHandlers). If you use some of the specialized objects in AXIOM and spring, you should be able to accomplish this. CHeck this article out here: http://forum.springsource.org/archive/index.php/t-48343.html
Thanks,
KK

Related

cherrypy (python httpserver), how to post xml file in body

I want cherrypy to return a xml file in response body in post.
In POST(self), I read a xml file and modify some of the attributes and do these things:
cherrypy.response.headers['Content-Type'] = 'application/soap+xml;charset=UTF-8'
cherrypy.response.headers['Content-Length'] = len(data)
cherrypy.response.body = data
cherrypy.log("response body is: %s" % cherrypy.response.body)
When the client calls, it won't get the body.
curl waits for few seconds and returns this:
curl: (18) transfer closed with 4018 bytes remaining to read
Not sure if I am doing the right thing to send the data back to the client.
I took wireshark trace and I am not seeing any data getting sent out from the server.
Can someone please suggest?
I think I have made this work. Earlier I was calling another function to set above mentioned values. Once I moved them in POST function, things started working. I am not sure what difference do it make. Now, I am setting them this way:
cherrypy.response.headers['Content-Type'] = 'application/soap+xml;charset=UTF-8'
cherrypy.response.headers['Content-Length'] = len(data)
return data

Asp.net MVC web api Response.CreateResponse sending odd content

Am currently communicating to a Mobile device using Windows Compact Framework 3.5. The message sent to the device is built is as thus,
HttpResponseMessage result;
var response = Encoding.UTF8.GetBytes("<?xml version=\"1.0\" encoding=\"windows-1252\"?><message type=\"response\"><header><datetime>2013-04-03T09:49:35</datetime><sender version=\"1.1.4.1138\"><userid>Connect Server</userid></sender><commandlist><module>ADMIN</module><command1>VALIDATE</command1></commandlist><result type=\"ok\"/></header></message>");
result = Request.CreateResponse(HttpStatusCode.OK, response);
The device then retrieves the message and then uses
Encoding.UTF8.GetString(responseContent);
After decoding the message is:
<base64Binary xmlns="http://schemas.microsoft.com/2003/10/Serialization/">PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0id2luZG93cy0xMjUyIj8+PG1lc3NhZ2UgdHlwZT0icmVzcG9uc2UiPjxoZWFkZXI+PGRhdGV0aW1lPjIwMTMtMDQtMDNUMDk6NDk6MzU8L2RhdGV0aW1lPjxzZW5kZXIgdmVyc2lvbj0iMS4xLjQuMTEzOCI+PHVzZXJpZD5Db25uZWN0IFNlcnZlcjwvdXNlcmlkPjwvc2VuZGVyPjxjb21tYW5kbGlzdD48bW9kdWxlPkFETUlOPC9tb2R1bGU+PGNvbW1hbmQxPlZBTElEQVRFPC9jb21tYW5kMT48L2NvbW1hbmRsaXN0PjxyZXN1bHQgdHlwZT0ib2siLz48L2hlYWRlcj48L21lc3NhZ2U+</base64Binary>
Tried decoding the message on the server before sending it off and it's fine. Unsure what could be going wrong.
Any help would be greatly appreciated.
Request.CreateResponse() uses ObjectContent. For this scenario, you don't want that. You should use either StringContent or StreamContent to return the XML. See this question for details https://stackoverflow.com/a/15372410/6819
You are encoding your XML as binary. You are then returning a byte array. Then your client requests XML in the Accept: application/xml header. The Web API serializes the binary into XML. That's what you're seeing.
Just return the XML as a string and you should have no problems, unless you've tried that already?
See here for similar question.

Invoke WCF Data Services Service Operation from iOS

How do I invoke a Service Operation in WCF from iOS?
I have a Service Operation defined in my WCF Data Service (tied to a stored procedure in my DB schema) that I need to invoke from iOS. Say I've got the following declaration in my .svc.cs file:
[WebInvoke(RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public IQueryable<Foo> GetFoos(int param1, DateTime param2, string param3)
{
return CurrentDataSource.GetFoos(param1, param2, param3).AsQueryable();
}
And I've got it set up with the proper rights in InitializeService:
config.SetServiceOperationAccessRule("GetFoos", ServiceOperationRights.AllRead);
When I try to invoke this via HTTP POST from iOS, I get back an error wrapped in JSON:
Bad Request - Error in query syntax.
It seems like it doesn't like how I'm passing my parameters. I'm passing them JSON-encoded (using NSJSONSerialization to turn an NSDictionary into a JSON string) in the request body of a POST request. The same method works on another web service (.svc) not connected to WCF that has operations annotated the same way.
An answer to another question of mine in a similar vein suggests that data formats can be negotiated between client and server, and I've read that dates are a pain to format, so maybe it's my DateTime parameter that's a problem. But I've tried both the JSON format (\/Date(836438400000)\/ and /Date(836438400000)/) and the JSON Light format (1996-07-16T00:00:00) to no avail.
So my question is this: what is the proper way to invoke this operation? If I need to have my app tell the server what format to expect, how do I do that?
Update: I tried using the format datetime'1996-07-16T00:00:00' as mentioned in this question. Same error.
Update 2: The MSDN page for Service Operations seems to suggest that nothing besides Method = "POST" is supported when annotating the WebInvoke for a Service Operation. I tried removing everything from what is quoted in the above code and setting the method to POST. Same error.
Update 3: On Pawel's suggestion, I made a new Service Operation on my Data Service just like this:
[WebInvoke(Method = "POST")]
public IQueryable<string> GetFoos()
{
List<string> foos = new List<string>();
foos.Add("bar");
return foos.AsQueryable();
}
I was able to make it work in Fiddler's Composer pane by setting the method to POST, adding accept:application/json;charset=utf-8 and Content-Length:0 to the headers. Then I added a single int parameter to the operation (called param1). I set the body of my request in Fiddler to {"param1":"1"} and ran it (and Fiddler automatically updated my content-length header), and got the same error. I changed the type of my parameter to string and ran my request again and it worked. So my problem seems to be non-string types.
You need to send parameters in the Url and not in the request body.

Bad Request in SOAPUI

I am attempting to consume a web service using Delphi 2010 and Indy. To establish a usable SOAP stream to compare to the one created by my program, I am testing in SOAPUI. I am using a SOAP stream provided by the web service provider which also matches the SOAP stream specified in the WSDL file. I am getting an HTTP 400 (bad request) error from the service.
From what I can find online, it appears that receiving an HTTP 400 error indicates that your SOAP request is malformed and can not be read by the web service. I have tested my SOAP stream using XMLPad and the XML seems to be well formed. I suppose this may mean that something does not match its schema requirement. I will first check the schema description for the password in case that is expected to not be sent as plain text. What else should I be checking to eliminate an HTTP 400 error?
Here is my request (less username and password) in case it helps:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:xop="http://www.w3.org/2004/08/xop/include"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://wwww3.org/2001/XMLSchema-instance">
<soap:Header>
<wsa:Action>http://edd.ca.gov/SendTransmission</wsa:Action>
<wsa:MessageID>urn:uuid:5aa788dc-86e1-448b-b085-2d2743cf9f26</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address>
</wsa:ReplyTo>
<wsa:To>http://fsettestversion.edd.ca.gov/fsetproxy/fsetservice.asmx</wsa:To>
<wsse:Security soap:mustUnderstand="1">
<wsse:UsernameToken wsu:Id="UsernameToken">
<wsse:Username>#USERNAME#</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">#PASSWORD#/wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">O5QWht1bslLCX6KnlEypAA==</wsse:Nonce>
<wsu:Created>2012-02-29T22:32:38.250Z</wsu:Created>
</wsse:UsernameToken>
<wsu:Timestamp wsu:Id="Timestamp-805a7373-335c-43b6-ba21-6596c4848dbf">
<wsu:Created>2012-02-22T15:41:42Z</wsu:Created>
<wsu:Expires>2012-02-22T15:46:42Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
</soap:Header>
<soap:Body>
<SendTransmission xmlns="http://edd.ca.gov/">
<SendTransmissionRequest xmlns="http://www.irs.gov/a2a/mef/MeFTransmitterServiceWse.xsd">
<TransmissionDataList>
<Count>1</Count>
<TransmissionData>
<TransmissionId>123456789</TransmissionId>
<ElectronicPostmark>2012-02-22T07:41:42.2502206-08:00</ElectronicPostmark>
</TransmissionData>
</TransmissionDataList>
</SendTransmissionRequest>
<fileBytes>
<xop:Include href="cid:1.634654933022658454#example.org"/>
</fileBytes>
</SendTransmission>
</soap:Body>
</soap:Envelope>
There may be something else, but at the moment, I am suspicious of the wsse:UsernameToken. I downloaded the document at http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0.pdf and read it last night. It's written in fairly plain language and I feel like I understand what it is saying but it leaves me with a smaller question than the one I asked originally. This document proposes that you can use a plain text password in this format:
<S11:Envelope xmlns:S11="..." xmlns:wsse="...">
<S11:Header>
...
<wsse:Security>
<wsse:UsernameToken>
<wsse:Username>Zoe</wsse:Username>
<wsse:Password>IloveDogs</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
...
</S11:Header>
...
</S11:Envelope>
Or you can use a password digest. It defines a password digest like this:
Password_Digest = Base64 ( SHA-1 ( nonce + created + password ) )
According to the reference, the format for a password digest would look like this:
<S11:Envelope xmlns:S11="..." xmlns:wsse="..." xmlns:wsu= "...">
<S11:Header>
...
<wsse:Security>
<wsse:UsernameToken>
<wsse:Username>NNK</wsse:Username>
<wsse:Password Type="...#PasswordDigest">
weYI3nXd8LjMNVksCKFV8t3rgHh3Rw==
</wsse:Password>
<wsse:Nonce>WScqanjCEAC4mQoBE07sAQ==</wsse:Nonce>
<wsu:Created>2003-07-16T01:24:32Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
...
</S11:Header>
...
</S11:Envelope>
This is not the format used in the example provided by the web service publisher. The plain text version in the reference does not use a nonce. The example message uses a nonce but calls for a plain text password. It appears to me that the use of a nonce without a password digest does not add any security to the message. It could be any random string of characters if there is no agreement for how it is to be created. Am I missing the point?
I know this must seem like a tedious undertaking, but I am hoping that by providing this here, maybe we can provide a little help to the next person coming along.
I too have come across this issue. The web service publisher (edd.ca.gov) responded by stating that the " value is required by the SOAP 1.2 standards" yet I find no valid support for that. It looks like we both are heading down the same path (FSET) and maybe we should team up and work together, two heads are better than one. I have found many mistakes within the example code and I too have yet get it to work.

Microsoft Translator API answers 500 internal server error

I'm trying to use Microsoft's Translator API in my Rails app. Unfortunately and mostly unexpected, the server answers always with an internal server error. I also tried it manually with Poster[1] and I get the same results.
In more detail, what am I doing? I'm creating an XML string which goes into the body of the request. I used the C# Example of the API documentation. Well, and then I'm just invoking the RESTservice.
My code looks like this:
xmlns1 = "http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2"
xmlns2 = "http://schemas.microsoft.com/2003/10/Serialization/Arrays"
xml_builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
xml.TranslateArrayRequest("xmlns:ms" => xmlns1, "xmlns:arr" => xmlns2) {
xml.AppId token #using temporary token instead of appId
xml.From source
xml.To target
xml.Options {
xml["ms"].ContentType {
xml.text "text/html"
}
}
xml.Texts {
translate.each do |key,val|
xml["arr"].string {
xml.text CGI::unescape(val)
}
end
}
}
end
headers = {
'Content-Type' => 'text/xml'
}
uri = URI.parse(##msTranslatorBase + "/TranslateArray" + "?appId=" + token)
req = Net::HTTP::Post.new(uri.path, headers)
req.body = xml_builder.to_xml
response = Net::HTTP.start(uri.host, uri.port) { |http| http.request(req) }
# [...]
The xml_builder produces something like the following XML. Differently to the example from the API page, I'm defining two namespaces instead of referencing them on the certain tags (mainly because I wanted to reduces the overhead) -- but this doesn't seem to be a problem, when I do it like the docu-example I also get an internal server error.
<?xml version="1.0" encoding="UTF-8"?>
<TranslateArrayRequest xmlns:ms="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2" xmlns:arr="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<AppId>TX83NVx0MmIxxCzHjPwo2_HgYN7lmWIBqyjruYm7YzCpwnkZL5wtS5oucxqlEFKw9</AppId>
<From>de</From>
<To>en</To>
<Options>
<ms:ContentType>text/html</ms:ContentType>
</Options>
<Texts>
<arr:string>Bitte übersetze diesen Text.</arr:string>
<arr:string>Das hier muss auch noch übersetzt werden.</arr:string>
</Texts>
</TranslateArrayRequest>
Every time I request the service it answers with
#<Net::HTTPInternalServerError 500 The server encountered an error processing the request. Please see the server logs for more details.>
... except I do some unspecified things, like using GET instead of POST, then it answers with something like "method not allowed".
I thought it might be something wrong with the XML stuff, because I can request an AppIdToken and invoke the Translate method without problems. But to me, the XML looks just fine. The documentation states that there is a schema for the expected XML:
The request body is a xml string generated according to the schema specified at http:// api.microsofttranslator.com/v2/Http.svc/help
Unfortunately, I cannot find anything on that.
So now my question(s): Am I doing something wrong? Maybe someone experienced similar situations and can report on solutions or work-arounds?
[1] Poster FF plugin > addons.mozilla.org/en-US/firefox/addon/poster/
Well, after lot's of trial-and-error I think I made it. So in case someone has similar problems, here is how I fixed this:
Apparently, the API is kind of fussy with the incoming XML. But since there is no schema (or at least I couldn't find the one specified in the documentation) it's kind of hard to do it the right way: the ordering of the tags is crucial!
<TranslateArrayRequest>
<AppId/>
<From/>
<Options />
<Texts/>
<To/>
</TranslateArrayRequest>
When the XML has this ordering it works. Otherwise you'll only see the useless internal server error response. Furthermore, I read a couple of times that the API also breaks if the XML contains improper UTF-8. One can force untrusted UTF-8 (e.g. coming from a user form) this way:
ic = Iconv.new('UTF-8//IGNORE', 'UTF-8')
valid_string = ic.iconv(untrusted_string + ' ')[0..-2]

Resources