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
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: c#, noteToSelf, wcf