I am hitting a WCF service using a WSDL I don't have access to and cannot modify. For one of the requests the remote service is dying because we are sending the:
<Action s:mustUnderstand="1"....>
Having searched extensively I cannot find a simple solution to my problem. So,
in a typical message:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none" />
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<retrieveBooking xmlns="http://services.rccl.com/Interfaces/RetrieveBooking">
<OTA_ReadRQ TransactionActionCode="RetrievePrice" SequenceNmbr="1" Version="1" xmlns="http://www.opentravel.org/OTA/2003/05/alpha">
I figured I could remove this node as part of message inspector:
internal class MyMessageInspector : IClientMessageInspector
{
public object BeforeSendRequest(ref Message aRequest, IClientChannel aChannel)
{
//Get rid of mustUnderstand Action node
foreach (MessageHeaderInfo headerInfo in aRequest.Headers.UnderstoodHeaders)
{
aRequest.Headers.UnderstoodHeaders.Remove(headerInfo);
}
return null;
}
}
however even though the aRequest.Headers.UnderstoodHeaders is empty after I remove all the elements, I am still seeing the Action node being emitted in the XML.
What do I have to do to make this work?
How do I get at the
message contents so that I can inspect the name of the first node of
the body tag retrieveBooking in this case? (I only need to do
this for a specific message, not all of them)
And the answer ends up being very simple in the end.
public object BeforeSendRequest(ref Message aRequest, IClientChannel aChannel)
{
//For the CabinDetail message the API provider has requested that we REMOVE the XML action node from the header as it causes their end to fail
//<s:Header>
//<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none" />
//</s:Header>
if (aRequest.ToString().Contains("CabinDetail"))
{
int headerIndexOfAction = aRequest.Headers.FindHeader("Action", "http://schemas.microsoft.com/ws/2005/05/addressing/none");
aRequest.Headers.RemoveAt(headerIndexOfAction);
}
return null;
}
Replace
[System.ServiceModel.OperationContractAttribute(Action ="", ReplyAction="*")]
By
[System.ServiceModel.OperationContractAttribute(Action ="*", ReplyAction="*")]
Related
I am facing an issue in calling .Net Webservice from Java client using Spring Boot.
Error logs:
org.springframework.ws.soap.client.SoapFaultClientException: System.Web.Services.Protocols.SoapException: Server did not recognize the value of HTTP Header SOAPAction: http://staging.dayross.ca/public/shipmentservices.asmx.
I added SOAP action in my client code:
#Component
public class SOAPConnector extends WebServiceGatewaySupport {
public Object callWebService(String url, Object request) {
return getWebServiceTemplate().marshalSendAndReceive(url, request, new SoapActionCallback("http://staging.dayross.ca/public/shipmentservices.asmx"));
}
}
//Client code:
CreatePickup2Response result = (CreatePickup2Response) soapConnector
.callWebService("http://staging.dayross.ca/public/shipmentservices.asmx", pickupReq);
//Bean configuration:
#Bean
public SOAPConnector soapConnector(Jaxb2Marshaller marshaller) {
SOAPConnector client = new SOAPConnector();
client.setDefaultUri("http://staging.dayross.ca/public/shipmentservices.asmx");
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
return client;
}
I am pretty sure I am missing some basic configuration here but not able to figure out. Can someone help me troubleshoot this issue?
Complete Trace:
2019-05-11 22:21:06.685 DEBUG 21052 --- [ restartedMain] o.s.ws.client.core.WebServiceTemplate : Opening [org.springframework.ws.transport.http.HttpUrlConnection#4c12c5cb] to [http://staging.dayross.ca/public/shipmentservices.asmx]
2019-05-11 22:21:06.719 TRACE 21052 --- [ restartedMain] o.s.ws.client.MessageTracing.sent : Sent request [<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><SOAP-ENV:Body><ns2:CreatePickup2 xmlns:ns2="http://dayrossgroup.com/web/public/webservices/shipmentServices" xmlns:ns3="http://www.dayrossgroup.com/web/common/webServices/OnlineShipping"><ns2:division>GeneralFreight</ns2:division><ns2:emailAddress>staging#dr.com</ns2:emailAddress><ns2:password>test</ns2:password><ns2:shipment><ns2:ShipperAddress><ns2:Address1>8345 WHITE OAK AVENUE</ns2:Address1><ns2:City>Saint-Augustin-De-Desmaures</ns2:City><ns2:Country>CA</ns2:Country><ns2:Name>SHIPPER CONTACT NAME</ns2:Name><ns2:PostalCode>G3A0G2</ns2:PostalCode><ns2:Province>QC</ns2:Province><ns2:CompanyName>ADVANTAGE</ns2:CompanyName><ns2:EmailAddress>SHIPPEREMAIL#EMAIL.COM</ns2:EmailAddress><ns2:PhoneNumber>9092044990</ns2:PhoneNumber></ns2:ShipperAddress><ns2:ConsigneeAddress><ns2:Address1>5622 BURLEIGH CRESCENT SE</ns2:Address1><ns2:City>CALGARY</ns2:City><ns2:Country>CA</ns2:Country><ns2:Name>CONSIGNEE CONTACT NAME</ns2:Name><ns2:PostalCode>T2H1Z8</ns2:PostalCode><ns2:Province>AB</ns2:Province><ns2:CompanyName>LOWRY</ns2:CompanyName><ns2:PhoneNumber>8964567412</ns2:PhoneNumber></ns2:ConsigneeAddress><ns2:BillToAccount>56896</ns2:BillToAccount><ns2:Items><ns2:ShipmentItem><ns2:Description>SKID OF TAPE</ns2:Description><ns2:Height>24</ns2:Height><ns2:Length>30</ns2:Length><ns2:LengthUnit>Inches</ns2:LengthUnit><ns2:Pieces>2</ns2:Pieces><ns2:Weight>50</ns2:Weight><ns2:WeightUnit>Pounds</ns2:WeightUnit><ns2:Width>30</ns2:Width></ns2:ShipmentItem></ns2:Items><ns2:ServiceLevel>GL</ns2:ServiceLevel><ns2:ShipmentType>Regular</ns2:ShipmentType><ns2:ReadyTime>2019-05-19 15:19:27</ns2:ReadyTime><ns2:ClosingTime>2019-05-19 17:19:27</ns2:ClosingTime><ns2:ShipmentStatus><ns2:Id xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/><ns2:RowVersion>0</ns2:RowVersion><ns2:InternalStatus>false</ns2:InternalStatus><ns2:OrderEntryState>ReadyForPickup</ns2:OrderEntryState></ns2:ShipmentStatus><ns2:MeasurementSystem>Imperial</ns2:MeasurementSystem><ns2:ExpiryDate>2029-01-07T22:21:06.659-06:00</ns2:ExpiryDate><ns2:Division>GeneralFreight</ns2:Division><ns2:ReferenceNumbers><ns2:string>RA89653</ns2:string></ns2:ReferenceNumbers></ns2:shipment><ns2:language>EN</ns2:language></ns2:CreatePickup2></SOAP-ENV:Body></SOAP-ENV:Envelope>]
2019-05-11 22:21:06.970 TRACE 21052 --- [ restartedMain] o.s.ws.client.MessageTracing.received : Received response [<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soap:Body><soap:Fault><faultcode>soap:Client</faultcode><faultstring>System.Web.Services.Protocols.SoapException: Server did not recognize the value of HTTP Header SOAPAction: http://staging.dayross.ca/public/shipmentservices.asmx.
at System.Web.Services.Protocols.Soap11ServerProtocolHelper.RouteRequest()
at System.Web.Services.Protocols.SoapServerProtocol.RouteRequest(SoapServerMessage message)
at System.Web.Services.Protocols.SoapServerProtocol.Initialize()
at System.Web.Services.Protocols.ServerProtocol.SetContext(Type type, HttpContext context, HttpRequest request, HttpResponse response)
at System.Web.Services.Protocols.ServerProtocolFactory.Create(Type type, HttpContext context, HttpRequest request, HttpResponse response, Boolean& abortProcessing)</faultstring><detail/></soap:Fault></soap:Body></soap:Envelope>] for request [<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><SOAP-ENV:Body><ns2:CreatePickup2 xmlns:ns2="http://dayrossgroup.com/web/public/webservices/shipmentServices" xmlns:ns3="http://www.dayrossgroup.com/web/common/webServices/OnlineShipping"><ns2:division>GeneralFreight</ns2:division><ns2:emailAddress>staging#dr.com</ns2:emailAddress><ns2:password>test</ns2:password><ns2:shipment><ns2:ShipperAddress><ns2:Address1>8345 WHITE OAK AVENUE</ns2:Address1><ns2:City>Saint-Augustin-De-Desmaures</ns2:City><ns2:Country>CA</ns2:Country><ns2:Name>SHIPPER CONTACT NAME</ns2:Name><ns2:PostalCode>G3A0G2</ns2:PostalCode><ns2:Province>QC</ns2:Province><ns2:CompanyName>ADVANTAGE</ns2:CompanyName><ns2:EmailAddress>SHIPPEREMAIL#EMAIL.COM</ns2:EmailAddress><ns2:PhoneNumber>9092044990</ns2:PhoneNumber></ns2:ShipperAddress><ns2:ConsigneeAddress><ns2:Address1>5622 BURLEIGH CRESCENT SE</ns2:Address1><ns2:City>CALGARY</ns2:City><ns2:Country>CA</ns2:Country><ns2:Name>CONSIGNEE CONTACT NAME</ns2:Name><ns2:PostalCode>T2H1Z8</ns2:PostalCode><ns2:Province>AB</ns2:Province><ns2:CompanyName>LOWRY</ns2:CompanyName><ns2:PhoneNumber>8964567412</ns2:PhoneNumber></ns2:ConsigneeAddress><ns2:BillToAccount>56896</ns2:BillToAccount><ns2:Items><ns2:ShipmentItem><ns2:Description>SKID OF TAPE</ns2:Description><ns2:Height>24</ns2:Height><ns2:Length>30</ns2:Length><ns2:LengthUnit>Inches</ns2:LengthUnit><ns2:Pieces>2</ns2:Pieces><ns2:Weight>50</ns2:Weight><ns2:WeightUnit>Pounds</ns2:WeightUnit><ns2:Width>30</ns2:Width></ns2:ShipmentItem></ns2:Items><ns2:ServiceLevel>GL</ns2:ServiceLevel><ns2:ShipmentType>Regular</ns2:ShipmentType><ns2:ReadyTime>2019-05-19 15:19:27</ns2:ReadyTime><ns2:ClosingTime>2019-05-19 17:19:27</ns2:ClosingTime><ns2:ShipmentStatus><ns2:Id xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/><ns2:RowVersion>0</ns2:RowVersion><ns2:InternalStatus>false</ns2:InternalStatus><ns2:OrderEntryState>ReadyForPickup</ns2:OrderEntryState></ns2:ShipmentStatus><ns2:MeasurementSystem>Imperial</ns2:MeasurementSystem><ns2:ExpiryDate>2029-01-07T22:21:06.659-06:00</ns2:ExpiryDate><ns2:Division>GeneralFreight</ns2:Division><ns2:ReferenceNumbers><ns2:string>RA89653</ns2:string></ns2:ReferenceNumbers></ns2:shipment><ns2:language>EN</ns2:language></ns2:CreatePickup2></SOAP-ENV:Body></SOAP-ENV:Envelope>]
2019-05-11 22:21:06.971 DEBUG 21052 --- [ restartedMain] o.s.ws.client.core.WebServiceTemplate : Received Fault message for request [SaajSoapMessage {http://dayrossgroup.com/web/public/webservices/shipmentServices}CreatePickup2]
Your problem is that the server you're trying to perform an operation on does not recognize the SOAPAction header and is therefore unable to route your requests to the appropriate endpoint. If you take a look at the WSDL of the service you're trying to connect to there are many different operations you can perform.
For your specific example, CreatePickup2, the operation is specified as
<wsdl:operation name="CreatePickup2">
<soap:operation soapAction="http://dayrossgroup.com/web/public/webservices/shipmentServices/CreatePickup2" style="document"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
Since you've already specified the defaultUri in your bean you don't need to specify the URL in your getWebServiceTemplate().marshalSendAndReceive(...) method.
That will give you a component looking like this
#Component
public class SOAPConnector extends WebServiceGatewaySupport {
public Object callWebService(Object request, String soapAction) {
return getWebServiceTemplate().marshalSendAndReceive(request, new SoapActionCallback(soapAction));
}
}
And a client code something like this
CreatePickup2Response result = (CreatePickup2Response) soapConnector
.callWebService(pickupReq, "http://dayrossgroup.com/web/public/webservices/shipmentServices/CreatePickup2");
If you're gonna use more than one of the operations you'll need to make my proposed solution more generic.
I am trying to call a method in a WCF service. I can hit the method and it can receive all the parameters except an array of integers. The content of my calls looks like:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action>Test/TestContract/DeletePersons</a:Action>
<a:To>#Location</a:To>
</s:Header>
<s:Body>
<DeletePersons xmlns="Test">
<groupname>connStringDatabase</groupname>
<username>#Username</username>
<password>#Password</password>
<insurer>#Insurer</insurer>
<onlyfromself>false</onlyfromself>
<identification_number/>
<initials/>
<firstname>#FirstName</firstname>
<name>#LastName</name>
<gender/>
<dateofbirth/>
<personIds>
<int>123</int>
</personIds>
</DeletePersons>
</s:Body>
</s:Envelope>
The method in the contract of the service looks like this:
[ServiceContract(Namespace = "Test")]
public interface TestContract
{
[OperationContract]
TestResponse DeletePersons(string groupname, string username, string password, string insurer, List<int> personIds);
}
As I said, all the parameters (strings) are received correctly while the personIds array is received but it's empty. How should encode it into the XML request? Thanks in advance!
I have been inspecting messages sent in a WCF-based system, using the available IClientMessageInspector (and IDispatchMessageInspector).
Currently I'm trying to manually add XML to the message, I cannot manage to get it working.
Situation:
Incoming message has a body like
<s:Body>
<Type xmlns="http://schemas.microsoft.com/2003/10/Serialization/">
...
</Type>
</s:Body>
I want to replace the entire body with custom content, manually structured in a string. I.e., I have a correct XML body in a string, which I want to place within the body of the message.
Is this even possible?
Edit:
To further clarify the question: Can I somehow access the "raw text" of the message, and edit it?
Edit2: I.e. I want to keep the original header and all from the incoming message, but want to replace everything between
<body> </body>
with my custom content which currently resides in a string.
You could use approach similar to one in this blog post https://blogs.msdn.microsoft.com/kaevans/2008/01/08/modify-message-content-with-wcf/
In short you add EndpointBehavior in which you add custom MessageInspector:
Service1Client proxy = new Service1Client();
proxy.Endpoint.Behaviors.Add(new MyBehavior());
public class MyBehavior : IEndpointBehavior
{
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
MyInspector inspector = new MyInspector();
clientRuntime.MessageInspectors.Add(inspector);
}
}
public class MyInspector : IClientMessageInspector
{
public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
var xml = "XML of Body goes here";
var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml));
XmlDictionaryReader xdr = XmlDictionaryReader.CreateTextReader(stream, new XmlDictionaryReaderQuotas());
Message replacedMessage = Message.CreateMessage(reply.Version, null, xdr);
replacedMessage.Headers.CopyHeadersFrom(reply.Headers);
replacedMessage.Properties.CopyProperties(reply.Properties);
reply = replacedMessage;
}
}
EDIT: added MemoryStream initiated with data from a string value.
I'm developing an ASP.NET Webservice (not WCF) for a given client. This is one of those situations, where you can not change anything at the client.
The client sends the following XML to request a method:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:cometxsd="http://werk-ii.de/soap/comet/Schema"
xmlns:comet="http://werk-ii.de/soap/comet"
xmlns:xop="http://www.w3.org/2004/08/xop/include"
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:xmime5="http://www.w3.org/2005/05/xmlmime"
xmlns:ns1="http://soap.comet.werkii.com/">
<SOAP-ENV:Body>
<ns1:login xsi:type="ns1:login">
<user>myusername</user>
<password>mypassword</password>
<client>whatever</client>
<language>de</language>
</ns1:login>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
My Service provides the login-Method like this:
[WebService(Namespace = "http://soap.comet.werkii.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class CometService : WebService
{
[WebMethod(MessageName = "login")]
[return: XmlElement("return")]
public LoginResult Login (string user, string password, string client, string language)
{
return new LoginResult() {
ResultCode = 0,
SessionId = user + "-" + password + "-" + client + "-" + language
};
}
}
public class LoginResult
{
[XmlElement("resultCode")]
public int ResultCode { get; set; }
[XmlElement("sessionId")]
public string SessionId { get; set; }
}
If I start the service, it tells me what SOAP 1.1 code I have to send as a request, that ist:
<?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>
<login xmlns="http://soap.comet.werkii.com/">
<user>string</user>
<password>string</password>
<client>string</client>
<language>string</language>
</login>
</soap:Body>
</soap:Envelope>
When I use this code - as told by the service - from another test client (i wrote one in PHP), everything works fine an I get a result. But when I send the code from the beginning (which is what the real client will send), the method is called but all 4 arguments are null.
From an XML view, in my opinion both requests are the same. The only difference is, where the namespace is defined and if elements use the ns1 prefix. This should not make any difference, when the service reads it as XML. Maybe I'm wrong.
Perhaps the 4 arguments in the first XML have a different namespace (none) than the method (ns1). Can that be the reason why all arguments are null? How would I change the namespace for the arguments only?
When I change only the method lines in XML - replacing <ns1:login xsi:type="ns1:login"> with <login xmlns="http://soap.comet.werkii.com/"> and also the closing tag - it works! So the service seems not to understand my request, if the method element uses a namespace prefix, though the namespace is properly defined in the root element.
I tried the following to change the XML format which the service expects:
System.Web.Services.Protocols.SoapDocumentMethodAttribute - no effect at all
XmlNamespaceDeclarationsAttribute as shown here - which seems not to work because it is made for manipulating complex types, not the service class or a method
So the question is, how can I tell my service to accept the XML from the first example?
Good to know that parameters can also have Attributes:
public LoginResult Login (
[XmlElement(Namespace = "")] string user,
[XmlElement(Namespace = "")] string password,
[XmlElement(Namespace = "")] string client,
[XmlElement(Namespace = "")] string language)
{
return new LoginResult() {
ResultCode = 0,
SessionId = user + "-" + password + "-" + client + "-" + language
};
}
That's the solution to put the parameters into the global namespace – problem solved.
I have a method that is calling a web service. When this web service is called, the following method is called:
[System.Web.Services.Protocols.SoapDocumentMethodAttribute(
"http://mydomain.com/services/DoSomething",
RequestNamespace = "http://mydomain.com/services",
ResponseNamespace = "http://mydomain.com/services",
Use = System.Web.Services.Description.SoapBindingUse.Literal,
ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
[return: System.Xml.Serialization.XmlElementAttribute("MyResponse")]
public MyResponse DoSomethingr(MyRequest myRequest)
{
object[] results = this.Invoke("DoSomething", new object[] { myRequest});
return ((MyResponse)(results[0]));
}
When this method is called, I've noticed that the XML includes the following:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<!-- XML --!>
</soap:Body>
</soap:Envelope>
How do I remove the <soap:> wrappers from my XML?
I wouldn't. Soap is a standard protocol for publishing services and accessing remote data. Without it the remote server won't understand your request.