Cannot provide REST XML, REST JSON and SOAP properly via WCF-only configuration

I want to be able to create a WCF service that provides the following:

  • POINTS JSON
  • RESTfully provides XML
  • Provides SOAP
  • Other bindings as needed

The key point here is that I want to do this all through configuration , and only one function written in code for each method required, instead of having to code separately ResponseFormat=ResponseFormat.Json

or ResponseFormat=ResponseFormat.Xml

above separate functions for RESTful methods. I've done a lot of research and I can't seem to find anything solid if it's possible solely by configuration.

The weird thing is, when I build a project, the RESTful methods work when I access them via the url, but the WSDL throws an error - that is, if someone wants to reference / consume the service via SOAP, it will fail when the WSDL import step.

Below is the code for the service as well as the configuration and the service is hosted locally for testing at http: // localhost / WCF . The following 2 RESTful calls succeed and return successful XML and JSON in the browser:

local / WCF / service1.svc / json / students

local / wcf / service1.svc / leisure / students

But, if I call http: //localhost/WCF/service1.svc? Wsdl I get below error. If I remove one of the webhttpBinding endpoint configurations, the WSDL works and displays correctly, and therefore can be referenced.

Is it not possible to have multiple webHttpBindings in the configuration (i.e. has to be done with separate methods and attributes like ResponseFormat=ResponseFormat.Json

) to generate the WSDL? Or am I configured incorrectly here?

Any help that would be appreciated, it would be much easier to do this via config, which is additional functionality with the specified serialization. Thank.

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; }
    }
}

      

+3


source to share


3 answers


This is a known issue where having json, xml and soap endpoints for the same service throws an exception.

I raised it as a bug on MS Connect .

If you switch your Framework to 3.5 then this will work or a workaround is provided as well. You can simply get the content header from the request object and define that in your code to set the response format and send the response accordingly.



Also in the web API, the framework automatically detects this based on the content type if nothing is specified in your WebGet / WebInvoke attributes and returns a response accordingly.

NOTE . The moment I try to open the MS Connect link, I get some system error on the MS Connect site. If you want a workaround to let me know, I can post it. Also MS confirmed that they are not going to fix this as not many clients will want to expose all 3 formats on one service.

+3


source


Hmmm, I created a very simple service that works with SOAP, JSON and XML endpoints. Does it only work because of its simplicity?

I found out that I had to declare separate binding configurations (albeit empty ones) for json and xml. Here is the configuration (updated because previously only the system.serviceModel section was visible):

<?xml version="1.0"?>
<configuration>

  <system.web>
    <compilation debug="true" targetFramework="4.0"/>
  </system.web>

  <system.serviceModel>

    <bindings>
      <webHttpBinding>
        <!-- separate bindings are necessary -->
        <binding name="jsonBinding"/>
        <binding name="xmlBinding"/>
      </webHttpBinding>
    </bindings>

    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>

      <endpointBehaviors>
        <behavior name="xmlEndpoint">
          <webHttp helpEnabled="true" defaultOutgoingResponseFormat="Xml"/>
        </behavior>
        <behavior name="jsonEndpoint">
          <!-- do not specify enableWebScript or UriTemplate will not work -->
          <webHttp helpEnabled="true" defaultOutgoingResponseFormat="Json"/>
        </behavior>
      </endpointBehaviors>
    </behaviors>

    <services>
      <service name="Services.Addressbook.AddressbookService">
        <endpoint address="" binding="basicHttpBinding" name="soap" contract="Services.Addressbook.IAddressbookService" />
        <endpoint address="mex" binding="mexHttpBinding" name="mex" contract="IMetadataExchange" />
        <endpoint address="json" behaviorConfiguration="jsonEndpoint" binding="webHttpBinding" bindingConfiguration="jsonBinding" name="restJson" contract="Services.Addressbook.IAddressbookService" />
        <endpoint address="xml" behaviorConfiguration="xmlEndpoint" binding="webHttpBinding" bindingConfiguration="xmlBinding" name="restXml" contract="Services.Addressbook.IAddressbookService" />
      </service>
    </services>

    <serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>

  </system.serviceModel>

  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>

</configuration>

      

This is a service contract:



[ServiceContract]
public interface IAddressbookService {
  [OperationContract]
  [WebGet(UriTemplate = "Version")]
  string GetVersion();

  [OperationContract]
  [WebGet(UriTemplate = "Entries/Count")]
  int GetEntriesCount();

  [OperationContract]
  [WebGet(UriTemplate = "Entries")]
  Address[] GetEntries();

  [OperationContract]
  [WebGet(UriTemplate = "Entries/{name}")]
  Address[] SearchEntriesByName(string name);

  [OperationContract]
  [WebInvoke(Method = "POST", UriTemplate = "Entries/Add")]
  bool AddEntry(Address entry);

  [OperationContract]
  [WebInvoke(Method = "DELETE", UriTemplate = "Entries/Remove")]
  bool RemoveEntry(Address entry);
}

      

And here is the data contract:

[DataContract]
public class Address {
  [DataMember]
  public string FirstName { get; set; }
  [DataMember]
  public string LastName { get; set; }
  [DataMember]
  public string Street { get; set; }
  [DataMember]
  public string City { get; set; }
  [DataMember]
  public int ZipCode { get; set; }
  [DataMember]
  public string Country { get; set; }
  [DataMember]
  public string Phone { get; set; }
  [DataMember]
  public string Email { get; set; }
}

      

+2


source


Try this

<bindings>
  <webHttpBinding>
    <binding name="webBindingXML"></binding>
    <binding name="webBindingSOAP"></binding>
  </webHttpBinding>
</bindings>

      

+1


source







All Articles