MacBook, defective by design banner

title:
Put the knife down and take a green herb, dude.


descrip:

One feller's views on the state of everyday computer science & its application (and now, OTHER STUFF) who isn't rich enough to shell out for www.myfreakinfirst-andlast-name.com

Using 89% of the same design the blog had in 2001.

FOR ENTERTAINMENT PURPOSES ONLY!!!
Back-up your data and, when you bike, always wear white.

As an Amazon Associate, I earn from qualifying purchases. Affiliate links in green.

x

MarkUpDown is the best Markdown editor for professionals on Windows 10.

It includes two-pane live preview, in-app uploads to imgur for image hosting, and MultiMarkdown table support.

Features you won't find anywhere else include...

You've wasted more than $15 of your time looking for a great Markdown editor.

Stop looking. MarkUpDown is the app you're looking for.

Learn more or head over to the 'Store now!

Tuesday, June 17, 2025

Okay, was looking back through WCF earlier this week for a prospective client, and figured I'd leave some notes.

Here's the stock code, give or take:

public interface IService
{
    [OperationContract]
    string GetData(int value);
    // ...
}
public class Service : IService
{
    public string GetData(int value)
    {
    	return string.Format("You entered: {0}", value);
    }

    // ...
}

And all that's in Service.svc is <%@ ServiceHost Language="C#" Debug="true" Service="Service" CodeBehind="~/App_Code/Service.cs" %>

Again, this is the stock WCF project so far. Not overly fancy.

I wanted to be able to hit the stock endpoint from a WCF project in Visual Studio without using a WCF client. Getting to the WSDL file was easy: You can steal that from the page the WCF Test Client refers to if nothing else.

For me, that stock page was:

http://localhost:60817/Service.svc

Here's some of the startup page's contents:

You have created a service.

To test this service, you will need to create a client and use it to call the service. You can do this using the svcutil.exe tool from the command line with the following syntax:

svcutil.exe http://localhost:60817/Service.svc?wsdl

You can also access the service description as a single file:

http://localhost:60817/Service.svc?singleWsdl

Then the WSDL itself:

http://localhost:60817/Service.svc?wsdl

<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions name="Service" targetNamespace="http://tempuri.org/"
	xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
	xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex"
	xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
	xmlns:wsa10="http://www.w3.org/2005/08/addressing"
	xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
	xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy"
	xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract"
	xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
	xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
	xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	xmlns:tns="http://tempuri.org/"
	xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
	xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
	xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
	<wsdl:types>
		<xsd:schema targetNamespace="http://tempuri.org/Imports">
			<xsd:import schemaLocation="http://localhost:60817/Service.svc?xsd=xsd0" namespace="http://tempuri.org/"/>
			<xsd:import schemaLocation="http://localhost:60817/Service.svc?xsd=xsd1" namespace="http://schemas.microsoft.com/2003/10/Serialization/"/>
			<xsd:import schemaLocation="http://localhost:60817/Service.svc?xsd=xsd2" namespace="http://schemas.datacontract.org/2004/07/"/>
		</xsd:schema>
	</wsdl:types>
	<wsdl:message name="IService_GetData_InputMessage">
		<wsdl:part name="parameters" element="tns:GetData"/>
	</wsdl:message>
	<wsdl:message name="IService_GetData_OutputMessage">
		<wsdl:part name="parameters" element="tns:GetDataResponse"/>
	</wsdl:message>
	<wsdl:message name="IService_GetDataUsingDataContract_InputMessage">
		<wsdl:part name="parameters" element="tns:GetDataUsingDataContract"/>
	</wsdl:message>
	<wsdl:message name="IService_GetDataUsingDataContract_OutputMessage">
		<wsdl:part name="parameters" element="tns:GetDataUsingDataContractResponse"/>
	</wsdl:message>
	<wsdl:portType name="IService">
		<wsdl:operation name="GetData">
			<wsdl:input wsaw:Action="http://tempuri.org/IService/GetData" message="tns:IService_GetData_InputMessage"/>
			<wsdl:output wsaw:Action="http://tempuri.org/IService/GetDataResponse" message="tns:IService_GetData_OutputMessage"/>
		</wsdl:operation>
		<wsdl:operation name="GetDataUsingDataContract">
			<wsdl:input wsaw:Action="http://tempuri.org/IService/GetDataUsingDataContract" message="tns:IService_GetDataUsingDataContract_InputMessage"/>
			<wsdl:output wsaw:Action="http://tempuri.org/IService/GetDataUsingDataContractResponse" message="tns:IService_GetDataUsingDataContract_OutputMessage"/>
		</wsdl:operation>
	</wsdl:portType>
	<wsdl:binding name="BasicHttpBinding_IService" type="tns:IService">
		<soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
		<wsdl:operation name="GetData">
