Open hemawst opened 4 years ago
Hello,
The problem with it is that every project handles the exception in one/their way. This brings me an idea: I can configure a way to parse the response if the execution is an error. I'll study it a little and try to come with a solution for it.
Hi, i have provisionally solved the problem for myself as follows:
var wsCon = new HodStudio.XitSoap.WebService("https://example.com/AkkordlohnWS/test?wsdl", "http://example.com/AkkordlohnWS");
try
{
wsCon.AddParameter("reportthrombexinRequest", rpTrom);
rpResponse = wsCon.Invoke<reportthrombexinResponseType>("reportthrombexin");
if (rpResponse != null)
{
// do anything
}
}
catch(System.Net.WebException exW)
{
string strResponsStream = "";
string exMessage = exW.Message;
if (exW.Response != null)
{
using (System.IO.StreamReader responseReader = new System.IO.StreamReader(exW.Response.GetResponseStream()))
{
strResponsStream = responseReader.ReadToEnd();
}
}
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(strResponsStream);
XmlNodeList xnList;
xnList = xmlDoc.GetElementsByTagName("faultstring");
string node = "";
if(xnList != null && xnList.Count > 0)
{
foreach(XmlNode xn in xnList)
{
node = xn.InnerText;
}
}
}
I'll get the response as string and create a xml document. Then i search for the faultstring tag and then the value of it. Would that be an idea for solving the problem?
Yes. We are in a quite similar approach. I'm just trying to create a good way to configure the exception returns and configure them automatically. But thanks for the contribution. I'll use this as a base for improvement.
Just one thing: I recommend you to use two using
for the stream, to avoid any possible problem.
Hello, i have another problem with the very bad web service which i have to use.
My Code:
try
{
var wsCon = new HodStudio.XitSoap.WebService("https://example.de:8445/c4ws/services/AkkordlohnWS/test?wsdl", "http://example.com/AkkordlohnWS");
wsCon.AddParameter("report.bundle.operationRequest", rpBundOp);
rpBundOpResponse = wsCon.Invoke<reportbundleoperationResponseType>("report.bundle.operation");
if (rpBundOpResponse != null)
{
retStr = rpBundOpResponse.DataArea[0].InfoMessage.ToString();
}
}
catch (Exception e)
{
string ret1 = e.StackTrace.ToString();
retStr = e.Message;
}
The response from web service (web service did his job successful). I only interested in InfoMessage
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<report.bundle.operationResponse xmlns="http://www.infor.com/businessinterface/MEDIAkkordlohnWS">
<report.bundle.operationResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="">
<DataArea>
<MEDIAkkordlohnWS>
<OutData>0</OutData>
<InfoMessage>Noch 1 Packen zum AG offen</InfoMessage>
</MEDIAkkordlohnWS>
</DataArea>
</report.bundle.operationResponse>
</report.bundle.operationResponse>
</S:Body>
</S:Envelope>
I get an exception:
System.NullReferenceException: Object reference not set to an instance of an object.
exception stack trace:
at HodStudio.XitSoap.Helpers.WebHelpers.ExtractResult (HodStudio.XitSoap.WebService service, System.String methodName) [0x00045] in <b1bbdb60aaff4ab7bcfc89bc944b4cce>:0
at HodStudio.XitSoap.Helpers.WebHelpers.InvokeService (HodStudio.XitSoap.WebService service, System.String methodName, System.Boolean encode, System.String soapActionComplement) [0x001c3] in <b1bbdb60aaff4ab7bcfc89bc944b4cce>:0
at HodStudio.XitSoap.WebService.Invoke[ResultType] (System.String methodName, System.Boolean encode, System.String soapActionComplement) [0x0007f] in <b1bbdb60aaff4ab7bcfc89bc944b4cce>:0
at HodStudio.XitSoap.WebService.Invoke[ResultType] (System.String methodName, System.Boolean encode) [0x00000] in <b1bbdb60aaff4ab7bcfc89bc944b4cce>:0
at HodStudio.XitSoap.WebService.Invoke[ResultType] (System.String methodName) [0x00000] in <b1bbdb60aaff4ab7bcfc89bc944b4cce>:0
at AppXamarinTest.Droid.ModelWebServiceLnNeu+<>c.<AppXamarinTest.IWebserviceLN.GetPersonalDataNeu>b__0_0 () [0x000cd] in C:\VSProjekteXamarin\AppXamarinTest\AppXamarinTest\AppXamarinTest.Android\ModelWebServiceLnNeu.cs:63
The relevant parts of wsdl file:
<xsd:complexType name="report.bundle.operationResponseType">
<xsd:sequence>
<xsd:element minOccurs="0" name="DataArea">
<xsd:complexType>
<xsd:sequence>
<xsd:element maxOccurs="unbounded" minOccurs="0" name="MEDIAkkordlohnWS">
<xsd:complexType>
<xsd:sequence>
<xsd:element minOccurs="0" name="OutData" type="xsd:long"/>
<xsd:element minOccurs="0" name="InfoMessage" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
<wsdl:message name="report.bundle.operationResponse">
<wsdl:part name="report.bundle.operationResponse" type="bo:report.bundle.operationResponseType"/>
</wsdl:message>
<wsdl:operation name="report.bundle.operation">
<wsdl:input message="bo:report.bundle.operationRequest"/>
<wsdl:output message="bo:report.bundle.operationResponse"/>
<wsdl:fault message="bo:Result" name="FaultName"/>
</wsdl:operation>
So i debugged your code. The exception appeared at ExtractResult. It seems, the problem is the StringConstants.XmlResultResultFormat. In my case the "Result" have to chance with Response". (Just like that in ExtractResultClass); But when i chance this, i got another error at var result = (ResultType)serializer.Deserialize(rdr);
Have you any idea?
Provide me please the class reportbundleoperationResponseType
so I can take a look please.
The class was created automatically via a web reference from the wsdl file. Here are the two relevant classes from the reference.cs file
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.8.3761.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(TypeName="report.bundle.operationResponseType", Namespace="http://example.com/MEDIAkkordlohnWS")]
public partial class reportbundleoperationResponseType {
private reportbundleoperationResponseTypeMEDIAkkordlohnWS[] dataAreaField;
/// <remarks/>
[System.Xml.Serialization.XmlArrayAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
[System.Xml.Serialization.XmlArrayItemAttribute("MEDIAkkordlohnWS", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=false)]
public reportbundleoperationResponseTypeMEDIAkkordlohnWS[] DataArea {
get {
return this.dataAreaField;
}
set {
this.dataAreaField = value;
}
}
}
and
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.8.3761.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://example.com/MEDIAkkordlohnWS")]
public partial class reportbundleoperationResponseTypeMEDIAkkordlohnWS {
private long outDataField;
private bool outDataFieldSpecified;
private string infoMessageField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public long OutData {
get {
return this.outDataField;
}
set {
this.outDataField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool OutDataSpecified {
get {
return this.outDataFieldSpecified;
}
set {
this.outDataFieldSpecified = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public string InfoMessage {
get {
return this.infoMessageField;
}
set {
this.infoMessageField = value;
}
}
}
I hope, this will help.
So, the idea of the library is exactly to avoid use this WSDL to not have so "ugly" classes. Most of them throw several warnings in code analyzers, like SonarQube.
What I can recommend to you is to recreate the same structure of the response to your class. There was an idea about an improvement to "cut" part of the responses that were not used. That would be useful in your situation now... 🤔
Sorry, I'm a bit confused today. I tried it with my own class. However, I always get the error in the same place:
internal static void ExtractResult(this WebService service, string methodName)
{
// Selects just the elements with namespace http://tempuri.org/ (i.e. ignores SOAP namespace)
XmlNamespaceManager namespMan = new XmlNamespaceManager(new NameTable());
namespMan.AddNamespace(StringConstants.XmlResultDummyNamespace, service.Namespace);
var methodNameResult = string.Format(StringConstants.XmlResultResultFormat, methodName);
XElement webMethodResult = service.Result.SoapResponse.XPathSelectElement(string.Format(StringConstants.XmlResultXPathSelectorFormat, methodNameResult), namespMan);
// If the result is an XML, return it and convert it to string
if (webMethodResult.FirstNode.NodeType == XmlNodeType.Element)
{
var xmlResult = XDocument.Parse(webMethodResult.ToString());
service.Result.XmlResult = XmlHelpers.RemoveNamespaces(xmlResult);
service.Result.StringResult = service.Result.XmlResult.ToString();
}
// If the result is a string, return it and convert it to XML (creating a root node to wrap the result)
else
{
service.Result.StringResult = webMethodResult.FirstNode.ToString();
service.Result.XmlResult = XDocument.Parse(string.Format(StringConstants.XmlResultXDocumentFormat, service.Result.StringResult));
}
}
var methodNameResult = string.Format(StringConstants.XmlResultResultFormat, methodName);
This adds always "Result" on my methode name (report.bundle.operation -> report.bundle.operationResult).
XElement webMethodResult = service.Result.SoapResponse.XPathSelectElement(string.Format(StringConstants.XmlResultXPathSelectorFormat, methodNameResult), namespMan);
This search for the name report.bundle.operationResult, but by method named report.bundle.operationResponse.
And this
if (webMethodResult.FirstNode.NodeType == XmlNodeType.Element)
falls into an NullPointerException because the webMethodResult is null.
Hello @hemawst ,
After some digging here, I was able to figure out the basic problem.
First, I should say that in the case, we are using this issue to talk about two problems. One is related to the FaultException, which will probably be an improvement for the next version. The second one is related to the exception during the invoke.
This answer is most about the second question, as we already know the "problem" and even some solution to the first one.
The first thing that called my attention was that the returned XML does not follow the same structure as the class. Creating an instance and serializing it already produces a different result. (Name is report.bundle.operationResponseType
instead of report.bundle.operationResponse
)
<report.bundle.operationResponseType xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<DataArea>
<MEDIAkkordlohnWS>
<InfoMessage>Noch 1 Packen zum AG offen</InfoMessage>
</MEDIAkkordlohnWS>
</DataArea>
</report.bundle.operationResponseType>
This looks a little bit strange to me, as the element that is the return and the object have the same name. Because of that, the XPathSelectElement
is getting not only the object that is returned but the enclosed element too.
ACTUAL
<report.bundle.operationResponse>
<report.bundle.operationResponse>
<DataArea>
<MEDIAkkordlohnWS>
<OutData>0</OutData>
<InfoMessage>Noch 1 Packen zum AG offen</InfoMessage>
</MEDIAkkordlohnWS>
</DataArea>
</report.bundle.operationResponse>
</report.bundle.operationResponse>
EXPECTED
<report.bundle.operationResponse>
<DataArea>
<MEDIAkkordlohnWS>
<OutData>0</OutData>
<InfoMessage>Noch 1 Packen zum AG offen</InfoMessage>
</MEDIAkkordlohnWS>
</DataArea>
</report.bundle.operationResponse>
Because of this situation, all the following code will have some problems. The library is not prepared to have two elements with the same name. I was even thinking about some improvement where it could receive the name of the "result type," but given the fact that the name is the same from the enclosing type, it would not work.
Another point is that, as I said, the idea of the library is to use it without generating the classes from the WSDL, as they have a lot of garbage. So, because of the XmlTypeAttribute
that is on the reportbundleoperationResponseType
, the library does a substitution that will fail too.
What I can recommend to you:
1) if you have some control (or can report this situation), I would say to change the name of the returning "class." Change it from report.bundle.operationResponse
to report.bundle.operationResponseType
would fix part of the problem. I say part because this would mean that the improvement of informing the result type name would be necessary.
2) the library follows the pattern that the "result type" is always method name + Result
. If there is a possibility to change to this pattern, that will solve everything. This seems to be the general followed approach. To tell the truth is the first time I see a web service that does not follow it.
3) Latest recommendation: don't use the auto-generated classes from WSDL. They create a lot of things that are not really used and make some mess on the code. We have already a WsMapper attribute that you can use to have better property names on the project, mapping them to the properties returned by the web service.
I hope that the answer helps you. If you need some information, please, reach out.
Your answer helped me a lot to understand a lot better. Thank you for that and for the quick response. I am only surprised that "method name + Result" seems to be the standard, that I hear the first time. Unfortunately, the web service I have to use is not mine and I can not change it. Maybe you could add an additional parameter to the library, which makes this behavior more variable.
Your answer helped me a lot to understand a lot better. Thank you for that and for the quick response.
Happy to hear that!
I am only surprised that "method name + Result" seems to be the standard, that I hear the first time.
This is something that I saw basically everywhere, even that it's not a rule. If you create a web service in .net, for example, it will use this pattern automatically.
Unfortunately, the web service I have to use is not mine and I can not change it. Maybe you could add an additional parameter to the library, which makes this behavior more variable.
If you can not change it, the parameter to provide the name of the response will not work, as it still will have the same name two times. In that case, maybe the solution would be:
However, this still would require that you stop to use the XmlTypeAttribute
in the class, to not have problems when doing the substitution/deserialization.
Do you think that it would be a problem?
Ok, I think I'm just getting a bit of a headache. I can not quite understand what exactly happened there. I catch the responsReader (string result = responseReader.ReadToEnd ()
) and replace it with the (right) my one response and create with this the service.Result.SoapResponse XDocument.
<report.bundle.operationResponse>
<DataArea>
<MEDIAkkordlohnWS>
<OutData>0</OutData>
<InfoMessage>My message</InfoMessage>
</MEDIAkkordlohnWS>
</DataArea>
</report.bundle.operationResponse>
Then in the method "ExctracResults" an XmlNamespaceManager is created with the dummy prefix "guisgjkjtlcenzpabjfm" and the method name is added a "Result" (in my case a "Response").
Then the service.Result.SoapResponse is searched for the element "//guisgjkjtlcenzpabjfm:report.bundle.operationResponse".
XElement webMethodResult = service.Result.SoapResponse.XPathSelectElement (string.Format (StringConstants.XmlResultXPathSelectorFormat, methodNameResult), namespMan);
And that always gives me back null! How can the element "guisgjkjtlcenzpabjfm" be found in service.Result.SoapResponse? It does not exist.
I have a problem with understanding, I do not get it.
The point is that the response is not exactly what the library is expecting.
In the WebHelper, line 17, we are creating a "mapper" for the guisgjkjtlcenzpabjfm
to the service name that you have.
So, we kind of map this line from the response: <report.bundle.operationResponse xmlns="http://www.infor.com/businessinterface/MEDIAkkordlohnWS">
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<report.bundle.operationResponse xmlns="http://www.infor.com/businessinterface/MEDIAkkordlohnWS">
<report.bundle.operationResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="">
<DataArea>
<MEDIAkkordlohnWS>
<OutData>0</OutData>
<InfoMessage>Noch 1 Packen zum AG offen</InfoMessage>
</MEDIAkkordlohnWS>
</DataArea>
</report.bundle.operationResponse>
</report.bundle.operationResponse>
</S:Body>
</S:Envelope>
After the mapping, we try to find the "response object" inside this mapper. Problem is that we have the report.bundle.operationResponse
two times. This makes the XPathSelectElement
find the first one, which will cause subsequent problems.
"Right" way to fix the problem would be to "replace" the second report.bundle.operationResponse
with the "expected" name. If that happens (plus removing the XmlTypeAttribute from the class), the library will work as expected.
Hi, i use your very good project in my source. I call a soap services, the response give me an error. The exception is "The remote server returned an error: (500) ."
SOAPUI show we, that is a soap fault:
But in my application i can only get the error code "The remote server returned an error: (500) ." Is there a way to evaluate the soap fault in my application?
For a quick reply, I thank you in advance.