I have a WCF service (target .NET 4) and I need to expose 4 endpoints: SOAP, POX, JSON & MEX.
My configuration file looks like this:
<system.serviceModel>
<!-- bindings -->
<bindings>
<basicHttpBinding>
<binding name ="soapBinding">
<security mode="None">
</security>
</binding>
</basicHttpBinding>
<webHttpBinding>
<binding name="webBinding">
</binding>
</webHttpBinding>
</bindings>
<!-- behaviors -->
<behaviors>
<endpointBehaviors>
<!-- plain old XML -->
<behavior name="poxBehavior">
<webHttp/>
</behavior>
<!-- JSON -->
<behavior name="jsonBehavior">
<enableWebScript />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="defaultBehavior">
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="PubtranService.Service" behaviorConfiguration="defaultBehavior">
<host>
<baseAddresses>
<add baseAddress="http://192.168.1.102:8080/PT/"/>
</baseAddresses>
</host>
<endpoint name="soap"
address="soap"
binding="basicHttpBinding"
bindingConfiguration="soapBinding"
contract="PubtranService.IService" />
<endpoint name="pox"
address="pox"
binding="webHttpBinding"
bindingConfiguration="webBinding"
behaviorConfiguration="poxBehavior"
contract="PubtranService.IService" />
<endpoint name="json"
address="json"
binding="webHttpBinding"
bindingConfiguration="webBinding"
behaviorConfiguration="jsonBehavior"
contract="PubtranService.IService" />
<endpoint name="mex"
address="mex"
binding="mexHttpBinding"
bindingConfiguration=""
contract="IMetadataExchange"/>
</service>
</services>
</system.serviceModel>
And when I run the service the WCF Test Client returns failed to add service msg with a System.NullReferenceException
- object reference not set to an instance of an object and a stack trace:
System.ServiceModel.Description.ServiceMetadataBehavior.MetadataExtensionInitializer.GenerateMetadata()
System.ServiceModel.Description.ServiceMetadataExtension.EnsureInitialized() System.ServiceModel.Description.ServiceMetadataExtension.WSMexImpl.GatherMetadata(String dialect, String identifier) System.ServiceModel.Description.ServiceMetadataExtension.WSMexImpl.Get(Message request)
SyncInvokeGet(Object , Object[] , Object[] )
System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc)
System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
But the JSON and POX endpoints are accessible and work just fine. On the other hand when I comment them out from the configuration the WCF Test Client properly shows the SOAP service.
At the moment there is only one simple method implemented in the service:
[ServiceContract(Namespace="http://192.168.1.102:8080/")]
public interface IService
{
[OperationContract]
[WebGet]
Tram[] GetOverview(string id);
}
Why do I brake the mex endpoint by adding the pox and json endpoints?
If your aim is to have 1 method to serve for both xml and json i could achieve it as shown below:
<service name ="XMLService.MultipleEndpointService">
<endpoint name="soap" address="soap" binding="basicHttpBinding" contract="XMLService.IMultipleEndpointService" />
<endpoint name="pox" address="pox" binding="webHttpBinding" behaviorConfiguration="web" contract="XMLService.IMultipleEndpointService" />
<endpoint name="mex" address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
<endpointBehaviors>
<behavior name="web">
<webHttp/>
</behavior>
</endpointBehaviors>
Now in your class that implements the Interface i did the following:
public Tram[] GetOverview(string id)
{
IList<ContentType> acceptHeaderElements = WebOperationContext.Current.IncomingRequest.GetAcceptHeaderElements();
for (int x = 0; x < acceptHeaderElements.Count; x++)
{
string normalizedMediaType = acceptHeaderElements[x].MediaType.ToLowerInvariant();
switch (normalizedMediaType)
{
case "application/xml":
WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Xml;
break;
case "application/json":
WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Json;
break;
}
}
return new[] { new Tram() { TramName = "Tram 1" }, new Tram() { TramName = "Tram 2" } };
}
Now when i perform a get on the above service using Fiddler :
GET http://localhost/XMLService/multipleendpointservice.svc/pox/GetOverview HTTP/1.1
User-Agent: Fiddler
Host: localhost
Accept: application/json
My response is
HTTP/1.1 200 OK
Cache-Control: private
Content-Length: 79
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Wed, 30 Nov 2011 11:39:16 GMT
[{"<TramName>k__BackingField":"Tram 1"},{"<TramName>k__BackingField":"Tram 2"}]
Similarly when i perform :
GET http://localhost/XMLService/multipleendpointservice.svc/pox/GetOverview HTTP/1.1
User-Agent: Fiddler
Host: localhost
Accept: application/xml
My response is :
HTTP/1.1 200 OK
Cache-Control: private
Content-Length: 334
Content-Type: application/xml; charset=utf-8
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Wed, 30 Nov 2011 11:39:09 GMT
<ArrayOfTram xmlns="http://schemas.datacontract.org/2004/07/XMLService" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Tram><_x003C_TramName_x003E_k__BackingField>Tram 1</_x003C_TramName_x003E_k__BackingField></Tram><Tram><_x003C_TramName_x003E_k__BackingField>Tram 2</_x003C_TramName_x003E_k__BackingField></Tram></ArrayOfTram>
NOTE: The Accept header can be other than application/xml or application/json. in such cases you need to handle it.
The other alternative is to use the WebServiceHostFactory and use the webHttpEndpoint element that has the automaticFormatSelection attribute to perform the above action