Skip to content

Commit

Permalink
Merge branch 'develop' into fix-data-contract-name
Browse files Browse the repository at this point in the history
  • Loading branch information
navozenko committed Jun 25, 2024
2 parents f934c24 + 17b862f commit 40447ca
Show file tree
Hide file tree
Showing 12 changed files with 433 additions and 89 deletions.
68 changes: 57 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ There are 2 different ways of adding SoapCore to your ASP.NET Core website. If y

In Startup.cs:


```csharp
public void ConfigureServices(IServiceCollection services)
{
Expand Down Expand Up @@ -65,6 +64,37 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF
}
```

### Using with custom implementation of Serialization

There is an optional feature included where you can implment the ISoapCoreSerializer to built your own custom serializar for body.

In Startup.cs:

```csharp
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSoapCore();
services.TryAddSingleton<ServiceContractImpl>();
services.AddCustomSoapMessageSerializer<CustomeBodyMessageSerializerImpl>(); //Add Your Custom Implementation or Extend Default Serializer
services.AddMvc();
...
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseSoapEndpoint<ServiceContractImpl>(soapCoreOptions =>
{
soapCoreOptions.Path = "/ServicePath.asmx";
soapCoreOptions.UseCustomSerializer<CustomeBodyMessageSerializerImpl>(); //Specify the Service to Use Service Soap Message Serializer
soapCoreOptions.SoapSerializer = SoapSerializer.DataContractSerializer;
...
});
}

```

### Using with legacy WCF/WS

It is possible to use SoapCore with .NET legacy WCF and Web Services, both as client and service.
Expand Down Expand Up @@ -108,7 +138,6 @@ To read the setting you can do the following

In Startup.cs:


