Developer's Dump

Posts Tagged ‘Web Services

Don’t use “Add Service Reference”!

with 9 comments

The Visual Studio tools have always been crappy at generating code. The result is acceptable if you are a Microsoft representative selling new technology at some fancy seminar, but it is nothing you want to use in a release version of you application. The Visual Studio “Add Service Reference” (WCF) feature makes no exception.

The “Add Service Reference” feature might become handy when you need to connect to some none-.NET services, but in most other cases I sincerely recommend the manual approach. In this article I’m only going to tell you have, if you need more information check Miguel A. Castro’s article “WCF the Manual Way…the Right Way“.

Start by createing a basic class library project and reference the System.ServiceModel. This library is going to be referenced by both the client and the server. Don’t use the WCF templates as they will only make your life harder on the long run.

Add your service interface to the newly created project:

[ServiceContract(Name = "TestService", Namespace = "http://www.company.com/tests")]
public
interface ITestService
{
    
    [OperationContract]
    TestData GetData(TestData data);

}

[DataContract]
public
class TestData
{
   
public TestData(string message)
    {
        Message = message;
    }

    [DataMember]
    public
string Message { get; set; }

   
public
override string ToString()
    {
        return “Some shared override”;
    }
}

Now add a new project to your solution and implement the server the same way you normaly would:

public class TestService: ITestService
{

    #region ITestService Members

    public TestData GetData(TestData data)
    {
        return new TestData(“Hello world! + data.Message);
    }

    #endregion
}

Instead of messing around with the XML configuration file, just configure your host by code:

ServiceHost host = new ServiceHost(typeof(TestService), new Uri[] { new Uri(uri) });
NetTcpBinding
binding = new NetTcpBinding(SecurityMode.Transport);

binding.TransferMode = TransferMode.Streamed;
binding.MaxBufferSize = 65536;

binding.MaxReceivedMessageSize = 104857600;
host.AddServiceEndpoint(typeof(ITestService), binding, “service1″);

host.Open();

As you can see there is not much new to implementing the service, but now it’s time for the client!

Create a new project for the client and reference the class library you first created. Instead of going for the “Add Service Reference” option create your own proxy:

public class TestServiceClient: ITestService
{

    private ITestService service;
    
    public TestServiceClient(Uri uri)
    {
        
// Any channel setup code goes here
   
     EndpointAddress address = new EndpointAddress(uri);
        NetTcpBinding binding = new NetTcpBinding(SecurityMode.Transport);
        binding.TransferMode = TransferMode.Streamed;
        binding.MaxBufferSize = 65536;
        binding.MaxReceivedMessageSize = 104857600;

        ChannelFactory<ITestService> factory = new ChannelFactory<ITestService>(binding, address);
        service = factory.CreateChannel();
    }

    #region ITestService Members

    public TestData GetData(TestData data)
    {
        return service.GetData(data);
    }

    #endregion
}

You can now place useful functions in your data classes and utilize them both on the client and server side. If you are running with .NET 3.5 you can even leave out the Data and Member Contract attributes of the data classes (same as choosing “Reuse types in referenced assemblies” when using thee Add Service Reference feature).

The biggest advantages of building your proxy manually compared to using the Add Service Reference feature is that you can now share classes and have the same client class implement several service interfaces instead of only one.

Written by Tom

January 5, 2009 at 15:06

Posted in WCF

Tagged with , ,

Working Web Services

with 2 comments

Working with web services in .NET (starting from 2.0) should be as easy as 1-2-3. Tools are provided for auto-generating code based on a WSDL. With .NET 3 Microsoft tries to direct every developer to use WCF for handling web services. Trying to get a Axis based Java web service to work proved to require some tricks.

I first generated a web service client (proxy) using the Add Service Reference tool in Visual Studio. This got a me a WCF based class to interact with the web service. All this was easy, but when I tried to call one of the remote methods, I was literally left with nothing (null).

As the diagnostic features (message tracking and tracing) of WCF really didn’t reveal anything usefull, I had to implement my own custom behavior with a message inspector to see what was really happening behind the secenes. This revealed that messages where sent and received, but the data did not get processed by the client. In the end, there wasn’t much else to do than to fall back and to make use of the older .NET web service handling technology (System.Web.Services).

Once again generating a client based on the WSDL was easy. And when I now tried calling the remote function I was returned with the data I expected. So, once again legacy technology prevailed when the newer failed.

As authentication (basic HTTP) is required by most we services I needed my client to support this. Normally this should only require to supply some valid credentials:

MatrixWebServicesServiceproxy = new MatrixWebServicesService();
proxy.PreAuthenticate =
true;
proxy.Credentials =
new NetworkCredential("user", "password");

But this did not do the trick. Using a TCP sniffer I was able to see that the “Authorized” HTTP header did not get sent to the server. After some research I came to the following conclution: The .NET client (this also applies to WCF) is designed to expect a “Unauthorized” HTTP error (401) from the server to respond to, without that the credentials are not sent. Also setting the PreAuthenticate property to true will only force the client to send the credentials with every message after this has first been required by the server. The problem with the Java web service was that is sent a “Internal Server Error” HTTP error (501) and a valid SOAP error when the authentication failed. So the credentials where never sent.

The only solution I found for this was to modify the generated client code and force it to send the credentials with every message. This can be done by overriding the clients GetWebRequest method. As the client is based on a bunch of partial classes, you only need to add your own with the following code to get things running:

public partial class SomeJavaWebServices : System.Web.Services.Protocols.SoapHttpClientProtocol {

protected override WebRequest GetWebRequest(Uri uri)
{
    HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(uri);

    if (PreAuthenticate)
   
{

        NetworkCredential networkCredentials = Credentials.GetCredential(uri, “Basic”);
        if (networkCredentials != null)
        {

            byte[] credentialBuffer = new System.Text.UTF8Encoding().GetBytes(networkCredentials.UserName + “:” + networkCredentials.Password);

            request.Headers["Authorization"] = “Basic “ + Convert.ToBase64String(credentialBuffer);
        }
        else
        {
            throw new ApplicationException(“No network credentials”);
        }
    }    return request;
}

 

}

Written by Tom

October 20, 2008 at 09:33

Posted in WCF, Web Services

Tagged with , , , ,

Follow

Get every new post delivered to your Inbox.