The most annoying thing about setting up a WCF is the number of things that seem to work in the local testing server that'll explode in IIS. The local testing server that you can invoke with F5 is very lenient.

But before we get into the complicated stuff, a quick list of System-Provided Bindings from Microsoft:

WSHttpBinding
A secure and interoperable binding that is suitable for non-duplex service contracts.
BasicHttpBinding
A binding that is suitable for communicating with WS-Basic Profile conformant Web services, for example, ASP.NET Web services (ASMX)-based services. This binding uses HTTP as the transport and text/XML as the default message encoding.
WebHttpBinding
A binding used to configure endpoints for WCF Web services that are exposed through HTTP requests instead of SOAP messages.


Fair warning: I've done this three or four times now to make sure things work, but I haven't started from scratch on a new machine to run through the steps as I present them, here. Could be wonky somewhere. YMMV.

So let's start a WCF Service project. You select File >>> New Web Site >>>WCF Service. Save the new project in IIS's root folder.



That'll create a project with the file structure seen below:


Looking in Web.config, you'll see that, by default, this project has two endpoints. One is the MEX endpoint, which is nice, but not really the business end of things. The other is a wsHttpBinding. That's important, because, as we learned above, that expects to be called from a SOAP-compliant client.

If you try hitting F5 off the bat, it'll seemingly work, first going to a URL like this one:
http://localhost:50319/WCFService1/Service.svc

But if you try to view the GetData method, which is one of the two IService methods that Service.cs implemented by default, using a URL like this one:
http://localhost:50319/WCFService1/Service.svc/GetData

... you get no response, just a 400 error.

So it's worth saying that it's odd to have a SOAP client in my line of programming. You'd usually rather send out a very simple AJAX request to a URL from a web page to the WCF and receive some JSON back to parse in Javascript. The take-home is that we need to remove the wsHttpBinding (set up for SOAP) and set up webHttpBinding (ready for REST) instead.

The endpoints that Visual Studio inserts into web.config by default are below:
<endpoint address="" 
binding="wsHttpBinding" contract="IService">
<!--
Upon deployment, the following identity element should be
removed or replaced to reflect the identity under which the
deployed service runs. If removed, WCF will infer an
appropriate identity automatically.
-->
<identity>
<dns value="localhost"/>
</identity>
</endpoint>

Our next step is to change that wsHttpBinding endpoint to one with webHttpBinding. I've also inserted the additional overhead of adding a JsonBehavior. Honestly, not sure what that's doing yet, but I think I want it.
<system.serviceModel>
<!-- serviceHostingEnvironment aspNetCompatibilityEnabled="true" / -->

<serviceHostingEnvironment aspNetCompatibilityEnabled="false" />
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<!-- To avoid disclosing metadata information, set
the value below to false and remove the
metadata endpoint above before deployment
-->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for
debugging purposes, set the value below to
true. Set to false before deployment to avoid
disclosing exception information
-->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="JsonBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>

<services>
<service name="Service" behaviorConfiguration="ServiceBehavior">
<endpoint address="" binding="webHttpBinding"
behaviorConfiguration="JsonBehavior" contract="IService">

<!-- original:
endpoint address=""
binding="wsHttpBinding" contract="IService" -->

<!--
Upon deployment, the following identity
element should be removed or replaced
to reflect the identity under which the
deployed service runs. If removed, WCF
will infer an appropriate identity
automatically.
-->
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>

(In case I've screwed up, the entire config is here.)

Again, switching from wsHttpBinding to webHttpBinding makes it so that we can use a URL to access the method. With wsHttpBinding, you'd have to have a SOAP client, which involves insane amounts of overhead for most of my applications.

There's more required than that, however. If you've got the same standard setup as I get in VS 2010, you've got two methods in the Service.cs file. One is pretty easy to set up to listen to query strings for its parameters;

[OperationContract]
[WebGet(RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.WrappedRequest)]
string GetData(int value);


Now, you should be able to nav to GetData and slap in a param of "?value=1231" and have that number repeat back to you with a URL like this:
http://localhost:50319/WCFService1/Service.svc/GetData?value=10

You'll see "You entered: 10" (with quotes) in the web page.


You can also leave the port off if you followed the instructions and created the dir in IIS' home dir and created the application using Internet Information Server (IIS) Manager. Go to your default web site, find your server's folder, right click, convert to application, and voila:



Unfortunately, the other method in the default project (GetDataUsingDataContract) is more complicated, as if you try to turn it into a GET-able method, your WCF Service will complain about the CompositeType hand-rolled datatype that's also part of the project VS 2010 dreams up for you.

[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);

You can't webget that because CompositeType isn't serializable.

Operation 'GetDataUsingDataContract' in contract 'IService' has a query variable named 'composite' of type 'CompositeType', but type 'CompositeType' is not convertible by 'QueryStringConverter'. Variables for UriTemplate query values must have types that can be converted by 'QueryStringConverter'.


Whoops. I'm not going to go into serializing to JSON right now. All things considered, that's an easy afterthought after this XML config wading.

So perhaps not the best composed, but that's today's lesson.

(A decent walkthrough of a slightly different way to go about this here.)

Labels: , , ,