Skip to content

Commit 15d6898

Browse files
Merge pull request #815 from DigDes/develop
v1.1.0.25
2 parents dcbc2e5 + 3664de1 commit 15d6898

17 files changed

+290
-91
lines changed

README.md

+36
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,42 @@ public class MyService : IMyServiceService
192192
}
193193
}
194194
```
195+
#### Additional namespace declaration attributes in envelope
196+
Adding additional namespaces to the **SOAP Envelope** can be done by populating `SoapEncoderOptions.AdditionalEnvelopeXmlnsAttributes` parameter.
197+
```csharp
198+
....
199+
endpoints.UseSoapEndpoint<IService>(opt =>
200+
{
201+
opt.Path = "/ServiceWithAdditionalEnvelopeXmlnsAttributes.asmx";
202+
opt.AdditionalEnvelopeXmlnsAttributes = new Dictionary<string, string>()
203+
{
204+
{ "myNS", "http://schemas.someting.org" },
205+
{ "arr", "http://schemas.microsoft.com/2003/10/Serialization/Arrays" }
206+
};
207+
});
208+
...
209+
```
210+
This code will put `xmlns:myNS="...` and `xmlns:arr="...` attributes in `Envelope` and message will look like:
211+
```xml
212+
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" ... xmlns:myNS="http://schemas.someting.org" xmlns:arr="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
213+
...
214+
<myNS:StringList>
215+
<arr:string>Error: one</arr:string>
216+
<arr:string>Error: two</arr:string>
217+
</fin:StringList>
218+
...
219+
```
220+
instead of:
221+
```xml
222+
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" ... >
223+
...
224+
<d3p1:StringList xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
225+
<d4p1:string>Error: one</arr:string>
226+
<d4p1:string>Error: two</arr:string>
227+
</d3p1:StringList>
228+
...
229+
```
230+
195231
### Contributing
196232

197233
See [Contributing guide](CONTRIBUTING.md)

src/SoapCore.Tests/MessageContract/RawRequestSoap11Tests.cs

+24
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,30 @@ public async Task Soap11MessageContractCheckWSDLElementsComplexNotWrapped()
287287
}
288288
}
289289

290+
[TestMethod]
291+
public async Task SoapMessageContractWithAdditionalEnvelopeXmlnsAttributes()
292+
{
293+
const string body = @"<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"">
294+
<soapenv:Header/>
295+
<soapenv:Body/>
296+
</soapenv:Envelope>
297+
";
298+
299+
using (var host = CreateTestHost(typeof(TestService)))
300+
using (var client = host.CreateClient())
301+
using (var content = new StringContent(body, Encoding.UTF8, "text/xml"))
302+
using (var res = host.CreateRequest("/ServiceWithAdditionalEnvelopeXmlnsAttributes.asmx").AddHeader("SOAPAction", @"""EmptyRequest""").And(msg => msg.Content = content).PostAsync().Result)
303+
{
304+
res.EnsureSuccessStatusCode();
305+
var stream = await res.Content.ReadAsStreamAsync();
306+
XmlDocument doc = new XmlDocument();
307+
doc.Load(stream);
308+
XmlElement root = doc.DocumentElement;
309+
Assert.IsTrue(root.HasAttribute("xmlns:arr"));
310+
Assert.AreEqual(root.GetAttribute("xmlns:arr"), "http://schemas.microsoft.com/2003/10/Serialization/Arrays");
311+
}
312+
}
313+
290314
private TestServer CreateTestHost(Type serviceType)
291315
{
292316
var webHostBuilder = new WebHostBuilder()

src/SoapCore.Tests/MessageContract/Startup.cs

+8
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerF
4444
{
4545
x.UseSoapEndpoint(_serviceType, "/Service.svc", new SoapEncoderOptions(), SoapSerializer.DataContractSerializer);
4646
x.UseSoapEndpoint(_serviceType, "/Service.asmx", new SoapEncoderOptions(), SoapSerializer.XmlSerializer);
47+
x.UseSoapEndpoint(_serviceType, opt =>
48+
{
49+
opt.Path = "/ServiceWithAdditionalEnvelopeXmlnsAttributes.asmx";
50+
opt.AdditionalEnvelopeXmlnsAttributes = new Dictionary<string, string>()
51+
{
52+
{ "arr", "http://schemas.microsoft.com/2003/10/Serialization/Arrays" }
53+
};
54+
});
4755
});
4856
}
4957
#endif

src/SoapCore.Tests/RawRequestSoap11Tests.cs

+61
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Net.Http;
2+
using System.Net.Http.Headers;
23
using System.Text;
34
using System.Threading.Tasks;
45
using Microsoft.AspNetCore.Hosting;
@@ -55,6 +56,66 @@ public async Task Soap11Ping()
5556
}
5657
}
5758

59+
[TestMethod]
60+
public void Soap11PingWithMixedNamespacing()
61+
{
62+
var pingValue = "Lorem ipsum";
63+
var body = $@"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""no""?>
64+
<SOAP-ENV:Envelope xmlns:SOAPSDK1=""http://www.w3.org/2001/XMLSchema""
65+
xmlns:SOAPSDK2=""http://www.w3.org/2001/XMLSchema-instance""
66+
xmlns:SOAPSDK3=""http://schemas.xmlsoap.org/soap/encoding/""
67+
xmlns:SOAP-ENV=""http://schemas.xmlsoap.org/soap/envelope/"">
68+
<SOAP-ENV:Body SOAP-ENV:encodingStyle=""http://schemas.xmlsoap.org/soap/encoding/"">
69+
<SOAPSDK4:Ping xmlns:SOAPSDK4=""http://tempuri.org/"">
70+
<s>{pingValue}</s>
71+
</SOAPSDK4:Ping>
72+
</SOAP-ENV:Body>
73+
</SOAP-ENV:Envelope>
74+
";
75+
using (var host = CreateTestHost())
76+
using (var client = host.CreateClient())
77+
using (var content = new StringContent(body, Encoding.UTF8, "text/xml"))
78+
using (var res = host.CreateRequest("/Service.svc").AddHeader("SOAPAction", @"""Ping""").And(msg => msg.Content = content).PostAsync().Result)
79+
{
80+
res.EnsureSuccessStatusCode();
81+
82+
var response = res.Content.ReadAsStringAsync().Result;
83+
Assert.IsTrue(response.Contains(pingValue));
84+
}
85+
}
86+
87+
[TestMethod]
88+
public async Task Soap11PingInMultipart()
89+
{
90+
var pingValue = "abc";
91+
var soapBody = $@"<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"">
92+
<soapenv:Body>
93+
<Ping xmlns=""http://tempuri.org/"">
94+
<s>{pingValue}</s>
95+
</Ping>
96+
</soapenv:Body>
97+
</soapenv:Envelope>";
98+
99+
using var host = CreateTestHost();
100+
using var client = host.CreateClient();
101+
using var multipartContent = new MultipartContent("related");
102+
multipartContent.Headers.ContentType!.Parameters.Add(new NameValueHeaderValue("type", "\"text/xml\""));
103+
104+
using var soapContent = new StringContent(soapBody, Encoding.UTF8, "text/xml");
105+
soapContent.Headers.Add("SOAPAction", @"""Ping""");
106+
107+
using var extraContent = new StringContent("some text payload", Encoding.UTF8, "text/plain");
108+
109+
multipartContent.Add(soapContent);
110+
multipartContent.Add(extraContent);
111+
112+
using var res = await host.CreateRequest("/Service.svc").And(msg => msg.Content = multipartContent).PostAsync();
113+
114+
res.EnsureSuccessStatusCode();
115+
var response = await res.Content.ReadAsStringAsync();
116+
Assert.IsTrue(response.Contains(pingValue));
117+
}
118+
58119
private TestServer CreateTestHost()
59120
{
60121
var webHostBuilder = new WebHostBuilder()

src/SoapCore.Tests/Startup.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerF
7373
{
7474
app.UseRouting();
7575

76-
app.UseWhen(ctx => ctx.Request.Headers.ContainsKey("SOAPAction"), app2 =>
76+
app.UseWhen(ctx => ctx.Request.Headers.ContainsKey("SOAPAction") || ctx.Request.ContentType.StartsWith("multipart"), app2 =>
7777
{
7878
app2.UseRouting();
7979

@@ -93,7 +93,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerF
9393
});
9494
});
9595

96-
app.UseWhen(ctx => !ctx.Request.Headers.ContainsKey("SOAPAction"), app2 =>
96+
app.UseWhen(ctx => !ctx.Request.Headers.ContainsKey("SOAPAction") && !ctx.Request.ContentType.StartsWith("multipart"), app2 =>
9797
{
9898
app2.UseRouting();
9999

src/SoapCore.Tests/Wsdl/Services/ArrayService.cs

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public class EnumerableResponse
2727
public IEnumerable<long?> LongNullableEnumerable { get; set; }
2828
public IEnumerable<long> LongEnumerable { get; set; }
2929
public IEnumerable<IEnumerable<long>> LongEnumerableEnumerable { get; set; }
30+
public IEnumerable<IEnumerable<string>> StringEnumerableEnumerable { get; set; }
3031
}
3132

3233
[MessageContract]
@@ -35,5 +36,6 @@ public class ArrayRequest
3536
public long?[] LongNullableArray { get; set; }
3637
public long[] LongArray { get; set; }
3738
public long[][] LongArrayArray { get; set; }
39+
public List<List<string>> StringListList { get; set; }
3840
}
3941
}

src/SoapCore.Tests/Wsdl/WsdlTests.cs

+6
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,9 @@ public async Task CheckArrayServiceWsdl()
585585
var arrayArray = root.XPathSelectElement("//xsd:complexType[@name='ArrayRequest']/xsd:sequence/xsd:element[@name='LongArrayArray' and @type='tns:ArrayOfArrayOfLong' and @nillable='true']", nm);
586586
Assert.IsNotNull(arrayArray);
587587

588+
var stringListList = root.XPathSelectElement("//xsd:complexType[@name='ArrayRequest']/xsd:sequence/xsd:element[@name='StringListList' and @type='tns:ArrayOfArrayOfString' and @nillable='true']", nm);
589+
Assert.IsNotNull(stringListList);
590+
588591
var nullableEnumerable = root.XPathSelectElement("//xsd:complexType[@name='EnumerableResponse']/xsd:sequence/xsd:element[@name='LongNullableEnumerable' and @type='tns:ArrayOfNullableLong' and @nillable='true']", nm);
589592
Assert.IsNotNull(nullableEnumerable);
590593

@@ -593,6 +596,9 @@ public async Task CheckArrayServiceWsdl()
593596

594597
var enumerableEnumberable = root.XPathSelectElement("//xsd:complexType[@name='EnumerableResponse']/xsd:sequence/xsd:element[@name='LongEnumerableEnumerable' and @type='tns:ArrayOfArrayOfLong' and @nillable='true']", nm);
595598
Assert.IsNotNull(enumerableEnumberable);
599+
600+
var stringEnumerableEnumberable = root.XPathSelectElement("//xsd:complexType[@name='EnumerableResponse']/xsd:sequence/xsd:element[@name='StringEnumerableEnumerable' and @type='tns:ArrayOfArrayOfString' and @nillable='true']", nm);
601+
Assert.IsNotNull(stringEnumerableEnumberable);
596602
}
597603

598604
[TestMethod]

src/SoapCore.Tests/WsdlFromFile/WsdlTests.cs

+15-9
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,21 @@
44
using System.IO;
55
using System.Linq;
66
using System.Net.Http;
7-
using System.ServiceModel.Channels;
87
using System.Threading;
9-
using System.Threading.Tasks;
108
using System.Xml;
119
using System.Xml.Linq;
12-
using System.Xml.XPath;
10+
using System.Xml.Schema;
1311
using Microsoft.AspNetCore.Hosting;
1412
using Microsoft.AspNetCore.Hosting.Server.Features;
1513
using Microsoft.Extensions.DependencyInjection;
1614
using Microsoft.VisualStudio.TestTools.UnitTesting;
17-
using Shouldly;
18-
using SoapCore.MessageEncoder;
19-
using SoapCore.Meta;
20-
using SoapCore.ServiceModel;
2115
using SoapCore.Tests.WsdlFromFile.Services;
2216

2317
namespace SoapCore.Tests.WsdlFromFile
2418
{
2519
[TestClass]
2620
public class WsdlTests
2721
{
28-
private readonly XNamespace _xmlSchema = "http://www.w3.org/2001/XMLSchema";
29-
3022
private IWebHost _host;
3123

3224
[TestMethod]
@@ -37,6 +29,13 @@ public void CheckWSDLExists()
3729
Trace.TraceInformation(wsdl);
3830
Assert.IsNotNull(wsdl);
3931
StopServer();
32+
33+
var root = new XmlDocument();
34+
root.LoadXml(wsdl);
35+
var nsmgr = new XmlNamespaceManager(root.NameTable);
36+
nsmgr.AddNamespace("wsdl", "http://schemas.xmlsoap.org/wsdl/");
37+
var element = root.SelectSingleNode("/wsdl:definitions", nsmgr);
38+
Assert.IsNotNull(element);
4039
}
4140

4241
[TestMethod]
@@ -47,6 +46,13 @@ public void CheckXSDExists()
4746
Trace.TraceInformation(xsd);
4847
Assert.IsNotNull(xsd);
4948
StopServer();
49+
50+
XmlSchema.Read(new XmlTextReader(new StringReader(xsd)), ValidationCallback);
51+
52+
static void ValidationCallback(object sender, ValidationEventArgs args)
53+
{
54+
Assert.Fail(args.Message);
55+
}
5056
}
5157

5258
[TestMethod]

src/SoapCore/CustomMessage.cs

+10
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ public CustomMessage(Message message)
1818

1919
public XmlNamespaceManager NamespaceManager { get; internal set; }
2020

21+
public System.Collections.Generic.Dictionary<string, string> AdditionalEnvelopeXmlnsAttributes { get; internal set; }
22+
2123
public override MessageHeaders Headers => Message.Headers;
2224

2325
public override MessageProperties Properties => Message.Properties;
@@ -40,6 +42,14 @@ protected override void OnWriteStartEnvelope(XmlDictionaryWriter writer)
4042

4143
var xsiPrefix = Namespaces.AddNamespaceIfNotAlreadyPresentAndGetPrefix(NamespaceManager, "xsi", Namespaces.XMLNS_XSI);
4244
writer.WriteXmlnsAttribute(xsiPrefix, Namespaces.XMLNS_XSI);
45+
46+
if (AdditionalEnvelopeXmlnsAttributes != null)
47+
{
48+
foreach (var rec in AdditionalEnvelopeXmlnsAttributes)
49+
{
50+
writer.WriteXmlnsAttribute(rec.Key, rec.Value);
51+
}
52+
}
4353
}
4454

4555
protected override void OnWriteStartBody(XmlDictionaryWriter writer)

src/SoapCore/Meta/ClrTypeResolver.cs

+4
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ public static string ResolveOrDefault(string typeName)
4242
return "string";
4343
case "Byte[]":
4444
return "base64Binary";
45+
#if NET6_0_OR_GREATER
46+
case "DateOnly":
47+
return "date";
48+
#endif
4549
}
4650

4751
return null;

src/SoapCore/Meta/MetaBodyWriter.cs

+5-1
Original file line numberDiff line numberDiff line change
@@ -1064,7 +1064,11 @@ private void AddSchemaType(XmlDictionaryWriter writer, TypeToBuild toBuild, stri
10641064
writer.WriteXmlnsAttribute(ns, Namespaces.ARRAYS_NS);
10651065
writer.WriteAttributeString("name", name);
10661066
WriteQualification(writer, isUnqualified);
1067-
writer.WriteAttributeString("nillable", "true");
1067+
1068+
if (!isArray)
1069+
{
1070+
writer.WriteAttributeString("nillable", "true");
1071+
}
10681072

10691073
writer.WriteAttributeString("type", $"{ns}:{newTypeToBuild.TypeName}");
10701074

src/SoapCore/Meta/MetaMessage.cs

-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ protected override void OnWriteStartEnvelope(XmlDictionaryWriter writer)
5858
throw new ArgumentOutOfRangeException(nameof(Version), "Unsupported MessageVersion encountered while writing envelope.");
5959
}
6060

61-
_xmlNamespaceManager.AddNamespace("tns", _service.GeneralContract.Namespace);
6261
WriteXmlnsAttribute(writer, _service.GeneralContract.Namespace);
6362
WriteXmlnsAttribute(writer, Namespaces.XMLNS_XSD);
6463
WriteXmlnsAttribute(writer, Namespaces.HTTP_NS);

src/SoapCore/SoapCore.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<Description>SOAP protocol middleware for ASP.NET Core</Description>
5-
<Version>1.1.0.24</Version>
5+
<Version>1.1.0.25</Version>
66
<Authors>Digital Design</Authors>
77
<TargetFrameworks>netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0</TargetFrameworks>
88
<PackageId>SoapCore</PackageId>

src/SoapCore/SoapCoreOptions.cs

+5
Original file line numberDiff line numberDiff line change
@@ -99,5 +99,10 @@ public class SoapCoreOptions
9999
public XmlNamespaceManager XmlNamespacePrefixOverrides { get; set; }
100100

101101
public WsdlFileOptions WsdlFileOptions { get; set; }
102+
103+
/// <summary>
104+
/// Sets additional namespace declaration attributes in envelope
105+
/// </summary>
106+
public Dictionary<string, string> AdditionalEnvelopeXmlnsAttributes { get; set; }
102107
}
103108
}

src/SoapCore/SoapEndpointExtensions.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -338,8 +338,8 @@ public static IEndpointConventionBuilder UseSoapEndpoint<T_MESSAGE>(this IEndpoi
338338
.UseMiddleware<SoapEndpointMiddleware<T_MESSAGE>>(soapOptions)
339339
.Build();
340340

341-
routes.Map(soapOptions.Path + "/$metadata", pipeline);
342-
routes.Map(soapOptions.Path + "/mex", pipeline);
341+
routes.Map(soapOptions.Path?.TrimEnd('/') + "/$metadata", pipeline);
342+
routes.Map(soapOptions.Path?.TrimEnd('/') + "/mex", pipeline);
343343

344344
return routes.Map(soapOptions.Path, pipeline)
345345
.WithDisplayName("SoapCore");

0 commit comments

Comments
 (0)