I've spent an evening on this.
Talking to a web service that REQUIRES SOAP 1.2 and some extras.
It complained if I did not send
Content-Type: application/soap+xml;
and even the (allegedly optional) 'action' param in the HTTP headers.
This CAN be done using the options array, but really it's a thing the wsclient service description should take care of.
Changes
- There is now a variation of the 'soap' endpoint called 'soap 1.2' available in the wsclient service settings. It's actually an alias for the normal wsclient_soap module, but it's a tiny bit more self-aware. The pluggable design of HOOK_wsclient_endpoint_types() was wonderful here!
- Primarily, it sets the
$options['soap_version'] = SOAP_1_2;
for you. This means that the PHP lib will send the appropriate headers. -
Additionally, it adds bonus configuration options to solve #1250914: Setting SOAP headers? !!
IF you choose 'soap 1.2' - you can now assign additional SOAP envelope Header values. This is available on a per-operation config. If needed, it's likely we should add global ones too, but I've not done that today.
This produces the required request like so
POST /Webservice/Boats HTTP/1.1 Host: example.co.nz:9010 Connection: Keep-Alive User-Agent: PHP-SOAP/5.3.18 Content-Type: application/soap+xml; charset=utf-8; action="http://example.org.nz/IBoats/CheckSailNumber" Content-Length: 534 <?xml version="1.0" encoding="UTF-8"?> <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://example.org.nz" xmlns:ns2="http://www.w3.org/2005/08/addressing"> <env:Header> <ns2:Action env:mustUnderstand="true">http://example.org.nz/IBoats/CheckSailNumber</ns2:Action> <ns2:To>http://example.co.nz:9010/Webservice/Boats</ns2:To> </env:Header> <env:Body> <ns1:CheckSailNumber> <ns1:BoatType>keelboat</ns1:BoatType> <ns1:SailNumber>234</ns1:SailNumber> </ns1:CheckSailNumber> </env:Body> </env:Envelope>
-
There should be NO IMPACT on any other methods not using SOAP 1.2, I've been careful about keeping the changes separate.
With that in mind, I've introduced a tiny bit of copy-paste to replicate exactly the design style that already is in the code. I didn't want to refactor it, so I (slightly) duplicated it.
I found it hard to discover a good reference for exactly what SOAP 1.2 is up to with this stuff, so there was an amount of black-box trial & error, but I finally got what works - for me.
It's hard to give anyone a test plan to try this out. The endpoint I worked against is not just private, it's a black-box and they would not even let us access the WSDL!
Patch forthcoming...
Comment | File | Size | Author |
---|---|---|---|
#41 | soap_headers-2065037-41.patch | 11.04 KB | dman |
#40 | soap_headers-2065037-40.patch | 10.92 KB | berenddeboer |
wsclient-soap_1_2-settings.png | 82.52 KB | dman |
Comments
Comment #1
dman CreditAttribution: dman commentedThis code should be pretty self-contained. A few lumps of stand-alone modification. (Lets see how drush iqs performs...)
Comment #2
dman CreditAttribution: dman commentedLooks like I must have missed something - making a new consumer from scratch using soap 1.2 fails to read the WSDL.
Creating it with soap (1) and then changing it to 1.2 does work. I need to look into that.
Comment #3
dman CreditAttribution: dman commentedFound the extra setting (form validate and submit) where I had to check for both 'soap' and 'soap 1.2' now. Saving as soap 1.2 properly initializes when saving now.
Comment #4
stella CreditAttribution: stella commentedVery useful feature. However, I don't need to set an actor and so when I attempt to make an api call, I get this warning 3 times:
Happens regardless of whether I set the actor to empty string or NULL, in the code. The attached patch re-roll fixes that by only setting the argument in the SoapHeader call if not empty.
Comment #5
dman CreditAttribution: dman commentedThat makes sense.
I only had one endpoint to test against, I'm not sure how much variation to expect.
Comment #6
imclean CreditAttribution: imclean commentedI couldn't apply the patch.
However
wsclient_tester/wsclient_tester.inc
does exist. It doesn't appear to be looking in the subdirectory.We have a need for nested headers. This is possible with SOAP headers, see this example. The $data parameter is mixed data type.
The UI would be the trickiest part for this I think, but as dman mentioned, we could use the structured-input-field-renderer code from the wsclient_tester.
Comment #7
dman CreditAttribution: dman commented#3 applies clean, not sure why #4 has patch problems.
Comment #8
dman CreditAttribution: dman commentedHere's another attempt at reducing the notice about SoapHeader::SoapHeader(): Invalid actor
(entirely untested, just a visual re-work, but worth a crack)
Comment #9
imclean CreditAttribution: imclean commentedPatch in #8 applies ok but there's no "add more" button for additional headers. To gain an extra row, enter data into the first one the click save and re-edit.
I also tested it for my specific situation to see if it work would without nesting headers, to no avail.
As the headers may be a string or an array or other, the UI could get interesting.
Comment #10
imclean CreditAttribution: imclean commentedThe header requirements for each method are actually defined in the WSDL I'm working with. Is this a normal approach?
http://ws.idssasp.com/Members.asmx?wsdl
For e.g. do a search for "GetMemberList" including the quotes.
The parameters are also defined, but these don't show up automatically in the method configuration. I suspect there may not be a standard for this.
Comment #11
imclean CreditAttribution: imclean commentedI just realised the significance of this:
With auth headers and lots of methods this would be very useful.
Comment #12
dman CreditAttribution: dman commented> The header requirements for each method are actually defined in the WSDL I'm working with. Is this a normal approach?
I see it, but I've not seen that syntax before. I can't say how normal it is.
All I can say is I don't have an endpoint that does that to test against, so I can't develop for it.
Comment #13
imclean CreditAttribution: imclean commentedUnderstood. It's not a huge priority anyway. Nested and global headers probably are, and I could potentially take a look at this sometime.
I also think the parameters for each method are being found but not recognised automatically as their Drupal generated machine name is different to their value. Again, not a big issue.
Comment #14
dman CreditAttribution: dman commentedThat thing saying 'parameters' is just a reflection of how wsclient analyses the WSDL. the actual machine name is moot AFAIK, it's the datatype that counts (and works)
Comment #15
dman CreditAttribution: dman commentedRe above .
The 'add more' button would be a nice UI extra, but at the time when I was mired with actually getting namespaces and protocol right, I skipped that bit and went with the old-school 'here's another if you need it' method.
Seeing as to actually make a full UI work perfectly it looks like we would be adding not just AJAX but NESTED AJAX for complex datatype forms ...I avoided getting lost in that.
It got to the point of 'does what I needed AND is re-usable enough for the next job of the same shape'
The next job of a DIFFERENT shape - I'd not budgeted for.
Comment #16
imclean CreditAttribution: imclean commentedIt does indeed. I was getting confused by the name, which was integer and string where required, and "parameters" otherwise. Also, the WSDL I'm working with involves a single parameter, which is an array of other parameters. So these are all nested too! Much UI fun to be had.
I do understand job priorities. I won't be able to work on this as a patch at the moment but will hopefully have a better understanding and code to share by the end of it.
Semi related: after further reading, I see WSDL is a very well defined and structured language. Pretty much everything can be defined in it so I can see how complete auto-configuration would be possible with the right data in the endpoints.
Comment #17
dman CreditAttribution: dman commentedYeah, I was happy with the *ability* for me to automatically build a full form that reflected the WSDL schema required, given enough patience. I had some background with XSD and schema importing before https://drupal.org/sandbox/dman/1126696 , so transferring it to FAPI is actually possible.
Still, making it all AJAXed and 'add more' - is further down the wish-list.
Comment #18
stella CreditAttribution: stella commentedI have one form on my site for which I need to submit up to 3 SOAP operations, one after the other (I know, ugh!). The SoapHeaders need to be different for each operation, but the __setSoapHeaders() call doesn't overwrite the existing headers unless you set them to NULL first, like this:
$client->__setSoapHeaders(NULL);
Comment #19
dman CreditAttribution: dman commentedHm. I understand the issue.
Certainly had not anticipated it.
Yeah, I guess resetting the headers to Null before doing anything is a safe way to deal with that. As far as I can imagine.
Comment #20
imclean CreditAttribution: imclean commentedSame as patch #8, against latest git in case anyone wants to play.
Clear your cache after patching.
Comment #21
Horroshow CreditAttribution: Horroshow commentedIs there something similar for REST header?
Comment #22
hvahid CreditAttribution: hvahid commentedNever mind the url was broken.
Comment #23
dman CreditAttribution: dman commentedRe-roll for fuzz, and one reject due to authentication changes smoothed out
Comment #24
Eric_A CreditAttribution: Eric_A commentedIt just so happens that I rerolled this myself and was going to upload it now. I noticed you made an extra change? Here's the output from a simple diff:
Comment #25
dman CreditAttribution: dman commentedYep, I noticed that the tester was not testing fully as it only handled SOAP and REST, so needed to handle SOAP 1.2 also. Was a minor oversight from earlier patches I guess.
The change mirrors that made in three other places already.
Comment #26
dman CreditAttribution: dman commentedRe-roll for fuzz
Comment #27
dman CreditAttribution: dman commentedI'd like to put this to bed BUT, don't really feel like doing that until we can find a way to do some real testing for regressions and things.
Comment #28
Deciphered CreditAttribution: Deciphered commentedI know you want to put this to bed, but I have a suggestion, yet not necessarily the time to commit to it myself:
Specific SOAP endpoint sets Authetnication in the Header, and the WSDL provides the an Authentication data structure, but this patch doesn't implement the ability to define a data structure or attach data to the header, plain text only.
I'm not sure how common this practice would be, if it's just something specific to his particular endpoint, but I could see it being useful.
Additionally, I would see it being useful to change the Header values on subsequent Rules, but it appears the header values can only be set via the service definition (and code I assume).
Just my 2c.
Comment #29
Deciphered CreditAttribution: Deciphered commentedIn addition to the last comment, it appears that I can't do what I need to via the UI with the current approach, which is:
I am able to set the header via code though which is a step forward.
Comment #30
Deciphered CreditAttribution: Deciphered at Realityloop for Park Street Group commentedAdditionally, while #8 does visually reduce the code for the Actor check, it does re-introduce the error #4 sought to fix, which is regardless of $actor being NULL or an empty string, the error message continues to be set. The argument can not be set at all if it doesn't have a value, and as such there has to be two calls to the function instead of one with a nested conditional.
Re-roll attached.
Comment #31
liquidcms CreditAttribution: liquidcms commentedCan someone explain the use of additions mentioned in this thread?
I am trying to add the following header to my SOAP request:
is this possible with this patch? Is it possible in code?
even before this patch i could see terms shown in this header listed in the data types under each Operation: http://screencast.com/t/X1sn5X5VH0X0 but have no idea how to utilize this.
Comment #32
Deciphered CreditAttribution: Deciphered at Realityloop for Park Street Group commented@liquidcms
I had essentially the same scenario, and I can confirm that the patch does not cater for it via the UI. Your header is far more complicated than mine was, which may make it tricky to do via code, but the general concept is this:
Although, I might have that slightly wrong. Play with it, and xdebug/debug it further down the line so you can see the xml that is generated.
Ideally the patch needs to be re-worked to allow for structured data to be applied to the header.
Comment #33
liquidcms CreditAttribution: liquidcms commentedthanks deciphered, dman had shown me how to do this a couple nights ago.. he ended up with this using the same hook you mention:
Comment #34
TheWrench CreditAttribution: TheWrench commentedI tried applying the patch in #26 and #30, and both give me a white screen of death. (I'm running the latest Drupal 7, with Web service client 7.x-1.x-dev.)
I really need to create a soap header like this:
Ultimately what I'm trying to do is retrieve data via SOAP web service and display it on a drupal page (possibly with wsclient-feeds), but I haven't been able to get past this header issue. I do see some people have developed code for a custom module that will add the headers, but then they provide some PHP to "call" it ...thats where I'm getting confused. Would love to just have the same header sent with every request. Any help is greatly appreciated!
Thanks!
Comment #35
dineshw CreditAttribution: dineshw as a volunteer commentedJust wondering if that works!
I will be trying it in a next shot!
Comment #36
imclean CreditAttribution: imclean commented@TheWrench, you'd need to extend WSClientSOAPEndpoint. Add the headers in the call() method. See the code in wsclient I think for an example.
e.g.
Comment #37
dineshw CreditAttribution: dineshw as a volunteer commentedIt works smoothly!
Comment #38
berenddeboer CreditAttribution: berenddeboer commentedComment #39
dman CreditAttribution: dman commentedThanks for the bump.
I actually picked this up and added a bunck of tests (OoOOooh!) last weekend - including local stub servers and services to test *against* !
I'll try to rein in my madness there and get something stable-ish to get into a new version number. I was about 3/4 of the way through when I figured I should be putting my examples in the example folder instead of the tests folder, so went to sleep instead of renaming everything I'd been working on.
Will try to re-animate that soon
Comment #40
berenddeboer CreditAttribution: berenddeboer at Xplain Hosting commentedReroll against latest dev.
Comment #41
dman CreditAttribution: dman as a volunteer commentedRe-roll. Correctly localised to module directory not webroot this time.
This has been useful enough for enough people for long enough, I'll push it in now, so we can address any issues as follow-ups.