Showing posts with label WCF. Show all posts
Showing posts with label WCF. Show all posts

Wednesday, March 17, 2010

My WCF service doesn’t generate a WSDL!

Recently I added a new WCF web service to an pre-existing application. That application already contained two other WCF web services and was specifically partitioned in that it had the interfaces, data classes and SVC files in separate projects. So I just copied the necessary files from inside the projects and adjusted them to suit my needs, adding many new data classes for the new interface.
I compiled to make sure I didn’t have any syntax errors and then I copied and changed the WCF config parts in the web.config.

So far so good. The code compiled and I could access the SVC page from my browser.
I then started SOAPUI so I could test the web service and pointed it towards the WSDL interface of the new web service. Imagine my surprise when it did find the web service, but not a single method from it.
I checked the WSDL from my browser and lo’-and-behold: There were only endpoints in there. No methods and data structures.

I re-checked the WSDL, comparing it to another, working, web service we had and it was all correct. Then I went over the SVC, service contract interface and implementation again and I noticed a line similar to this:

[OperationContract(Action="http://my-namespace.com/app/1.0/DoStuff", ReplyAction="*")]
void DoStuff();

I didn’t know what that “ReplyAction” was about, but that wildcard doesn’t seem right to me. I checked the interface I copied it from and that interface was an older, pre-existing SOAP web service that we added to our app by using the svcutil.exe tool. So that “ReplyAction” was added by that tool.
It turns out that ReplyAction"=”*” means: don’t use a SOAP action in the reply. This has to do with a WCF web service that is building the messages by itself, instead of letting the framework do it for you. This was probably caused by the old web service that didn’t quite fit into the mold WCF has created for us to develop web services in. But since WCF needs to provide us with the ability to create all kinds of web services that are legal according to the SOAP standards, it provides the ability to create the SOAP messages by hand. So when we used the WCF svcutil.exe tool, it determined that it needed to do that for that particular web service. Then when I copied those files for my new web service, I got stuck with the “ReplyAction”.

I removed the “ReplyAction” from the “OperationContract” and after that the web service auto-generated a WSDL just fine.

Tuesday, January 6, 2009

SOAP logging for webservices

If you wish to log incoming SOAP requests to your ASMX webservice, you can write a SOAP extension that does just that.

You’ll need to create a class that inherits from SoapExtension and you should override the methods GetInitializer, Initialize, ChainStream and ProcessMessage.
It’s not that hard, you can find an working example at the MSDN site: How to: Implement a SOAP Extension. It works for both the server (who accepts incoming SOAP requests) and a client (who sends SOAP requests to servers).

All you need to do is add this to your web.config (or app.config) and it will work:

<system.web>
    <webServices>
        <soapExtensionTypes>
            <add type="MySoapLogger, MyNamespace.SoapLogger" priority="1" group="High" />
        </soapExtensionTypes>
    </webServices>
</system.web>

If you wish to create a SOAP logger for a WCF webservice, that’s both easier and harder. It’s simpler to write the extension for, but now you must write different things for a server and a client. And making it configurable requires some more work then the ASMX version.

In WCF you don’t write a SOAP Extension, but rather a Message Interceptor. And you won’t need to inherit from a base class, but you’ll need to implement certain interfaces to make it work.
To be able to see incoming (server) messages you’ll need to implement IDispatchMessageInspector and for outgoing (client) messages you’ll need to implement IClientMessageInspector. It's worth mentioning that nothing prevents you from implementing these interfaces both on the same class. That's what I did.

Both interfaces require you implement two functions:

object IDispatchMessageInspector.AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext);
void IDispatchMessageInspector.BeforeSendReply(ref Message reply, object correlationState);

And:

object IClientMessageInspector.BeforeSendRequest(ref Message request, IClientChannel channel);
void IClientMessageInspector.AfterReceiveReply(ref Message reply, object correlationState);

For IDispatchMessageInspector you have AfterReceiveRequest for when the request is received, before your normal code will run and BeforeSendReply, which is directly before the response will be send to the requester, after all your normal code has run.
For IClientMessageInspector you have BeforeSendRequest which is right before the request is send to the server, after all your normal code has run and AfterReceiveReply for when the response from the server has been received, before your normal code will run.

Basically you can do anything to the request you want at those points, even alter the data. One of the examples I found was to validate incoming requests to an XSD. Another was to do some translations for backwards compatibility sake. But in this case we just want to log the data.

So what we can do in each of these functions, is the following:

try
{
    // Only try to parse the message if it is not empty.
    if(message.IsEmpty == false)
    {
        string xml = message.ToString();
        XDocument xmlDocument = XDocument.Parse(xml);

        StringBuilder sb = new StringBuilder();
        XmlWriter writer = XmlWriter.Create(sb, new XmlWriterSettings { Encoding = Encoding.ASCII, Indent = true });
        xmlDocument.WriteTo(writer);
        writer.Close();

        sb.Append("\r\n");
        this.soapLogger.Log(sb.ToString());
    }
}
catch(Exception e)
{
    if(System.Diagnostics.Debugger.IsAttached == true)
    {
        System.Diagnostics.Debugger.Break();
    }
}

In this case the "message" variable contains the SOAP message. I wrote one function to do the logging and have it called from the 4 functions mentioned above. Also the "soapLogger" class variable is the logger I use. It's not particulary important which logger this specifically is, but if you want to know, I use Log4NET.

