I'm having a problem with a web service that I'm writing (it's asmx).
I have this method:
[WebMethod()]
[SoapDocumentMethod(
RequestNamespace="http://bsp.XXX.org",
ResponseNamespace="http://bsp.XXX.org",
ResponseElementName="PaymentResults",
RequestElementName="GetPaymentResult",
Action = "http://bsp.XXX.org/GetPaymentResult")]
public PaymentResult[] GetPaymentResult(string MerchantRef)
{
try
{
if (!String.IsNullOrEmpty(MerchantRef))
{
return PaymentResultRepository.GetPaymentResults(MerchantRef).ToArray();
}
else
{
_errorLog.Error("MerchantRef is empty");
}
}
catch (Exception ex)
{
_errorLog.Error("Failed to get payment details", ex);
}
return new PaymentResult[0];
}
}
And it's being called from an Oracle Forms application. The SOAP request received is:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<GetPaymentResult xmlns="http://bsp.XXX.org/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<MerchantRef xsi:type="xsd:string">
IP/58991/1
</MerchantRef>
</GetPaymentResult>
</SOAP-ENV:Body>
The problem is that "MerchantRef" is always an empty string in my method... Anyone have any idea about this? Is the SOAP request wrong? Am I missing something obvious?
Turns out that the problem was the SOAP request...
It didn't like the encodingStyle attribute, once this was removed it worked perfectly.
i.e. from this:
<GetPaymentResult xmlns="http://bsp.XXX.org/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
to:
<GetPaymentResult xmlns="http://bsp.XXX.org/">
Related
I have a TwiML app with this code in the Connect action of the CallController. This code is taken straight from the Twilio demos.
[HttpPost]
public virtual ActionResult Connect(string phoneNumber, string called)
{
var response = new VoiceResponse();
var dial = new Dial(callerId: "+6138595????");
if (phoneNumber != null)
{
dial.Number(phoneNumber);
}
else
{
dial.Client("support_agent");
}
response.Dial(dial);
return TwiML(response);
}
When this is called it raises the error "Data at the root level is invalid. Line 1, position 1."
The XML this generates is
<?xml version="1.0" encoding="utf-8"?>
<Response>
<Dial callerId="+6138595????">
<Client>support_agent</Client>
</Dial>
</Response>
Twilio Evangelist here.
A quick question - is this happening every time the method is invoked, or only when specific inputs are provided? Needing to build the string manually is of course not desired. So I would like to get to the bottom of what triggered this result.
I have found I can fix it by replacing
return TwiML(response);
with
return new TwiMLResult(response.ToString(), new UTF8Encoding());
Appears to be some kind of encoding issue using the first method.
I had the same problem we solved it in our WebApi by skipping the Twilio sdk and generating the xml by ourselves.
I hope this will work for you too:
[HttpPost]
public virtual HttpResponseMessage Connect(string phoneNumber, string called)
{
string twiml = $"<?xml version=\"1.0\" encoding=\"utf-8\"?><Response><Dial callerId=\"{phoneNumber}\"><Client>support_agent</Client></Dial></Response>";
var xmlResponse = new HttpResponseMessage();
xmlResponse.Content = new StringContent(twiml, Encoding.UTF8, "text/xml");
return xmlResponse;
}
Please notice that there are no end of lines - "\n", \r", etc.
I try to access to my webmethods with SOAP UI but it works only for the first one and I don't undestand why.
My Webservices methods :
[SoapHeader ("AuthenticationInfo", Required=true)]
[WebMethod(EnableSession = true)]
public string HelloWorld()
{
if (!(AuthenticationInfo.Username == "test" && AuthenticationInfo.Password == "test"))
{
throw new Exception();
// I put that in the aim to get an error, I'll modify this later
}
return "OK";
}
[SoapHeader("AuthenticationInfo", Required = true)]
[WebMethod]
public string Authenticate(string MethodName)
{
if (!(AuthenticationInfo.Username == "test" && AuthenticationInfo.Password == "test")
{
throw new Exception();
}
else
{
HelloWorld();
}
return "aaaa";
}
[WebMethod]
public int Calcul(int a, int b)
{
return a+b ;
}
When I put this XML in SOAP UI :
<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:Header>
<AuthHeader xmlns="http://tempuri.org/">
<Username>test</Username>
<Password>test</Password>
<key>string</key>
</AuthHeader>
</soap:Header>
<soap:Body>
<HelloWorld xmlns="http://tempuri.org/" />
</soap:Body>
</soap:Envelope
It works perfectly, I get the return of HelloWord() Method.
But if I put :
<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>
<Calcul xmlns="http://tempuri.org/">
<a>1</a>
<b>1</b>
</Calcul>
</soap:Body>
</soap:Envelope>
It invokes again HelloWorld() method. My URL which I use in SOAP UI is :"http://localhost:62353/MyWebService.asmx", so I try a new request with the last XML at the URL "http://localhost:62353/MyWebService.asmx/Calcul" and i have an error.
Have you I Idea ? May I'm in the wrong way to use SOAP UI ?
To reply to Kosala W :
I get an UI like that where I can click on the methods. The calcul methods works only here because this methos doesn't need the SoapHeader.
Here is the response :
When you specify the URL in SOAP UI you need to add ?wsdl at the end like that : http://localhost:62353/MyWebService.asmx?wsdl Then I refresh the project in SOAP UI and all the methods appears
I'm trying to consume a webservice with async methods. I have used it's WSDL to create a client and calling the metod like this:
public static GetStockResponse GetResult()
{
var client = new ServiceSoapClient();
var inItems = new ArrayOfItemNo();
inItems.Add(new ItemNoRow { ItemNo = "0000001350" });
Task<GetStockResponse> task = client.GetStockAsync(inItems, "034");
task.Wait();
return task.Result;
}
In fiddler I can see the call with status 200
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body>
<GetStockRequest xmlns="http://www.unikum.se/pws"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ItemNo>
<ItemNoRow>
<ItemNo>0000001350</ItemNo>
</ItemNoRow>
</ItemNo>
<StoreID>034</StoreID>
</GetStockRequest>
And the response:
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body>
<GetStockResponse xmlns="http://www.unikum.se/pws">
<Items>
<Item>
<ItemNo>0000001350</ItemNo>
<DeliveryTimeText>4-6 veckor</DeliveryTimeText>
<DeliveryDays>42</DeliveryDays>
<Stores>
<Store>
<StoreID>034</StoreID>
<NotInERP>N</NotInERP>
<InStockQty>2</InStockQty>
<InShowRoom>N</InShowRoom>
</Store>
</Stores>
</Item>
</Items>
</GetStockResponse>
But the result of the task (task.Result) is empty. It is a GetStockResponse but all values is null.
Why? Everything seems correct or doesn't it?
Well, some more testing with some other methods provided by the service owner clearly indicated that they did not follow the response structure defined in their own WSDL. I could see that in a simpler test where they partially followed the WSDL structure I got correct values back on those specific properties but not on those that differed. So now I could more clearly point to the error and return with "Please fix this, or else" response to the service provider.
I am successfully working with a third party soap service. I have added a service reference to a soap web service which has auto generated the classes.
When an error occurs it returns a soap response like this:
<SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Client</faultcode>
<faultstring xsi:type="xsd:string">Error while reading parameters of method 'Demo'</faultstring>
<detail xsi:type="xsd:string">Invalid login or password. Connection denied.</detail>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I can catch the error but not extract the detail. I have tried the following code:
catch (FaultException ex)
{
MessageFault msgFault = ex.CreateMessageFault();
var elm = msgFault.GetDetail<string>();
//throw Detail
}
However it Errors with the following as detail node is not an object:
Expecting element 'string' from namespace 'http://schemas.datacontract.org/2004/07/MyDemoNamespace'.. Encountered 'Text' with name '', namespace ''.
This is third party API so I cannot change the response.
The detail node of the message fault is expected to contain XML. The GetDetail will deserialize this XML into the given object.
As the contents is not XML it was possible to use this method.
You can however get access to the XML and read the innerXml value:
MessageFault msgFault = ex.CreateMessageFault();
var msg = msgFault.GetReaderAtDetailContents().Value;
This approached worked.
Here's a few methods I've found of extracting that detailed exception information from FaultExceptions
Get the String Contents of a Single Element
catch (FaultException e)
{
var errorElement = XElement.Parse(e.CreateMessageFault().GetReaderAtDetailContents().ReadOuterXml());
var errorDictionary = errorElement.Elements().ToDictionary(key => key.Name.LocalName, val => val.Value);
var errorMessage = errorDictionary?["ErrorMessage"];
}
Example Output:
Organization does not exist.
Get the String Contents of a All Details as a Single String
catch (FaultException e)
{
var errorElement = XElement.Parse(e.CreateMessageFault().GetReaderAtDetailContents().ReadOuterXml());
var errorDictionary = errorElement.Elements().ToDictionary(key => key.Name.LocalName, val => val.Value);
var errorDetails = string.Join(";", errorDictionary);
}
Example Output:
[ErrorMessage, Organization does not exist.];[EventCode, 3459046134826139648];[Parameters, ]
Get the String Contents of a Everything as an XML string
var errorElement = XElement.Parse(e.CreateMessageFault().GetReaderAtDetailContents().ReadOuterXml());
var xmlDetail = (string)errorElement;
Example Output:
<FaultData xmlns="http://schemas.datacontract.org/2004/07/Xata.Ignition.Common.Contract" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ErrorMessage>Organization does not exist.</ErrorMessage>
<EventCode>3459046134826139648</EventCode>
<Parameters i:nil="true" xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays"></Parameters>
</FaultData>
The following should give you the value of the detail element of the FaultException.
var faultMessage = faultException.CreateMessageFault();
if(faultMessage.HasDetail){
Console.Write(faultMessage.GetDetail<XElement>().Value);
}
public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
if (reply.IsFault)
{
// Create a copy of the original reply to allow default WCF processing
MessageBuffer buffer = reply.CreateBufferedCopy(Int32.MaxValue);
Message copy = buffer.CreateMessage(); // Create a copy to work with
reply = buffer.CreateMessage(); // Restore the original message
MessageFault faultex = MessageFault.CreateFault(copy, Int32.MaxValue); //Get Fault from Message
FaultCode codigo = faultex.Code;
//if (faultex.HasDetail)... //More details
buffer.Close();
You can catch FaultException<TDetail>, which gives you detail for free.
catch (FaultException<string> ex)
{
string yourDetail = ex.Detail;
}
I've got a simple C# web service proxy class that I created with WSDL.exe. I am invoking a method on the remote web service, and it is including a bunch of WS-Addressing and WS-Security headers that I do not want (and that the server is choking on). Here is an example of the raw soap request:
<?xml version="1.0" encoding="utf-8"?>
<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"
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">
<soap:Header>
<wsa:Action></wsa:Action>
<wsa:MessageID>urn:uuid:22f12267-b162-4703-a451-2d1c5c5a619b</wsa:MessageID>
<wsa:To>http://example.com/wstest</wsa:To>
<wsse:Security>
<wsu:Timestamp wsu:Id="Timestamp-5c9f0ef0-ab45-421d-a633-4c4fad26d945">
<wsu:Created>2009-04-15T16:27:25Z</wsu:Created>
<wsu:Expires>2009-04-15T16:32:25Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
</soap:Header>
<soap:Body>
<Func1 xmlns="http://example.com">
<arg_1 xmlns="">blah</arg_1>
<arg_2 xmlns="">blah2</arg_2></arg_2>
</Func1>
</soap:Body>
</soap:Envelope>
But I don't care about the WS-Addressing/WS-Security stuff. I've done nothing to include it. The .NET WSE 3.0 package seems to be adding them by default. Is there any way to get rid of these? I can see no properties on my proxy object that allow me to remove these sections. I've tried:
proxyObject.Addressing.Clear();
proxyObject.Security.Clear();
Those cause a null reference exception when I invoke my web service method.
I want the SOAP request to look like this:
<?xml version="1.0" encoding="utf-8"?>
<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:Header>
</soap:Header>
<soap:Body>
<Func1 xmlns="http://example.com">
<arg_1 xmlns="">blah</arg_1>
<arg_2 xmlns="">blah2</arg_2></arg_2>
</Func1>
</soap:Body>
</soap:Envelope>
Thanks in advance
Well, I ended up using a technique I have used in the past. I created classes that implement SoapFilter and PolicyAssertion which allow me to modify the raw XML of the SOAP request before it is sent. Below is an example:
public class MyPolicy : SoapFilter
{
public override SoapFilterResult ProcessMessage(SoapEnvelope envelope)
{
// Remove all WS-Addressing and WS-Security header info
envelope.Header.RemoveAll();
return SoapFilterResult.Continue;
}
}
public class MyAssertion : PolicyAssertion
{
public override SoapFilter CreateClientInputFilter(FilterCreationContext context)
{
return null;
}
public override SoapFilter CreateClientOutputFilter(FilterCreationContext context)
{
return new MyPolicy();
}
public override SoapFilter CreateServiceInputFilter(FilterCreationContext context)
{
return null;
}
public override SoapFilter CreateServiceOutputFilter(FilterCreationContext context)
{
return null;
}
}
Then in your web service proxy's contructor you apply the policy:
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "2.0.50727.1433")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Web.Services.WebServiceBindingAttribute(Name="MyBinding", Namespace="http://example.com")]
public partial class MyWebClient : WebServicesClientProtocol {
// ... member variables here
/// <remarks/>
public MyWebClient()
{
this.Url = "http://example.com";
if ((this.IsLocalFileSystemWebService(this.Url) == true)) {
this.UseDefaultCredentials = true;
this.useDefaultCredentialsSetExplicitly = false;
}
else {
this.useDefaultCredentialsSetExplicitly = true;
}
// Apply policy here
Policy policy = new Policy();
policy.Assertions.Add(new MyAssertion());
this.SetPolicy(policy);
}
}
to remove the addressing header i used the below code in one of my app.
public override void SecureMessage(SoapEnvelope envelope, Security security)
{
//remove addressing Header from envelope
AddressingHeaders objAH = new AddressingHeaders(envelope);
objAH.RemoveXml(envelope);
//Add Wahtever security token you want to add.
security.Tokens.Add(bla-bla-bla);
}
I used SecureMessage function because i wanted to add security tokens,but same code can be used in ProcessMessage function.
I wonder if your problem might not also have ben resolved by simply not using WSE?
Based on the answer in this page, I added the code below to remove specific header (by tagname) recursively from XML.
Public Overrides Function ProcessMessage(ByVal envelope As SoapEnvelope) As SoapFilterResult
' Remove all WS-Addressing and WS-Security header info
RemoveTag(envelope.DocumentElement, "wsa:Action")
RemoveTag(envelope.DocumentElement, "wsa:MessageID")
RemoveTag(envelope.DocumentElement, "wsa:To")
Return SoapFilterResult.[Continue]
End Function
Private Sub RemoveTag(ByVal XE As System.Xml.XmlElement, ByVal TagName As String)
For Each N As XmlNode In XE
If N.ChildNodes.Count > 0 Then
RemoveTag(N, TagName)
End If
If N.Name = TagName Then
XE.RemoveChild(N)
End If
Next
End Sub
I found the easiest way to remove these addressing and security headers was to change the web service configuration to use BasicHttpBinding instead of WSHttp binding.