I'm wanting to be able to build a WCF service which achieves the following:
The key here, is that I want to do this all via configuration, and only one function written in code for each required method, rather than having to specify separately in code ResponseFormat=ResponseFormat.Json or ResponseFormat=ResponseFormat.Xml above separate functions for the RESTful methods. I've done plenty of research and I can't find anything solid for whether this is possible purely by configuration.
The strange thing is, when I build the project the RESTful methods work when I access them via URL, but the WSDL throws an error - i.e. if someone wanted to reference/consume the service via SOAP it would fail at the WSDL import step.
The code for the service is below, as well as the configuration, and the service is hosted locally for testing under http://localhost/WCF. The following 2 RESTful calls work succesfully and return successful XML and JSON in the browser:
localhost/WCF/service1.svc/json/students
localhost/WCF/service1.svc/rest/students
But, if I call http://localhost/WCF/service1.svc?wsdl I get the error below. If I remove one of the webhttpBinding endpoint configurations, the WSDL works and displays correctly, and can therefore be referenced.
Is it just not possible to have multiple webHttpBindings in the config (i.e. has to be done via separate methods and attributes such as ResponseFormat=ResponseFormat.Json) for WSDL generation to be valid? Or have I configured something incorrectly here?
Any help appreciated, would be so much more straightforward to do it via config that extra functions with serialization specified. Thanks.
WSDL generation error
An ExceptionDetail, likely created by IncludeExceptionDetailInFaults=true, whose value is:
System.NullReferenceException: Object reference not set to an instance of an object.
at System.ServiceModel.Description.WsdlExporter.CreateWsdlBindingAndPort(ServiceEndpoint endpoint, XmlQualifiedName wsdlServiceQName, Port& wsdlPort, Boolean& newBinding, Boolean& bindingNameWasUniquified)
at System.ServiceModel.Description.WsdlExporter.ExportEndpoint(ServiceEndpoint endpoint, XmlQualifiedName wsdlServiceQName)
at System.ServiceModel.Description.WsdlExporter.ExportEndpoints(IEnumerable`1 endpoints, XmlQualifiedName wsdlServiceQName)
at System.ServiceModel.Description.ServiceMetadataBehavior.MetadataExtensionInitializer.GenerateMetadata()
at System.ServiceModel.Description.ServiceMetadataExtension.EnsureInitialized()
at System.ServiceModel.Description.ServiceMetadataExtension.get_Metadata()
at System.ServiceModel.Description.ServiceMetadataExtension.HttpGetImpl.InitializationData.InitializeFrom(ServiceMetadataExtension extension)
at System.ServiceModel.Description.ServiceMetadataExtension.HttpGetImpl.GetInitData()
at System.ServiceModel.Description.ServiceMetadataExtension.HttpGetImpl.TryHandleMetadataRequest(Message httpGetRequest, String[] queries, Message& replyMessage)
at System.ServiceModel.Description.ServiceMetadataExtension.HttpGetImpl.ProcessHttpRequest(Message httpGetRequest)
at SyncInvokeGet(Object , Object[] , Object[] )
at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
Web.config
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<services>
<service behaviorConfiguration="httpGetBehavior" name="WCF.Service1">
<endpoint address="json" binding="webHttpBinding" name="jsonRest" contract="WCF.IService1" behaviorConfiguration="jsonBehavior"></endpoint>
<endpoint address="rest" binding="webHttpBinding" name="xmlRest" contract="WCF.IService1" behaviorConfiguration="restBehaviour"></endpoint>
<endpoint address="soap" binding="basicHttpBinding" name="soap" contract="WCF.IService1"></endpoint>
<endpoint name="mex" address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
<host>
<baseAddresses>
<add baseAddress="http://localhost/WCF"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="httpGetBehavior">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="jsonBehavior">
<webHttp defaultOutgoingResponseFormat="Json"/>
</behavior>
<behavior name="restBehaviour">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
Code
public class Service1 : IService1
{
public IList<Student> GetStudents()
{
IList<Student> students = new List<Student>();
students.Add(new Student() { FirstNme = "Bob", LastName = "Long", StudentId = 1, Subject = "Economics" });
students.Add(new Student() { FirstNme = "Jack", LastName = "Short", StudentId = 2, Subject = "IT" });
return students;
}
}
[ServiceContract]
public interface IService1
{
[WebGet(UriTemplate = "/students")]
[OperationContract]
IList<Student> GetStudents();
}
[DataContract]
public class Student
{
private int _studentId;
private string _firstName;
private string _lastName;
private string _subject;
[DataMember]
public int StudentId
{
get { return _studentId; }
set { _studentId = value; }
}
[DataMember]
public string FirstNme
{
get { return _firstName; }
set { _firstName = value; }
}
[DataMember]
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
[DataMember]
public string Subject
{
get { return _subject; }
set { _subject = value; }
}
}
This is a known issue where having json, xml and soap endpoints for a single service throws an exception.
I have raised it as an error on MS Connect.
If you switch your Framework to 3.5 then that would work or an workaround is provided as well. You can just get the content-header from the request object and determine that in your code to set the response format and send the response back accordingly.
Also in the Web API, the framework automatically determines this based on the content type if nothing is specified in your WebGet/WebInvoke attributes and returns the response accordingly.
NOTE: At the moment when i try to open the link on MS Connect I get some system error on the MS Connect site. If you would like the workaround let me know i can send it across. Also MS has confirmed that they are not going to Fix it as not many clients would want to expose all the 3 formats on a single service.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With