```csharp
var settings = Configuration.GetSection("FileWSDL").Get<WsdlFileOptions>();

Expand All @@ -125,21 +154,23 @@ If the WsdFileOptions parameter is supplied then this feature is enabled / used.

### References

* [stackify.com/soap-net-core](https://stackify.com/soap-net-core/)
- [stackify.com/soap-net-core](https://stackify.com/soap-net-core/)

### Tips and Tricks

#### Extending the pipeline

In your ConfigureServices method, you can register some additional items to extend the pipeline:
* services.AddSoapMessageInspector() - add a custom MessageInspector. This function is similar to the `IDispatchMessageInspector` in WCF. The newer `IMessageInspector2` interface allows you to register multiple inspectors, and to know which service was being called.
* services.AddSingleton<MyOperatorInvoker>() - add a custom OperationInvoker. Similar to WCF's `IOperationInvoker` this allows you to override the invoking of a service operation, commonly to add custom logging or exception handling logic around it.
* services.AddSoapMessageProcessor() - add a custom SoapMessageProcessor. Similar to ASP.NET Cores middlewares, this allows you to inspect the message on the way in and out. You can also short-circuit the message processing and return your own custom message instead. Inspecting and modifying HttpContext is also possible

- services.AddSoapMessageInspector() - add a custom MessageInspector. This function is similar to the `IDispatchMessageInspector` in WCF. The newer `IMessageInspector2` interface allows you to register multiple inspectors, and to know which service was being called.
- services.AddSingleton<MyOperatorInvoker>() - add a custom OperationInvoker. Similar to WCF's `IOperationInvoker` this allows you to override the invoking of a service operation, commonly to add custom logging or exception handling logic around it.
- services.AddSoapMessageProcessor() - add a custom SoapMessageProcessor. Similar to ASP.NET Cores middlewares, this allows you to inspect the message on the way in and out. You can also short-circuit the message processing and return your own custom message instead. Inspecting and modifying HttpContext is also possible

#### Using ISoapMessageProcessor()

```csharp
//Add this to ConfigureServices in Startup.cs
services.AddSoapMessageProcessor(async (message, httpcontext, next) =>
{
var bufferedMessage = message.CreateBufferedCopy(int.MaxValue);
Expand All @@ -155,7 +186,7 @@ services.AddSoapMessageProcessor(async (message, httpcontext, next) =>
var responseMessage = await next(message);

//Inspect and modify the contents of returnMessage in the same way as the incoming message.
//finish by returning the modified message.
//finish by returning the modified message.
return responseMessage;
});
Expand All @@ -167,9 +198,10 @@ Use interface IServiceOperationTuner to tune each operation call.

Create class that implements IServiceOperationTuner.
Parameters in Tune method:
* httpContext - current HttpContext. Can be used to get http headers or body.
* serviceInstance - instance of your service.
* operation - information about called operation.

- httpContext - current HttpContext. Can be used to get http headers or body.
- serviceInstance - instance of your service.
- operation - information about called operation.

```csharp
public class MyServiceOperationTuner : IServiceOperationTuner
Expand Down Expand Up @@ -229,8 +261,11 @@ public class MyService : IMyServiceService
}
}
```

#### Additional namespace declaration attributes in envelope

Adding additional namespaces to the **SOAP Envelope** can be done by populating `SoapEncoderOptions.AdditionalEnvelopeXmlnsAttributes` parameter.

```csharp
....
endpoints.UseSoapEndpoint<IService>(opt =>
Expand All @@ -244,7 +279,9 @@ endpoints.UseSoapEndpoint<IService>(opt =>
});
...
```

This code will put `xmlns:myNS="...` and `xmlns:arr="...` attributes in `Envelope` and message will look like:

```xml
<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">
...
Expand All @@ -254,7 +291,9 @@ This code will put `xmlns:myNS="...` and `xmlns:arr="...` attributes in `Envelop
</fin:StringList>
...
```

instead of:

```xml
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" ... >
...
Expand All @@ -264,12 +303,19 @@ instead of:
</d3p1:StringList>
...
```
### Not implemented
WCF and legacy WebService supports many scenarios and lots of attributes. SoapCore only supports the most common patterns.
Stuff that are not supported includes:
* XmlIncludeAttribute/SoapIncludeAttribute
* SoapDocumentMethodAttribute
*

### Contributing

See [Contributing guide](CONTRIBUTING.md)

### Contributors

<a href="https://github.com/digdes/soapcore/graphs/contributors">
<img src="https://contributors-img.web.app/image?repo=digdes/soapcore" />
</a>
Expand Down
4 changes: 2 additions & 2 deletions samples/Sample.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30717.126
# Visual Studio Version 17
VisualStudioVersion = 17.7.34031.279
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Client", "Client\Client.csproj", "{EBFDF95D-F7CC-457F-8924-3CB6069FBA3B}"
EndProject
Expand Down
75 changes: 71 additions & 4 deletions src/SoapCore.Tests/InvalidXMLTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public async Task MissingNamespace()
SoapSerializer = SoapSerializer.DataContractSerializer
};

var soapCore = new SoapEndpointMiddleware<CustomMessage>(logger, (innerContext) => Task.CompletedTask, options);
var soapCore = new SoapEndpointMiddleware<CustomMessage>(logger, (innerContext) => Task.CompletedTask, options, serviceCollection.BuildServiceProvider());

var context = new DefaultHttpContext();
context.Request.Path = new PathString("/Service.svc");
Expand All @@ -63,7 +63,7 @@ public async Task MissingNamespace()
context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(request), false);
context.Request.ContentType = "text/xml; charset=utf-8";

await soapCore.Invoke(context, serviceCollection.BuildServiceProvider());
await soapCore.Invoke(context);

// Assert
Assert.IsTrue(context.Response.Body.Length > 0);
Expand Down Expand Up @@ -96,7 +96,9 @@ public async Task DuplicatedElement()
SoapSerializer = SoapSerializer.XmlSerializer
};

var soapCore = new SoapEndpointMiddleware<CustomMessage>(logger, (innerContext) => Task.CompletedTask, options);
var serviceProvider = serviceCollection.BuildServiceProvider();

var soapCore = new SoapEndpointMiddleware<CustomMessage>(logger, (innerContext) => Task.CompletedTask, options, serviceProvider);

var context = new DefaultHttpContext();
context.Request.Path = new PathString("/Service.asmx");
Expand All @@ -117,7 +119,72 @@ public async Task DuplicatedElement()
context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(request), false);
context.Request.ContentType = "text/xml; charset=utf-8";

await soapCore.Invoke(context, serviceCollection.BuildServiceProvider());
await soapCore.Invoke(context);

// Assert
context.Response.Body.Seek(0, SeekOrigin.Begin);
using var response = new StreamReader(context.Response.Body, Encoding.UTF8);
var body = await response.ReadToEndAsync();
Assert.IsTrue(body.Contains("<TestResult>a c</TestResult>"));
}

[DataTestMethod]
[DataRow("application/soap+xml; charset=utf-8; action=\"Request\"")]
[DataRow("application/soap+xml; charset=utf-8; action=\"Request/\"")]
[DataRow("application/soap+xml; charset=utf-8; action=\"testRequest/\"")]
[DataRow("application/soap+xml; charset=utf-8; action=\"TESTRequest/\"")]
[DataRow("application/soap+xml; charset=utf-8; action=\"/\"")]
[DataRow("application/soap+xml; charset=utf-8; action=\"https://dos.brianfeucht.com/test/\"")]
[DataRow("application/soap+xml; charset=utf-8; action=\"https://dos.brianfeucht.com/test\"")]
[DataRow("application/soap+xml; charset=utf-8; action=\"https://dos.brianfeucht.com/TEST\"")]
[DataRow("application/soap+xml; charset=utf-8; action=\"https://dos.brianfeucht.com/Test\"")]
public async Task InvalidSoapActionContentType(string contentType)
{
// Arrange
var logger = NullLoggerFactory.Instance.CreateLogger<SoapEndpointMiddleware<CustomMessage>>();

var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<DuplicatedElementService>();
serviceCollection.AddSoapCore();

var options = new SoapOptions()
{
Path = "/Service.asmx",
EncoderOptions = new[]
{
new SoapEncoderOptions
{
MessageVersion = MessageVersion.Soap12WSAddressing10,
WriteEncoding = Encoding.UTF8,
ReaderQuotas = XmlDictionaryReaderQuotas.Max
}
},
ServiceType = typeof(DuplicatedElementService),
SoapModelBounder = new MockModelBounder(),
SoapSerializer = SoapSerializer.XmlSerializer
};

var soapCore = new SoapEndpointMiddleware<CustomMessage>(logger, _ => Task.CompletedTask, options, serviceCollection.BuildServiceProvider());

var context = new DefaultHttpContext();
context.Request.Path = new PathString("/Service.asmx");
context.Request.Method = "POST";
context.Response.Body = new MemoryStream();

// Act
var request = @"
<soap12:Envelope xmlns:soap12=""http://www.w3.org/2003/05/soap-envelope"" xmlns:rlx=""https://dos.brianfeucht.com/"">
<soap12:Body>
<rlx:Test>
<eventRef>a</eventRef>
<other>c</other>
</rlx:Test>
</soap12:Body>
</soap12:Envelope>";
context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(request), false);
context.Request.ContentType = contentType;

await soapCore.Invoke(context);

// Assert
context.Response.Body.Seek(0, SeekOrigin.Begin);
Expand Down
Loading

0 comments on commit 40447ca

Please sign in to comment.