The use of the XDocument and the XmlWriter is absolutely not necessary, but I use them to make the XML look pretty (indented) in the log. It is a lot slower then just directly logging it to the logfile, but that would result in the whole XML message to appear on 1 line.
The stuff in the try/catch clause is a neat trick I use to catch the unexpected exceptions.

If you want the most simple solution, you can also do it like this:

// Only try to parse the message if it is not empty.
if(message.IsEmpty == false)
{
    string xml = message.ToString();
    this.soapLogger.Log(xml);
}

Monday, December 29, 2008

XML element order in WCF

Call me old-fashioned, but I’ve always held to the belief that in XML nesting is very important and order should never be important. I’ve always found it unbelievably stupid when some application depended on the order of elements in an XML document. To me XML is all about representing structured data, not about a rigorous document layout.

Imagine my surprise when I started testing my new WCF webservice with a request I wrote by hand. All of a sudden a member was left null no matter what I did. Because it was the last element in my hand-written request that remained null, my first though (after extensively verifying I didn’t make any typo’s) was that, maybe, for some obscure reason, the last member didn’t get deserialized correctly. So I put another member last and indeed it was also null. Then I put another member last and al of a sudden all where filled, except for the first one.

After a few hours of WTF?!! I found out that the ‘default’ serializer for WCF, the Microsoft favourite, the “Data Object Serializer” has a dependency on XML element order. After changing the serializer to the “XML serializer” (just remove the “DataContract” attribute from the classes and the “DataMember” attributes from the members of those classes and replace then with the “Serializable” atttribute and/or any of the “Xml*” attributes from the “System.Xml.Serialization” namespace and tack the “XmlSerializerFormat” attribute on the class that contains your WCF methods) it all just worked! Order wasn’t important anymore, as it should be.

What I don’t get is why Microsoft would promote the “Data Object Serializer” as the preferred methods for WCF. The “XML Serializer” is much more flexible if you want to control what the resulting XML document should look like and XML element order doesn’t matter! The only advantage to using the “Data Object Serializer” is that it doesn’t serialize members of classes that do not have the required attribute (opt-in instead of the opt-out method of the “XML Serializer”) and that you can serialize private members, which can come in handy from time to time. But it has a dependency on XML element order!!

Tuesday, May 6, 2008

Some Ajax/WCF things I learned

I'm reworking a small ad-hoc web application I wrote because like all ad-hoc tools, it got a rather permanent status. It's like they say: "Nothing is as permanent as a temporal solution".
I wrote the first version using ASP.NET MVC. The only reason I did so was because I wanted to experiment with MVC and they told me to create the application in any way I saw fit. So it was a great way to experiment a little. Now the web application will be used on a more permanent basis and we will have to actively maintain it I decided to rewrite it. Also an additional requirement made the whole MVC thing a little harder then I ought to be. So I rewrote the web application, but this time I decided to use a WCF JSON webservice and go all-out Ajax. Still experimental, but less CTP-al.

So my first order of business was to write a WCF webservice that did the things I needed it to do. This was easy. No hurdles there.
Did you know you can put a [DataMember] attribute on a private member? It will even show up in the WCF service while still being private in your code. I used this because I had some data that needed to be exposed in the webservice as a string, but as a different data type internally. So I made a private property that made a string out of that data and put the [DataMember] attribute on it. And then I had a public property without the attribute for use inside the code. Very handy.

Next up after getting the WCF webservice done was consuming it through JavaScript. Nothing really difficult here either. Just Google for JavaScript and WCF and you'll find lots of examples. That's what I did. The only thing I did ran into was that at first I tried to have two separate web projects. One for the WCF webservice and one for the web application. For some reason I never got the JavaScript to work with the WCF webservice. I don't think it's impossible, but after fiddling with the WCF configuration for an hour I gave up and just made it into one project and it worked.

After I got the WCF/JavaScript communication to work I started to work on the JavaScript logic. And in doing so I learned some stuff about JavaScript.

JavaScript isn't really object oriented, but you can fake it.
You can create a new 'object' using the new keyword and a function declaration:

var myObject = new MyObject(){}

If you leave out the 'new' keyword the variable "myObject" would contain a reference to a function named "MyObject". A function that doesn't do anything. But add the 'new' keyword and now it's something like an object.
Unfortunately I have no idea how to proceed any further. It's possible to add properties and methods to the object. but I'm not 100% sure how.Also I had the site working in Firefox and Internet Explorer 8 beta, but when I tried it with Internet Explorer 7 it didn't work. It turns out that doing something like:

var element = document.getElementById("someId"); element.setAttribute("class", "cssClass"); element.setAttribute("onclick", "alert('Yay!');");

Works perfectly in Firefox and IE8, but not in IE7. The annoying part is that if you use the Developer Toolbar in IE7 and inspect the DOM properties of the element they look good. I created a similar element in straight HTML and compared it to the element I added and setup dynamically through JavaScript, but I didn't see any difference. Yet it didn't work.
Turns out you can't do this in IE7 and you need to use the proper properties of the element to set stuff up. So the above needs to be:

var element = document.getElementById("someId"); element.className = "cssClass"; element.onclick = function() { alert('Yay!'); };

Luckily this also works in Firefox and IE8.

More as I learn more...