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