DigDes / SoapCore

SOAP extension for ASP.NET Core
MIT License
991 stars 372 forks source link

xmlReader reads wrong start element? #117

Closed tomskarasha closed 3 years ago

tomskarasha commented 6 years ago

Hi! I have an issue on SoapCore reading XML body it's first element! Here is the XML I'm using:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:a="https://someAddress.com/address/AddressSOME" xmlns:b="https://someAddress.com/objectos/ObjectSome">
   <s:Header>
   </s:Header>
   <s:Body>
      <a:someRequest>
         <b:SomeObject>
            <b:DataOne>Once upon a time</b:DataOne>
        </b:SomeObject>
      </a:someRequest>
    </s:Body>
</s:Envelope>

And these are the models:

    [DebuggerStepThrough]
    [DesignerCategory("code")]
    [GeneratedCode("xsd", "4.0.30319.17929")]
    [XmlRoot(Namespace = "https://someAddress.com/address/AddressSOME", IsNullable = false)]
    [XmlType(AnonymousType = true, Namespace = "https://someAddress.com/address/AddressSOME")]
    public class someRequest
    {
        public someRequest();
        [XmlElement(Namespace = "https://someAddress.com/objectos/ObjectSome")]
        public SomeObject SomeObject { get; set; }
    }

    [DebuggerStepThrough]
    [DesignerCategory("code")]
    [GeneratedCode("xsd", "4.0.30319.17929")]
    [XmlRoot(Namespace = "https://someAddress.com/objectos/ObjectSome", IsNullable = false)]
    [XmlType(AnonymousType = true, Namespace = "https://someAddress.com/objectos/ObjectSome")]
    public class SomeObject
    {
        public SomeObject ();
        public string DataOne{ get; set; }
    }

My service interface:

    [ServiceContract(Namespace = "https://someAddress.com/address/AddressSOME")]
    public interface ISomeRequestService
    {
        [OperationContract(Action = "https://actionos.com/action/Operation", ReplyAction = "*")]
        ObjectResponse SomeRequest(someRequest someRequest);
    }

It calls service just fine, but the someRequest is null, always null!

Then I started to debug and turns out that it reads wrong start element in: xmlReader.ReadStartElement(operation.Name, operation.Contract.Namespace); xmlReader thinks that SomeObject is the start element, in someRequest xml tag it is, but in the body it's not. I think that it should read/take the first, after body not whats in someRequest because someRequest is first element which needs to be taken.

When I comment out this line: // xmlReader.ReadStartElement(operation.Name, operation.Contract.Namespace); Works like a charm, read, deserealized, delivered to operation!

What do you think guys @kotovaleksandr @DmitryBryluk is it fixable different way or not?

DmitryBryluk commented 6 years ago

try previous versions of SoapCore, maybe there is something broken with new fixes, still exploring

also you may perform some exploration in SoapEndpointMiddleware near mstm.Seek(0, SeekOrigin.Begin); replace it with:

            mstm.Seek(0, SeekOrigin.Begin);
            Console.WriteLine(Encoding.UTF8.GetString(mstm.ToArray()));
            mstm.Seek(0, SeekOrigin.Begin);

so you will be able to view received xml, and try sending from .net client to see what xml it have created, to be sure there no differences

in GetRequestArguments you may experiment with xml order, to rewrite it as commented in my branch

there are serialization tests here, mostly XmlSerialization is working (seems except your case), so you may try to experiment with

tomskarasha commented 6 years ago

Did as you told, and received XML as expected with all its elements and right order. Ou snaaap, according to documentation when this ReadStartElement() is called it moves to next element and when it comes to IsStartElement then this xmlReader shows incorrect element but in OperationDescription is correct, so when it comes to IsStartElement its false and class object parameter is empty. :/ hmm... In my opinion there is no need for ReadStartElement() as it jumps forward to next than its compared in IsStartElement to operations

DmitryBryluk commented 6 years ago

some fixes here I implemented handling of [MessageContract] attribute for request/response, so in provided samples (see tests) it is working. also with these tests you can easier explore what is happening.

DmitryBryluk commented 6 years ago

completed, merged to master. try it now and see extended samples under serialization tests

tomskarasha commented 6 years ago

Yeah, I see, good solution for issue. Only thing is that I can't change that contract to add that MessageContract attribute, request contract is generated from XSDs into dll that comes from other system(s) and I cant change that! Is there a possibility to add/mark it somehow differently?

hmm... I'm not sure how legit that is but I created some sort of a wrapper with MessageContract attribute:

    [MessageContract(WrapperName = nameof(someRequest))]
    public class someRequest : GeneratedAllContractdll.someRequest
    {
    }

sort of works! :/

DmitryBryluk commented 6 years ago

Is there a possibility to add/mark it somehow differently?

you may explore and make solution (may be check for XmlRoot attribute together with MessageContract?). idea with inheritance is also good

jrummell commented 5 years ago

I added a PR that fixes this issue for me. #239

github-actions[bot] commented 3 years ago

This issue is stale because it has been open for 30 days with no activity.

github-actions[bot] commented 3 years ago

This issue was closed because it has been inactive for 14 days since being marked as stale.