<!-- ================================================================================= -->
<!-- This part is reasonably important later.                                                     -->
<!-- ================================================================================= -->
<soap:operation soapAction="http://tempuri.org/IService/GetData" style="document"/>
<!-- ================================================================================= -->
<!-- ================================================================================= -->
			<wsdl:input>
				<soap:body use="literal"/>
			</wsdl:input>
			<wsdl:output>
				<soap:body use="literal"/>
			</wsdl:output>
		</wsdl:operation>
		<wsdl:operation name="GetDataUsingDataContract">
			<soap:operation soapAction="http://tempuri.org/IService/GetDataUsingDataContract" style="document"/>
			<wsdl:input>
				<soap:body use="literal"/>
			</wsdl:input>
			<wsdl:output>
				<soap:body use="literal"/>
			</wsdl:output>
		</wsdl:operation>
	</wsdl:binding>
	<wsdl:service name="Service">
		<wsdl:port name="BasicHttpBinding_IService" binding="tns:BasicHttpBinding_IService">
			<soap:address location="http://localhost:60817/Service.svc"/>
		</wsdl:port>
	</wsdl:service>
</wsdl:definitions>

What you want to be able to do to invoke a service endpoint directly is, of course, find its URL. In my case, with a little help from SoapUI, I figured out it was the same URL that started up initially:

http://localhost:60817/Service.svc

The first key is that you have to set up two headers to the request:

  • Content-Type
    • In my case, text/xml
  • SOAPAction
    • So for me, based on the WSDL above, that's http://tempuri.org/IService/GetData for the GetData action.

But you seem to have to send it a body in a POST -- GET gives you that stock opening page again. (Since GET and POST are REST conventions, I half-way expected the WCF service not to care, especially since we know GET can have a body now.)

I am not yet going to claim I know how the WSDL tells you what your input parameters should have looked like.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
   <soapenv:Header/>
   <soapenv:Body>
      <tem:GetData>
         <!--Optional:-->
         <tem:value>5</tem:value>
      </tem:GetData>
   </soapenv:Body>
</soapenv:Envelope>

Again, do this all in a Postman POST request with a body that's raw with XML as its type and you're golden. Change tem:value to another int to see more insanely interesting messages like...

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Body>
        <GetDataResponse xmlns="http://tempuri.org/">
            <GetDataResult>You entered: 5</GetDataResult>
        </GetDataResponse>
    </s:Body>
</s:Envelope>

I do not miss XML.


Update: Ha! Wish I'd talked to myself from 13 years ago first!

Labels: , ,


posted by Jalindrine at 6/17/2025 07:27:00 PM
Thursday, May 03, 2012

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: , , ,


posted by ruffin at 5/03/2012 03:52:00 PM

Creating a WCF service that consumes an ASMX service:

I am looking for guidelines on which "project type" to use in getting started via Visual Studio 2008 and C#. Here the specific need is having my WCF service call an ASMX service.
I am looking for guidelines on which "project type" to use in getting started via Visual Studio 2008 and C#. Here the specific need is having my WCF service call an ASMX service.
Choice 1. Create Project; Visual C#; WCF; select WCF Service Library.
  • Described as "A project for creating a WCF service class library (.dll)".
  • Gives an app.config
  • does not expose the "Add Web Reference" choice which I need. 
  • Debug brings up a WCF Service Host and a WCF Test Client automatically.
Choice 2. Create Project; Visual C#; Web; select WCF Service Application.
  • Described as "A project for creating WCF services".
  • Yields a web.config
  • exposes the "Add Web Reference" dialog choice (which is good for my requirement I think). 
  • The web.config does NOT include a in the service definition.
  • Debug only brings up an ASP.NET Development Server and a browser window to http://localhost:3648/Service1.svc?wsdl

Labels:


posted by ruffin at 5/03/2012 11:28:00 AM

<< Older | Newer >>


Support freedom
All posts can be accessed here:


Just the last year o' posts:

URLs I want to remember:
* Atari 2600 programming on your Mac
* joel on software (tip pt)
* Professional links: resume, github, paltry StackOverflow * Regular Expression Introduction (copy)
* The hex editor whose name I forget
* JSONLint to pretty-ify JSON
* Using CommonDialog in VB 6 * Free zip utils
* git repo mapped drive setup * Regex Tester
* Read the bits about the zone * Find column in sql server db by name
* Giant ASCII Textifier in Stick Figures (in Ivrit) * Quick intro to Javascript
* Don't [over-]sweat "micro-optimization" * Parsing str's in VB6
* .ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture); (src) * Break on a Lenovo T430: Fn+Alt+B
email if ya gotta, RSS if ya wanna RSS, (?_?), ยข, & ? if you're keypadless


Powered by Blogger etree.org Curmudgeon Gamer badge
The postings on this site are [usually] my own and do not necessarily reflect the views of any employer, past or present, or other entity.