DigDes / SoapCore

SOAP extension for ASP.NET Core
MIT License
977 stars 369 forks source link

Empty attributes namespace with parent node namespace #1066

Open ach1819 opened 1 month ago

ach1819 commented 1 month ago

Hello,

I'm creating an API to expose as a SOAP service, and I need to comply with a structure for my request.

<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
    <Body>
        <Operation xmlns="https://example.com">
            <fileName xmlns="">[string?]</fileName>
            <contentFile xmlns="">[base64Binary?]</contentFile>
        </Operation>
    </Body>
</Envelope>

As you can see I have an Operation node with a namespace, this node has two attributes with an empty namespace.

<fileName xmlns="">[string?]</fileName>
<contentFile xmlns="">[base64Binary?]</contentFile>

Trying to fit with this request my code looks like,

my files

app
.UseRouting()
.UseEndpoints(endpoint =>
{
    endpoint.UseSoapEndpoint<IMyService>(
    "/my-custom-path/myService.svc", 
    new SoapEncoderOptions(), SoapSerializer.DataContractSerializer);
});

[ServiceContract(Name = "MyService", Namespace = "https://example.com")]
public interface IMyService
{
   [OperationContract(Name = "Operation")]
   Task<IActionResult> PostAsync(SendBillRequest sendBillRequestFields);
}

[DataContract(Namespace = "https://should-be-empty")]
public class PostRequestFields 
{
   [DataMember]
   public string FileName { get; set; }

   [DataMember]
   public string ContentFile { get; set; }
}

[DataContract(Name = "Operation", Namespace = "https://example.com")]
public class PostRequest : PostRequestFields { }

public partial class MyService : IMyService
{

   public Task<IActionResult> PostAsync(PostRequest postRequest)
   {
         return Task.FromResult<IActionResult>(new OkResult());
   }
}

The problems come here

With the above config I got,

<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
    <Body>
        <Operation xmlns="https://example.com">
            <ContentFile xmlns="https://should-be-empty">[string?]</ContentFile>
            <FileName xmlns="https://should-be-empty">[string?]</FileName>
        </Operation>
    </Body>
</Envelope>

which is almost close, the only problem I have it's I can't set an empty namespace if I try this

[DataContract(Namespace = "")]
public class PostRequestFields 
{
   [DataMember]
   public string FileName { get; set; }

   [DataMember]
   public string ContentFile { get; set; }
}

I got an error,

An unhandled exception occurred while processing the request.

ArgumentException: Cannot use a prefix with an empty namespace. System.Xml.XmlWellFormedWriter.WriteEndAttribute()

if I try with namespace as null

[DataContrect(Namespace = null)]

I got,

An unhandled exception occurred while processing the request.

ArgumentNullException: Value cannot be null. (Parameter 'key') System.Collections.Generic.Dictionary<TKey, TValue>.TryInsert(TKey key, TValue value, InsertionBehavior behavior)

Do you know how may I achieve my goal?

andersjonsson commented 3 weeks ago

The namespace from DataContractAttribute is only used in the wsdl, so I don't think that is the way to go. You are more than welcome to debug your way through the source to see if you can figure out a nice way to support this scenario.

Otherwise you can use an ISoapMessageProcessor so remove the namespaces from the messages on the way out.

ach1819 commented 3 weeks ago

Thank you very much for your response.

I tried with ISoapMessageProcessor but I can't control the serializer before the service creation that allows to map the wsdl. I think I'm going to make the debug.