I want to use this WcfCoreMtomEncoder lib here in my .Net Core project but I'm not sure how to use it syntactically.
I have this code below but can't use MessageEncoding because I'm in a .Net Core project (no mtom support):
var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport)
{
// MessageEncoding = WSMessageEncoding.Mtom, not supported in .Net Core
TransferMode = TransferMode.Streamed
};
EndpointAddress endpoint = new EndpointAddress(url);
var channelFactory = new ChannelFactory<T>(binding, endpoint);
var webService = channelFactory.CreateChannel();
user.UserName = await webService.EncryptValueAsync(userName);
user.Password = await webService.EncryptValueAsync(password);
var documentAddResult = webService.DocumentAdd(document);
channelFactory.Close();
From what I read I can replace it with this library code below and I see from the documentation for the encoder lib that the usage looks like this:
var encoding = new MtomMessageEncoderBindingElement(new TextMessageEncodingBindingElement());
var transport = new HttpTransportBindingElement();
var customBinding = new CustomBinding(encoding, transport);
var client = new MtomEnabledServiceClient(customBinding);
but I'm not sure what's what here?
How would it be used to perform the document upload I'm trying to achieve? And is the library doing this or am I misunderstanding what it does?
If anyone can provide me an example of how to use this library to perform the document upload it would be appreciated.
Proceed as follows:
Generate the service client using WCF, this will result in a namespace and partial class, say DocumentWebServiceClient,
in the same project and namespace, create a file to extend the partial class and implement the ConfigureEndpoint method which is intended for endpoint configuration tasks:
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using WcfCoreMtomEncoder;
public partial class DocumentWebServiceClient // DocumentWebServiceClient is generated by the WCF
{
static partial void ConfigureEndpoint(ServiceEndpoint serviceEndpoint, ClientCredentials clientCredentials)
{
var messageEncodingBindingElementType = typeof(MessageEncodingBindingElement);
var elements = serviceEndpoint.Binding.CreateBindingElements();
IEnumerable<BindingElement> elementsWithoutEncodingElement = elements.Where(item => !messageEncodingBindingElementType.IsAssignableFrom(item.GetType()));
var existingEncodingElement = (MessageEncodingBindingElement)elements.Where(item => messageEncodingBindingElementType.IsAssignableFrom(item.GetType())).First();
var newEncodingElement = new MtomMessageEncoderBindingElement(existingEncodingElement);
// Encoding is before transport, so we prepend the MTOM message encoding binding element
// https://learn.microsoft.com/en-us/dotnet/framework/wcf/extending/custom-bindings
var cb = new CustomBinding(elementsWithoutEncodingElement.Prepend(newEncodingElement));
serviceEndpoint.Binding = cb;
}
}
After some struggles, I managed to create a WCF service that could be consumed by the class library. But it only supports the Custombinding. Please refer to the below example.
Server-side (a console application based on Dotnet Framework 4.7.2)
class Program
{
static void Main(string[] args)
{
Uri uri = new Uri("http://localhost:21011");
MtomMessageEncodingBindingElement encoding = new MtomMessageEncodingBindingElement();
var transport = new HttpTransportBindingElement();
transport.TransferMode = TransferMode.Streamed;
var binding = new CustomBinding(encoding, transport);
using (ServiceHost sh = new ServiceHost(typeof(MyService), uri))
{
sh.AddServiceEndpoint(typeof(IService), binding, "");
ServiceMetadataBehavior smb;
smb = sh.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (smb == null)
{
smb = new ServiceMetadataBehavior()
{
HttpGetEnabled = true
};
sh.Description.Behaviors.Add(smb);
}
Binding mexbinding = MetadataExchangeBindings.CreateMexHttpBinding();
sh.AddServiceEndpoint(typeof(IMetadataExchange), mexbinding, "mex");
sh.Opened += delegate
{
Console.WriteLine("Service is ready");
};
sh.Closed += delegate
{
Console.WriteLine("Service is clsoed");
};
sh.Open();
Console.ReadLine();
//pause
sh.Close();
Console.ReadLine();
}
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
string Test();
}
public class MyService : IService
{
public string Test()
{
return DateTime.Now.ToLongTimeString();
}
}
Client-side (Core-based Console application with WcfCoreMtomEncoder nuget package, calling the service by using ChannelFactory).
class Program
{
static void Main(string[] args)
{
var encoding = new MtomMessageEncoderBindingElement(new TextMessageEncodingBindingElement());
var transport = new HttpTransportBindingElement();
transport.TransferMode = TransferMode.Streamed;
var binding = new CustomBinding(encoding, transport);
EndpointAddress endpoint = new EndpointAddress("http://vabqia969vm:21011");
ChannelFactory<IService> channelFactory = new ChannelFactory<IService>(binding, endpoint);
var webService = channelFactory.CreateChannel();
Console.WriteLine(webService.Test());
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
string Test();
}
One more thing we pay attention to is that we should manually bind a certificate to the particular port on the server-side if the server using Transport security mode to secure the communication.
Netsh http add sslcert ipport=0.0.0.0
certhash=0102030405060708090A0B0C0D0E0F1011121314
appid={00112233-4455-6677-8899-AABBCCDDEEFF}
In the above example, I bind a certificate that has a named vabqia969vm subject(DNS) to the machine(hostname is vabqia969vm). Here are some official links.
https://learn.microsoft.com/en-us/windows/win32/http/add-sslcert
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-configure-a-port-with-an-ssl-certificate
On the client-side, before making a call to the service, we should establish a trust relationship so that communication is available between the client-side and the server-side. Therefore, I install the server certificate on the client-side LocalCA(Trusted Root Certification Authorities in the certification store). Alternatively, we could manually add a certificate validation process.
ChannelFactory<IService> channelFactory = new ChannelFactory<IService>(binding, endpoint);
channelFactory.Credentials.ServiceCertificate.SslCertificateAuthentication = new System.ServiceModel.Security.X509ServiceCertificateAuthentication()
{
CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None,
RevocationMode = System.Security.Cryptography.X509Certificates.X509RevocationMode.NoCheck
};
Feel free to let me know if there is anything I can help with.
Updated.
I have updated the above example which works properly over HTTP protocol.
your .net core client code should be something like this
var encoding = new MtomMessageEncoderBindingElement(new TextMessageEncodingBindingElement());
var transport = new HttpTransportBindingElement();
var customBinding = new CustomBinding(encoding, transport);
EndpointAddress endpoint = new EndpointAddress(url);
var channelFactory = new ChannelFactory<T>(customBinding, endpoint);
var webService = channelFactory.CreateChannel();
user.UserName = await webService.EncryptValueAsync(userName);
user.Password = await webService.EncryptValueAsync(password);
var documentAddResult = webService.DocumentAdd(document);
channelFactory.Close();
Expanding on #divyang4481's answer. For those still struggling with this, depending on the service you are interacting with, you may have to change your encoding to something like the following:
var encoding = new MtomMessageEncoderBindingElement(new TextMessageEncodingBindingElement
{
MessageVersion = MessageVersion.CreateVersion(EnvelopeVersion.Soap12, AddressingVersion.None)
});
var transport = new HttpsTransportBindingElement();
var customBinding = new CustomBinding(encoding, transport);
The web service I was calling threw an error, i.e. mustUnderstand headers with default TextMessageEncodingBindingElement, so, I had to set the AddressVersion to none to solve the issue.
I want to create WCF client directly from code:
var binding = new BasicHttpBinding();
var myEndpoint = new EndpointAddress("http://localhost/acquisition/production.svc");
var myChannelFactory = new ChannelFactory<IProduction>(binding, myEndpoint);
proxy = myChannelFactory.CreateChannel();
The problem is that following code throws exception:
There was no endpoint listening at.
But when add reference do WCF test client, service is discovered properly and can be invoked withouy any errors:
Why manual endpoint doesn't work?
I have a WCF service where everything is started programmatically (and need to continue doing so) and I want this service to respond to [WebGet] attributes.
However, when I call one of the WCF methods the service returns a 400 Bad Request.
This would initially seem like a duplicate of WCF Service Returns 400 error when using WebGet or Bad Request 400 while accessing WCF Rest service (WebGet) but both of these solutions add to web.config, a file I don't have because I need to do everything programmatically.
I have tried to add a WebHttpBinding to what seems like the endpoint, but it doesn't work properly (I'm probably not doing it the correct way).
The code below starts without errors, but when I try to go to http://localhost:8765/MyService/MyTest I get the aforementioned 400 Bad Request
What am I missing?
WCF starter
MyService myService = new MyService();
ServiceHost host = new ServiceHost(myService, new Uri("http://localhost:8765/MyService"));
ServiceBehaviorAttribute behavior = host.Description.Behaviors.Find<ServiceBehaviorAttribute>();
behavior.InstanceContextMode = InstanceContextMode.Single;
foreach(ServiceEndpoint endpoint in host.Description.Endpoints) {
endpoint.Binding = new WebHttpBinding();
}
host.Open();
Service interface
[ServiceContract]
public interface IMyService {
[OperationContract]
[WebGet]
string MyTest();
}
Service implementation
public class MyService : IMyService {
public string MyTest() {
return "Response from WCF service";
}
}
I use this code I wrote to initialize and start my WCF restful services completely from code:
public static WebServiceHost InitializeAndStartWebServiceHost(int port, string endPointName, object serviceModel, Type implementedContractType) {
var baseAddress = new Uri($"http://0.0.0.0:{port}/{endPointName}");
WebServiceHost host;
try {
host = new WebServiceHost(serviceModel, baseAddress);
} catch (Exception exception) {
Debug.Print("Error when creating WebServiceHost, message: " + exception.Message);
return null;
}
// ReSharper disable once UseObjectOrCollectionInitializer
var binding = new WebHttpBinding();
binding.UseDefaultWebProxy = false;
binding.BypassProxyOnLocal = true;
//By default, TransferMode is Buffered which causes C# wcf client to be slow as hell (>500ms for requests which give >2kB responses).
//I am not exactly sure why this helps, but it helps!
binding.TransferMode = TransferMode.Streamed;
host.AddServiceEndpoint(implementedContractType, binding, "");
var behavior = new WebHttpBehavior();
behavior.HelpEnabled = false;
behavior.DefaultBodyStyle = WebMessageBodyStyle.Bare;
// We will use json format for all our messages.
behavior.DefaultOutgoingRequestFormat = WebMessageFormat.Json;
behavior.DefaultOutgoingResponseFormat = WebMessageFormat.Json;
behavior.AutomaticFormatSelectionEnabled = false;
behavior.FaultExceptionEnabled = true;
host.Description.Endpoints[0].Behaviors.Add(behavior);
try {
host.Open();
} catch (AddressAccessDeniedException) {
Console.WriteLine(#"Application must run with administrator rights.");
Console.ReadKey();
Environment.Exit(0);
}
return host;
}
I'm trying to get my generated WCF ServiceClient to send its requests to the WCF service through a HTTP (not HTTPS) proxy with username/password authentication, however I just can't get it work. My WCF service uses basicHttpBinding so I tried to configure my ServiceClient instance like so:
var svc = new ServiceClient();
var b = svc.Endpoint.Binding as BasicHttpBinding;
b.ProxyAddress = new Uri(proxyAddress);
b.UseDefaultWebProxy = false;
b.Security.Mode = BasicHttpSecurityMode.Transport;
b.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
b.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.Basic;
svc.ClientCredentials.UserName.UserName = proxyUsername;
svc.ClientCredentials.UserName.Password = proxyPassword;
This however results in a System.ArgumentException saying:
The provided URI scheme http is invalid. expected https
When I set the b.Security.Mode to BasicHttpSecurityMode.None though, it seems the HTTP proxy settings are ignored by WCF altogether!
The second solution I tried was to set the DefaultWebProxy property of WebRequest and set the UseDefaultWebProxy property to true such as so:
var webProxy = new WebProxy(proxyAddress, true);
webProxy.Credentials = new NetworkCredential(proxyUsername, proxyPassword);
WebRequest.DefaultWebProxy = webProxy;
var svc = new ServiceClient();
var b = svc.Endpoint.Binding as BasicHttpBinding;
b.UseDefaultWebProxy = true;
However this also doesn't work and ServiceClient doesn't send it's requests through the HTTP proxy.
I'm out of ideas here so please let me know what I am doing wrong, thank you!
Set the security mode to BasicHttpSecurityMode.TransportCredentialOnly. This allows for passing plain-text authentication details over HTTP.
I have created a WCF service which has a callback. I have created a sample client which will subscribe to these callbacks. I have been using the ListBasedPublishSubscribe sample as a base for this. However when I try and setup the unique callback address in the client with this code
context = new InstanceContext(null, new MyClass());
client = new MyClient(context);
WSDualHttpBinding binding = (WSDualHttpBinding)client.Endpoint.Binding;
string clientcallbackaddress = binding.ClientBaseAddress.AbsoluteUri;
clientcallbackaddress += Guid.NewGuid().ToString();
binding.ClientBaseAddress = new Uri(clientcallbackaddress);
The third line fails because client.Endpoint.Binding.ClientBaseAddress is null. Should this not be null (I assume so for the sample to work) and why is this the case in my app? Have I forgotten to do something?