How do I use WcfCoreMtomEncoder in a .Net Core project?
Asked Answered
C

4

6

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.

Conferee answered 6/1, 2020 at 21:12 Comment(4)
On my side, I tried, it didn’t work, always the same error occurred.Bravissimo
Hi.What exactly did you try? Maybe you can show a snippet of your code and the error?Conferee
I created a WCF service with basichttpbinding using the Mtom coder. Then I call the service by configuring an custombinding using the Mtom encoder. I also tried to create a WCF service with the given binding, and consume it. Besides, Channel Factory with that binding also didn’t work. I don't know what the class library is used for. the GitHub issues also haven’t been solved yet.Bravissimo
(The content type multipart/related; type="application/xop+xml"; start="<tempuri.org/0>"; boundary="uuid:b84ef9c6-0def-4900-a3cb-27d13b846c62+id=5"; start-info="text/xml" of the response message does not match the content type of the binding (text/xml; charset=utf-8). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly. The first 435 bytes of the response were:Bravissimo
I
6

Proceed as follows:

  1. Generate the service client using WCF, this will result in a namespace and partial class, say DocumentWebServiceClient,
  2. 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;
    }
}
Inappetence answered 31/8, 2020 at 10:27 Comment(0)
B
4

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.

Bravissimo answered 24/1, 2020 at 7:40 Comment(3)
Your code uses .Net Framework 4.7.2? My post was specifically related to .Net CoreConferee
Server-side uses .Net Framework 4.7.2, the client-side uses .Net core Sdk. That’s where the library apply in, enabling the core-based application can use Mtom encoder. Server-side based on .Net Core cannot create Wcf service applications,because it is a distributed framework based on .Net framework, has not been implemented yet in Core. gRPC is the best approach for building distributed applications in CoreBravissimo
@user1186050 , In the above code snippets, we can also create a service without transport layer security mode. and it can be called properly in the Dotnet Core application as well. If we do in this way, we needn't specify `https' base address, there is no need to bind a certificate, It will be easily finished. I have updated the reply, please check.Bravissimo
R
0

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();
Rebhun answered 14/1, 2020 at 19:48 Comment(4)
Do you know if this supports https? Using it I'm getting an exception that says: "The provided URI scheme 'https' is invalid; expected 'http'"Conferee
in that case use HttpsTransportBindingElement instead of HttpTransportBindingElementRebhun
but it expects http, not https according to the message. Aren't you suggesting I do the opposite?Conferee
If your server url is http then go with httptransportbindingRebhun
F
0

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.

Fierro answered 25/4, 2021 at 22:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.