The contract can be created in the same way a contract would be created in legacy WCF using .Net Framework. The implementation is simpler because there is no need for the ServiceBehavior attribute.
[ServiceContract]
public interface IEchoServiceContract
{
[OperationContract]
Task<string> EchoAsync(string value);
}
public class EchoService : IEchoServiceContract
{
public Task<string> EchoAsync(string value) => Task.FromResult(value);
}
Configuring the service is quite simple, as only vanilla AspNetCore is required. Mvc is not needed.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services
.AddRouting()
.AddSingletonSoapService<IEchoServiceContract, EchoService>()
;
}
public void Configure(IApplicationBuilder builder)
{
builder
.UseRouting()
.UseEndpoints(endpoints => endpoints.MapSoapService<IEchoServiceContract>("/echo", MessageVersion.Soap11))
;
}
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingletonSoapService<IEchoServiceContract, EchoService>();
}
public void Configure(IApplicationBuilder builder)
{
builder.MapSoapService<IEchoServiceContract>("/echo", MessageVersion.Soap11);
}
}
Middleware can be added in the MapSoapService methods, both for endpoint routing in AspNetCore 3.1 and middleware in AspNetCore 2.1. This can be done to, for example, handle custom SOAP headers. Any AspNetCore middleware can be used as well as middleware that implements either our SoapMiddleware or SoapHeaderMiddleware classes.
public void Configure(IApplicationBuilder builder)
{
builder
.UseRouting()
.UseEndpoints(endpoints =>
{
endpoints.MapSoapService<IEchoServiceContract>(
"/echo",
MessageVersion.Soap11,
// This will be used after the SOAP request has been accepted.
// This will be used before all SOAP headers must be understood and before the service method has been invoked.
b => b.AddMiddleware<MyCustomMiddleware>()
);
})
;
}
As you can see above, we are not using System.ServiceModel.Channels.Binding on the server side. We felt that it was unnecessary. If you're going to host the service with https, why would you need to program that in? Just host with https!
Since we don't have wsdl generation yet, channels should be created manually using either code or config. Thankfully, there is nothing custom about this. In c#, this is just vanilla WCF.
// The binding could also be BasicHttpsBinding
var binding = new BasicHttpBinding();
var baseUrl = new Uri("http://localhost:5000");
var url = new Uri(baseUrl, "echo");
var endpointAddress = new EndpointAddress(url);
var factory = new ChannelFactory<IEchoServiceContract>(binding, endpointAddress);
var channel = factory.CreateChannel();
The BasicHttpBinding and BasicHttpsBinding use Soap11 as their default message version, which is why the example above uses MessageVersion.Soap11. However, if the service and/or endpoint is defined using a different message version in the Configure method of the Startup class, this can be changed manually in the client binding aswell.
var binding = new CustomBinding(new BasicHttpsBinding())
{
};
var encoding = binding.Elements.Find<TextMessageEncodingBindingElement>();
encoding.MessageVersion = MessageVersion.Soap12WSAddressing10;
var baseUrl = new Uri("http://localhost:5000");
var url = new Uri(baseUrl, "echo");
var endpointAddress = new EndpointAddress(url);
var factory = new ChannelFactory<IEchoServiceContract>(binding, endpointAddress);
var channel = factory.CreateChannel();