-1

I want to write a C# code which calls a (remote) web service in another machine. For this I have to pass username and password in the SOAP header of the call.

I would like to know an example of code to make this in C#.

the produced XML should be like :

<env:Header>
    <ns1:Security>
        <ns1:UsernameToken>
            <ns1:Username>XXXXXXXXXXXXXXXX</ns1:Username>
            <ns1:Password>YYYYYYYYYYYYYYYY</ns1:Password>
        </ns1:UsernameToken>
    </ns1:Security>
</env:Header>

Thanks in advance J.

johnf
  • 171
  • 1
  • 3
  • 21

2 Answers2

0

Are many ways to do that. The CustomBinding is more flexible because it allow more controll, for that i propose you with that. Pasing header to endpoint is a simple way:

// binding
var binding = new CustomBinding();
binding.Elements.Clear();
binding.Elements.Add(new TextMessageEncodingBindingElement{MessageVersion = MessageVersion.Soap12});
binding.Elements.Add(new HttpTransportBindingElement{MaxReceivedMessageSize = 20000000,});
// endpoint
var endpoint = new EndpointAddress(new Uri(listeningUri), new MySecurityHeader())
var client = new Client(binding, endpoint);
client.SomeMethod();

where MySecurityHeader is an AddressHeader, for example:

    public class MySecurityHeader : AddressHeader
    {
        public override string Name
        {
            get { return "Security"; }
        }

        public override string Namespace
        {
            get { return "<provide the appropiate namespace>"; }
        }

        protected override void OnWriteAddressHeaderContents(System.Xml.XmlDictionaryWriter writer)
        {
            // here you do what you want
            writer.WriteRaw(String.Format(@"
             <UsernameToken xmlns=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"">
             <Username>user</Username>
             <Password Type=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"">pass</Password>
             </UsernameToken>").Trim());
        }
    }
Alexander Leyva Caro
  • 1,183
  • 1
  • 12
  • 21
0

this is an example using an IEndpointAdress

    var customBinding = new CustomBinding();
    customBinding.Elements.Add(new TextMessageEncodingBindingElement { MessageVersion = MessageVersion.Soap12, });
    customBinding.Elements.Add(new HttpTransportBindingElement { MaxReceivedMessageSize = 20000000, });

    var endpointAddres = new EndpointAddress(listeningUri);

    var client = new Client(customBinding, endpointAddres);
    // add my own IEndpointBehavior
    client.ChannelFactory.Endpoint.Behaviors.Add(new CustomBehavior());
    client.SomeMethod();

and this is the CustomBehavior definition

public class CustomBehavior : IEndpointBehavior
{
    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {}

    public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
    {
        var inspector = new CustomMessageInspector();
        clientRuntime.MessageInspectors.Add(inspector);
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
    {}

    public void Validate(ServiceEndpoint endpoint)
    {}
}

public class CustomMessageInspector : IClientMessageInspector
{
    public void AfterReceiveReply(ref Message reply, object correlationState)
    {}

    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
        request.Headers.Add(new MyMessageHeader());
        return null;
    }
}

public class MyMessageHeader : MessageHeader
{

    protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
    {
        writer.WriteRaw(String.Format(@"
        <UsernameToken xmlns=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"">
        <Username>user</Username>
        <Password Type=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"">pass</Password>
        </UsernameToken>").Trim());
    }

    public override string Name
    {
        get { return "MyHeaderName"; }
    }

    public override string Namespace
    {
        get { return "MyHeaderNamespace"; }
    }
}

Note you have control before send the request and after receive your reply.
I hope this resolve your issue, if you have some problems with this yust ask me.

Alexander Leyva Caro
  • 1,183
  • 1
  • 12
  • 21
  • I have error on this line : var client = new Client(customBinding, endpointAddres); ie : the type of namespace Client could not be found ... – johnf Nov 20 '14 at 17:16
  • You need to change Change your client by your Proxy client (the class autogenerate for consume the web service), and then call its methods, OK? – Alexander Leyva Caro Nov 20 '14 at 17:54
  • System.InvalidOperationException was unhandled HResult=-2146233079 Message=Path property must be set before calling the Send method. Source=System.Web.Services StackTrace: at System.Web.Services.Protocols.WebClientProtocol.GetWebRequest(Uri uri) at System.Web.Services.Protocols.HttpWebClientProtocol.GetWebRequest(Uri uri) at System.Web.Services.Protocols.SoapHttpClientProtocol.GetWebRequest(Uri uri) at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters) at – johnf Nov 20 '14 at 18:04
  • what line throw the exception? – Alexander Leyva Caro Nov 20 '14 at 18:11
  • Line : 137 - object[] results = this.Invoke("vhWsCreatePayIdVersion", new object[0]); – johnf Nov 20 '14 at 18:18
  • This line is not from my code. Are you sure what you are trying to do? If not, explain to me, so i'll help you. because i don't know why you use this.Invoke. – Alexander Leyva Caro Nov 20 '14 at 18:23
  • Sorry, regarding your code, the line is : client.SomeMethod(); which I changed to : client.vhWsCreatePayIdVersion(); – johnf Nov 20 '14 at 18:28
  • 1
    Use fiddler (or sniffer) to see what is the soap message you send by http. If the message correspond with that you expect, then the server must respond properly. – Alexander Leyva Caro Nov 20 '14 at 18:47
  • With the code that i proposed you, i just show how you can handle the soap header, with the method OnWriteHeaderContents (change it for your purpose) from MyMessageHeader class. – Alexander Leyva Caro Nov 20 '14 at 18:49
  • I tried the second way and have issue with the line client.channelFactory.Endpoint.Behaviors.Add(new CustomBehavior()); with message : does not contain a definition for channelfactory ... Any ideas ? Thanks ... – johnf Nov 20 '14 at 19:59
  • when you consume a service, you have a contract (interface that describe the service) and when you generate the code (using svcutil.exe for example) it create the interface and its implementation, let IClient and Client respectively. In my code, Client is the implementation of yor contract. Other way to create the client is with that: var client = new ChannelFactory(customBinding).CreateChannel(endpointAddres); – Alexander Leyva Caro Nov 20 '14 at 20:14
  • IClient contains the method that you want to consume – Alexander Leyva Caro Nov 20 '14 at 20:15
  • I implemented as you suggested, Now I get message :The content type text/plain of the response message does not match the content type of the binding (application/soap+xml; charset=utf-8). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly. – johnf Nov 21 '14 at 07:44
  • Like i said before, I just showed you how handle manually the soap message, allowing you include the username and password, That was your question. Your current problem now, is diferent, I suggest your look some links like http://stackoverflow.com/questions/15481092/the-content-type-application-xmlcharset-utf-8-of-the-response-message-does-not to found a solution. Note that the CustomBinding have configuration like TextMessageEncodingBindingElement and HttpTransportBindingElement, is possible you need to provided appropiate values to its properties or include other configuration. – Alexander Leyva Caro Nov 21 '14 at 13:16
  • Thanks for your feedback. However I get message which seems strange to me ( any assistance is kindly appreciated ): The content type text/plain of the response message does not match the content type of the binding (text/plain). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly. The first 395 bytes of the response were: ' ... – johnf Nov 21 '14 at 14:11