locked
Issue with C# WCF REST Web Service accepting XML data RRS feed

  • Question

  • User1685873537 posted

    I have created a basic WCF REST web service in C#, and I need it to accept a content type of "application/xml" in an HTTP POST. The code I have will only work if the content type of the POST is "application/x-www-form-urlencode", and when I try to make the POST data content type of "application/xml", I get a 400 error message.

    The WCF web service is using a behaviorConfiguration of "RestBehavior" and a binding of "webHttpBinding".

    The client code that is composing and sending the post is like the following:

     

    HttpWebRequest request = (HttpWebRequest) WebRequest.Create(GetURL());
    request.Method = "POST";
    request.ContentLength = byteArray.Length;
    request.ContentType = "application/xml";
    Stream dataStream = request.GetRequestStream();
    dataStream.Write(byteArray, 0, byteArray.Length);
    dataStream.Close();
    HttpWebResponse response = (HttpWebResponse) request.GetResponse();
    Stream data = response.GetResponseStream();
    response.Close();

    When I change the content type to "application/x-www-form-urlencoded" it works, but this client code  with "application/xml" here gets a 400 error from the web service. I need this to work with content type of "application/xml".

    I am thinking it is something simple, Please Help!

    Tuesday, July 16, 2013 2:04 PM

Answers

  • User220959680 posted

    The code I have will only work if the content type of the POST is "application/x-www-form-urlencode", and when I try to make the POST data content type of "application/xml", I get a 400 error message.

    Reproduced the exception with below sample.

    ServiceContract

     [OperationContract]
    [WebInvoke(Method = "POST", UriTemplate = "processcoordinates",
    RequestFormat = WebMessageFormat.Xml, ResponseFormat = WebMessageFormat.Xml,
    BodyStyle = WebMessageBodyStyle.Bare)]
    
    string processcoordinates(Stream input);
    

    Implementation

    public string processcoordinates(Stream input)
            {
                return string.Format("The input string is: {0}", DateTime.Now.ToString());
    
            }

    Client

    string requestData = "<input>9</input>";
                byte[] data = Encoding.UTF8.GetBytes(requestData);
                request = (HttpWebRequest)WebRequest.Create("http://localhost:53641/Service1.svc/processcoordinates");
                request.Method = "POST";
                //request.ContentType = "application/xml";
                
                Stream dataStream = request.GetRequestStream();
                dataStream.Write(data, 0, data.Length);
                dataStream.Close();
    
                HttpWebResponse resp = (HttpWebResponse) request.GetResponse();
                result = new StreamReader(response.GetResponseStream()).ReadToEnd();
                Console.WriteLine(resp.StatusCode.ToString());

    Note when the contentType is commented it works without any exceptions in client source.

    Digging deeper using Google drawn attention to these code snippets from Carols where contentType="text/plain"

    http://codepaste.net/s5hwyv

    http://codepaste.net/xtx3ho

    Gone through How to: Create a Service That Accepts Arbitrary Data using the WCF REST Programming Model , which is useful. BUT out of scope towards the contentType: application/xml.

    Modified above posted service to accept an object in XML (source below), Json format in POST request, successsfully tested in Fiddler

    service Namespace in the POST message is very important. Incorrect xmlns throws HTTP 400 exception.

    Service contract

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.ServiceModel.Web;
    using System.Text;
    
    namespace WcfService2
    {
        // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
        [ServiceContract(Namespace="http://www.test.com/Enrollment")]
        public interface IService1
        {
    
            [OperationContract]
            [WebInvoke(Method = "POST", UriTemplate = "/processcoordinates", RequestFormat= WebMessageFormat.Xml,
            BodyStyle=WebMessageBodyStyle.Bare)]
            string HelloWorld(person content);
    
        }
    
    //POST an instance of person in XML format  
        [DataContract(Namespace = "http://www.test.com/Enrollment")]
        public class person
        {
            [DataMember]
            public string id { get; set; }
            [DataMember]
            public string firstName { get; set; }
            
        }
    }
    

    Service implementation

    public class Service1 : IService1
        {
            public string HelloWorld(person content)
            {
                return string.Format("person object has been posted");
            }
    
        }

    service Web.config


    <?xml version="1.0"?>
    <configuration>
    
      <appSettings>
        <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
      </appSettings>
      <system.web>
        <compilation debug="true" targetFramework="4.5" />
        <httpRuntime targetFramework="4.5"/>
      </system.web>
      <system.serviceModel>
        <behaviors>
          <serviceBehaviors>
            <behavior>
              <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
              <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
              <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
              <serviceDebug includeExceptionDetailInFaults="true"/>
            </behavior>
          </serviceBehaviors>
        <endpointBehaviors>
            <behavior name="RestBehavior">
              <webHttp helpEnabled="true" automaticFormatSelectionEnabled="true" defaultOutgoingResponseFormat="Xml" />
            </behavior>
          </endpointBehaviors>
        </behaviors>
        
        <services>
          <service name="WcfService2.Service1">
            <endpoint behaviorConfiguration="RestBehavior"  binding="webHttpBinding" name="REST" contract="WcfService2.IService1"  />
          </service>
        </services>
          
        <protocolMapping>
            <add binding="basicHttpsBinding" scheme="http" />
        </protocolMapping>    
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
      </system.serviceModel>
      <system.webServer>
        <modules runAllManagedModulesForAllRequests="true"/>
        <!--
            To browse web app root directory during debugging, set the value below to true.
            Set to false before deployment to avoid disclosing web app folder information.
          -->
        <directoryBrowse enabled="true"/>
      </system.webServer>
    
    </configuration>
    

    Please note that enabling help [helpEnabled] in endPointBehavior generates service help page, which can be accessed by navigating to the url http://localhost:53641/Service1.svc/help

    <endpointBehaviors>
            <behavior name="RestBehavior">
              <webHttp helpEnabled="true" automaticFormatSelectionEnabled="true" defaultOutgoingResponseFormat="Xml" />
            </behavior>
          </endpointBehaviors>
        

    Selecting the service method on service help page provides more information as copied below.



    Below is the Fiddler screenshot that is used to test the above service sucessfully. Service operation response as evident is

    string [ xmlns=http://schemas.microsoft.com/2003/10/Serialization/ ] person object posted

    Troubleshooting steps

    1. Make sure xmlns is POSTED in the service request.

    2. Enable tracing for the service. Refer my response on this thread http://forums.asp.net/t/1888153.aspx/1

    3. use Fiddler to debug the service to step through the implementation. 
    http://fiddler2.com/blog/blog/2013/01/08/capturing-traffic-from-.net-services-with-fiddler

    Let us know further queries, when this is resolved please share the resolution to help the community .





    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Sunday, July 21, 2013 11:56 PM

All replies

  • User220959680 posted

    Operation in service contract should have attribute RequestFormat= WebMessageFormat.xml

    [OperationContract]
    [WebInvoke(Method = "POST", UriTemplate = "Push",
    RequestFormat = WebMessageFormat.Xml,ResponseFormat = WebMessageFormat.Xml,
    BodyStyle = WebMessageBodyStyle.Bare)]
    XElement DoWork(Stream xml);



    Tuesday, July 16, 2013 2:38 PM
  • User1685873537 posted

    I implemented those changes like you suggested, but the WCF is still returning a 400 error, is there some additional steps I should take with Web.Config?

    Tuesday, July 16, 2013 3:47 PM
  • User220959680 posted

    Web.Config?

    Binding should be WebHttpBinding

    <configuration>
       <system.serviceModel>
         <services>
            <service name="BookmarkService">
                <endpoint binding="webHttpBinding" contract="BookmarkService"
                          behaviorConfiguration="webHttp"/>
            </service>
         </services>
         <behaviors>
            <endpointBehaviors>
                <behavior name="webHttp">
                    <webHttp/>
                </behavior>
            </endpointBehaviors>
         </behaviors>
      </system.serviceModel>
    <configuration>

    when the above does NOT resolve the issue, please post your web.config and the service method implementation.

    Tuesday, July 16, 2013 4:38 PM
  • User1685873537 posted

    Thanks for trying to help, I appreciate it very much :-)

    However it appears that my Web.Config is possibly exactly the same as yours, using WebHttpBinding. One thing I was wondering about, when the content type is "application/x-www-form-urlencode", C# is automatically placing the required HTTP headers for that kind of data, but with the content type is "application/xml" I am thinking maybe there is something missing from the headers? and that is the reason the the WCF service is rejecting the data?

    Here is the information from my Web.Config

    <?xml version="1.0"?>
    <configuration>
      <appSettings>
        <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
      </appSettings>
      <system.web>
        <compilation debug="true" targetFramework="4.5" />
        <httpRuntime targetFramework="4.5"/>
      </system.web>
      <system.serviceModel>
        <behaviors>
          <serviceBehaviors>
            <behavior>
              <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
              <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
              <serviceDebug includeExceptionDetailInFaults="false"/>
            </behavior>
          </serviceBehaviors>
          <endpointBehaviors>
            <behavior name="RestBehavior">
              <webHttp />
            </behavior>
          </endpointBehaviors>      
        </behaviors>
        <services>
          <service name="Service1">
            <endpoint behaviorConfiguration="RestBehavior" binding="webHttpBinding" name="REST" contract="IService1" />
          </service>
        </services>   
        <protocolMapping>
            <add binding="basicHttpsBinding" scheme="https" />
        </protocolMapping>    
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
      </system.serviceModel>
      <system.webServer>
    </configuration>
    

     

    Wednesday, July 17, 2013 1:58 PM
  • User220959680 posted

    Michael G. Workman

    <serviceDebug includeExceptionDetailInFaults="false"/>

    Set the above to "true" while troubleshooting.

    Michael G. Workman

    <protocolMapping>
            <add binding="basicHttpsBinding" scheme="https" />
        </protocolMapping>

    comment protocolMapping section or change the protocolMapping to 

    <protocolMapping>
            <add scheme="http" binding="basicHttpBinding"/>
    </protocolMapping>

    Please post your service POST mehod from service contract i.e., Interface when the above does not resolve the issue.

    It should be similar to the below.

    [OperationContract]
    [WebInvoke(Method = "POST", UriTemplate = "Push",
    RequestFormat = WebMessageFormat.Xml,ResponseFormat = WebMessageFormat.Xml,
    BodyStyle = WebMessageBodyStyle.Bare)]
    XElement DoWork(Stream xml);

    Also in Client that invoking the service change

    request.ContentType = "application/xml";
    TO
    request.ContentType = "application/xml;charset=UTF-8";

    Please use web debugger Fiddler to step through your service.

    Wednesday, July 17, 2013 6:17 PM
  • User1685873537 posted

    Thank you very much for your help, however the WCF webservice is still not working, here is the service contract I am using:

        [ServiceContract]
        public interface IService1
        {
            [OperationContract]
            [WebInvoke(Method = "POST", UriTemplate="processcoordinates",
                        RequestFormat = WebMessageFormat.Xml,
                        ResponseFormat = WebMessageFormat.Xml,
                        BodyStyle=WebMessageBodyStyle.Bare)]
            XElement processcoordinates(Stream input);
        }


     

    Thursday, July 18, 2013 11:08 AM
  • User220959680 posted

    The code I have will only work if the content type of the POST is "application/x-www-form-urlencode", and when I try to make the POST data content type of "application/xml", I get a 400 error message.

    Reproduced the exception with below sample.

    ServiceContract

     [OperationContract]
    [WebInvoke(Method = "POST", UriTemplate = "processcoordinates",
    RequestFormat = WebMessageFormat.Xml, ResponseFormat = WebMessageFormat.Xml,
    BodyStyle = WebMessageBodyStyle.Bare)]
    
    string processcoordinates(Stream input);
    

    Implementation

    public string processcoordinates(Stream input)
            {
                return string.Format("The input string is: {0}", DateTime.Now.ToString());
    
            }

    Client

    string requestData = "<input>9</input>";
                byte[] data = Encoding.UTF8.GetBytes(requestData);
                request = (HttpWebRequest)WebRequest.Create("http://localhost:53641/Service1.svc/processcoordinates");
                request.Method = "POST";
                //request.ContentType = "application/xml";
                
                Stream dataStream = request.GetRequestStream();
                dataStream.Write(data, 0, data.Length);
                dataStream.Close();
    
                HttpWebResponse resp = (HttpWebResponse) request.GetResponse();
                result = new StreamReader(response.GetResponseStream()).ReadToEnd();
                Console.WriteLine(resp.StatusCode.ToString());

    Note when the contentType is commented it works without any exceptions in client source.

    Digging deeper using Google drawn attention to these code snippets from Carols where contentType="text/plain"

    http://codepaste.net/s5hwyv

    http://codepaste.net/xtx3ho

    Gone through How to: Create a Service That Accepts Arbitrary Data using the WCF REST Programming Model , which is useful. BUT out of scope towards the contentType: application/xml.

    Modified above posted service to accept an object in XML (source below), Json format in POST request, successsfully tested in Fiddler

    service Namespace in the POST message is very important. Incorrect xmlns throws HTTP 400 exception.

    Service contract

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.ServiceModel.Web;
    using System.Text;
    
    namespace WcfService2
    {
        // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
        [ServiceContract(Namespace="http://www.test.com/Enrollment")]
        public interface IService1
        {
    
            [OperationContract]
            [WebInvoke(Method = "POST", UriTemplate = "/processcoordinates", RequestFormat= WebMessageFormat.Xml,
            BodyStyle=WebMessageBodyStyle.Bare)]
            string HelloWorld(person content);
    
        }
    
    //POST an instance of person in XML format  
        [DataContract(Namespace = "http://www.test.com/Enrollment")]
        public class person
        {
            [DataMember]
            public string id { get; set; }
            [DataMember]
            public string firstName { get; set; }
            
        }
    }
    

    Service implementation

    public class Service1 : IService1
        {
            public string HelloWorld(person content)
            {
                return string.Format("person object has been posted");
            }
    
        }

    service Web.config


    <?xml version="1.0"?>
    <configuration>
    
      <appSettings>
        <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
      </appSettings>
      <system.web>
        <compilation debug="true" targetFramework="4.5" />
        <httpRuntime targetFramework="4.5"/>
      </system.web>
      <system.serviceModel>
        <behaviors>
          <serviceBehaviors>
            <behavior>
              <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
              <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
              <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
              <serviceDebug includeExceptionDetailInFaults="true"/>
            </behavior>
          </serviceBehaviors>
        <endpointBehaviors>
            <behavior name="RestBehavior">
              <webHttp helpEnabled="true" automaticFormatSelectionEnabled="true" defaultOutgoingResponseFormat="Xml" />
            </behavior>
          </endpointBehaviors>
        </behaviors>
        
        <services>
          <service name="WcfService2.Service1">
            <endpoint behaviorConfiguration="RestBehavior"  binding="webHttpBinding" name="REST" contract="WcfService2.IService1"  />
          </service>
        </services>
          
        <protocolMapping>
            <add binding="basicHttpsBinding" scheme="http" />
        </protocolMapping>    
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
      </system.serviceModel>
      <system.webServer>
        <modules runAllManagedModulesForAllRequests="true"/>
        <!--
            To browse web app root directory during debugging, set the value below to true.
            Set to false before deployment to avoid disclosing web app folder information.
          -->
        <directoryBrowse enabled="true"/>
      </system.webServer>
    
    </configuration>
    

    Please note that enabling help [helpEnabled] in endPointBehavior generates service help page, which can be accessed by navigating to the url http://localhost:53641/Service1.svc/help

    <endpointBehaviors>
            <behavior name="RestBehavior">
              <webHttp helpEnabled="true" automaticFormatSelectionEnabled="true" defaultOutgoingResponseFormat="Xml" />
            </behavior>
          </endpointBehaviors>
        

    Selecting the service method on service help page provides more information as copied below.



    Below is the Fiddler screenshot that is used to test the above service sucessfully. Service operation response as evident is

    string [ xmlns=http://schemas.microsoft.com/2003/10/Serialization/ ] person object posted

    Troubleshooting steps

    1. Make sure xmlns is POSTED in the service request.

    2. Enable tracing for the service. Refer my response on this thread http://forums.asp.net/t/1888153.aspx/1

    3. use Fiddler to debug the service to step through the implementation. 
    http://fiddler2.com/blog/blog/2013/01/08/capturing-traffic-from-.net-services-with-fiddler

    Let us know further queries, when this is resolved please share the resolution to help the community .





    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Sunday, July 21, 2013 11:56 PM
  • User-429884439 posted

    Hi,

    i faced the same problem with the POST service.

    I used all suggestion from the post above, but still getting 400 bad request error sending application/xml data.

    Moreover i can't get the Response format to be XML... here is what i'm getting

    Request Unknown The Request body is a byte stream.

    I guess there is a problems with web.config i just can't figure it out.

    Could u please help me?!

    Here is my web.config

    <system.serviceModel>
        <behaviors>
          <endpointBehaviors>
            <behavior name="RestBehavior">
              <webHttp automaticFormatSelectionEnabled="true" helpEnabled="true" defaultBodyStyle="Bare" defaultOutgoingResponseFormat="Xml"/>
                 </behavior>
          </endpointBehaviors>
          <serviceBehaviors>
            </serviceBehaviors>
        </behaviors>
        
        <bindings>
          <webHttpBinding>
            <binding name="RESTBINDING" transferMode="Streamed">
            </binding>
          </webHttpBinding>
        </bindings>
        <services>
         
          <service name="Service_Gateway.Service">
            <endpoint behaviorConfiguration="RestBehavior" binding="webHttpBinding" bindingConfiguration="RESTBINDING" name="REST" contract="Service_Gateway.IService"/>
          </service>
        </services>
        <serviceHostingEnvironment aspNetCompatibilityEnabled="false" multipleSiteBindingsEnabled="true" />
      </system.serviceModel>

    Interface:

    [OperationContract]
            [WebInvoke(Method = "POST", RequestFormat= WebMessageFormat.Xml,
               BodyStyle = WebMessageBodyStyle.Bare,
               ResponseFormat = WebMessageFormat.Xml,
               UriTemplate = "{datapointsURI}")]
            string ServicePost(string datapointsURI, Stream XMLmsg);

    Thanks in advance,

    Liudmila

    Wednesday, October 9, 2013 6:55 AM