none
405 Method not allowed error hosting RESTful WCF service under Azure Worker Role

    Question

  • I'm developing the simplest RESTful WCF service in a Worker Role which when deployed to production works perfectly well when using the physical IP address within the Azure VM, but when I use the fully qualified service URL (even within the VM) I get a 405 Method Not Allowed error, inspecting with fiddler the RAW response specified only a POST operation is allowed.

    http://10.333.222.111:8080/MyService/Info  - works and returns the proper XML response

    http://mynamespace.cloudapp.net:8080/MyService/Info - 405 error

    I’ve created a Worker Role startup permissions elevator, I’ve messed with all permutations of the obvious suspects, HostNameComparisonMode, AddressFilterMode, but to no avail, any ideas?

      <WorkerRole name="WorkerRole1" vmsize="Small">

       <Startup>

          <Task commandLine="AzurePortDetector.exe" executionContext="elevated" taskType="simple"></Task>

        </Startup>

        <Endpoints>

          <InputEndpoint name="HttpInput" protocol="http" port="8080" />

        </Endpoints>

      </WorkerRole>



    Uri httpUri = new Uri(String.Format("http://{0}/{1}",RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["HttpInput"].IPEndpoint.ToString(),"MyService"));

    _serviceHost = newServiceHost(typeof(MyService), httpUri);

    WebHttpBinding binding = new WebHttpBinding(WebHttpSecurityMode.None) { HostNameComparisonMode = HostNameComparisonMode.Exact };

    ServiceEndpoint se = _serviceHost.AddServiceEndpoint(typeof(IMyService), binding, "");               

    ServiceBehaviorAttribute sa = _serviceHost.Description.Behaviors.Find<ServiceBehaviorAttribute>();

    sa.AddressFilterMode = AddressFilterMode.Any;

    se.Behaviors.Add(newWebHttpBehavior { HelpEnabled = true, AutomaticFormatSelectionEnabled = true});

    _serviceHost.Open();

    Thanks!

    Friday, March 30, 2012 12:12 AM

Answers

  • Yes I looked at Fiddler, the specific WCF function is a simple GET, but the webrole service isn't supporting anything other than POST

            [OperationContract]
            [WebGet(UriTemplate = "/Users",
            RequestFormat = WebMessageFormat.Xml,
            ResponseFormat = WebMessageFormat.Xml,
            BodyStyle = WebMessageBodyStyle.Bare)]
            List<string> EnumUsers();

    Let me try and get the traces above to see what are in them...

    Thanks, Gavin


    Hi,

    So it turns out to be a pure WCF issue. This behavior is normal as you're using WebGet attribute for the OperationContract. This means the operation only accepts GET requests. For POST, etc. we need to use WebInvoke attribute. I have a sample about RESTful WCF service that can help you quickly get started. Please check out below sample.

    web.config
     <system.serviceModel>
      <bindings>
       <webHttpBinding>
        <binding name="mywebHttpBinding"></binding>
       </webHttpBinding>
      </bindings>
      <behaviors>
          <endpointBehaviors>
            <behavior name="HttpEnableBehavior">
              <webHttp/>
            </behavior>
          </endpointBehaviors>
       <serviceBehaviors>
        <behavior name="WCF_REST.Service1Behavior">
         <serviceMetadata httpGetEnabled="true"/>
        </behavior>
       </serviceBehaviors>
      </behaviors>
      <services>
       <service behaviorConfiguration="WCF_REST.Service1Behavior" name="WCF_REST.Service1">
        <endpoint behaviorConfiguration="HttpEnableBehavior"   bindingConfiguration="mywebHttpBinding" address="" binding="webHttpBinding" contract="WCF_REST.IService1">
         <identity>
          <dns value="localhost"/>
         </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
       </service>
      </services>
     </system.serviceModel>

    IService1.cs:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Web;

    namespace WCF_REST
    {
        // NOTE: If you change the interface name "IService1" here, you must also update the reference to "IService1" in Web.config.

        [ServiceContract]
        public interface IService1
        {   //test http://.../Service1.svc/GetData?q=aaa
            [WebGet(UriTemplate="*")]
            [OperationContract]
            Message GetData();
            //test http://.../Service1.svc/Get/myparameter
            [WebGet(UriTemplate="Get/{param}")]
            [OperationContract]
            string GetData2(string param);

            [WebInvoke(Method="POST",UriTemplate="Post")]
            [OperationContract]
            string PostData();
        }
    }

    Service1.svc.cs:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Web;

    namespace WCF_REST
    {
        // NOTE: If you change the class name "Service1" here, you must also update the reference to "Service1" in Web.config.

        public class Service1 : IService1
        {
            public Message GetData()
            {
                WebOperationContext woc = WebOperationContext.Current;
                woc.OutgoingResponse.Headers["Content-Type"] = "text/xml";
               string querystring="";
               if (woc.IncomingRequest.UriTemplateMatch.QueryParameters.Count > 0)
               {
                   querystring = woc.IncomingRequest.UriTemplateMatch.QueryParameters[0];
               }
              
                Message response = Message.CreateMessage(MessageVersion.None, "*", "Simple response. Query String: " + querystring);
                return response;
            }

            public string GetData2(string param)
            {
              
                string response = "Simple response. Param: " + param;
                return response;
            }

            public string PostData()
            {
             string posteddata=   OperationContext.Current.RequestContext.RequestMessage.ToString();
             return "Posted at: " + DateTime.Now.ToString() + " Posted data: " + posteddata;
            }
        }
    }

    Client test code.
    aspx.cs:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Net;
    using System.IO;
    using System.Threading;

    namespace WCF_REST
    {
        public partial class _Default : System.Web.UI.Page
        {
            byte[] buffer;
            protected void Page_Load(object sender, EventArgs e)
            {//init buffer
             //buffer contains the data send to REST service
                string s = "<test>Hello World!</test>";
                buffer= System.Text.Encoding.Default.GetBytes(s);

                //note to change the url below to test
                HttpWebRequest hwr = (HttpWebRequest)HttpWebRequest.Create("http://localhost:1329/Service1.svc/Post");
                hwr.Method = "POST";

                //Important thing here. If it's not added we'll get 415
                //The Content-Type header is essential when using RESTful
                //services in general, but is especially important when
                //making requests to RESTful services.
                //If you don't have a Content-Type header in your HTTP
                //request to a WCF service, you'll always get a
                //"415 Missing Content Type" status code.
                //You don't need the Content-Type header when making
                //a GET request, since there isn't any entity body when
                //making a GET request.

                hwr.ContentType=@"text/xml";

                //sync
                using (Stream postStream = hwr.GetRequestStream())
                {
                    postStream.Write(buffer, 0, buffer.Length);
                    postStream.Close();
                    HttpWebResponse response = (HttpWebResponse)hwr.GetResponse();
                    using (Stream respStream = response.GetResponseStream())
                    {
                        using (StreamReader streamRead = new StreamReader(respStream))
                        {
                            //render the result retrieved from REST service
                            Response.Write(streamRead.ReadToEnd());
                        }
                    }

                    response.Close();  

              }

                ////async
                //bool flag=true;
                //hwr.BeginGetRequestStream((a) =>
                //{
                //    HttpWebRequest request = (HttpWebRequest)a.AsyncState;
                //    Stream postStream = request.EndGetRequestStream(a);
                //    postStream.Write(buffer, 0, buffer.Length);
                //    postStream.Close();
                //    request.BeginGetResponse((a1) =>
                //    {
                //        HttpWebResponse resp = (HttpWebResponse)request.EndGetResponse(a1);
                //        Stream streamResponse = resp.GetResponseStream();
                //        StreamReader streamRead = new StreamReader(streamResponse);
                //        string responseString = streamRead.ReadToEnd();
                //        //render the result retrieved from REST service
                //        Response.Write(responseString);
                //        // Close the stream object.
                //        streamResponse.Close();
                //        streamRead.Close();
                //        // Release the HttpWebResponse.
                //        resp.Close();
                //        flag=false;
                //    }, request);
                //}, hwr);

                ////helper for asp.net application to force sync otherwise the resource may be GCed.
                //while(flag){Thread.Sleep(1000);}
            }
        }
    }


    Allen Chen [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.






    Thursday, April 05, 2012 1:59 AM

All replies

  • Hi,

    I am trying to involve someone familiar with this topic to further look at this issue. There might be some time delay.

    Appreciate your patience.

    Best Regards,

    Ming Xu.


    Please mark the replies as answers if they help or unmark if not.
    If you have any feedback about my replies, please contact msdnmg@microsoft.com.
    Microsoft One Code Framework

    Sunday, April 01, 2012 5:23 PM
  • Hi,

    What's the client? Could you check Fiddler logs to make sure the client is sending the same POST requests to server? Could you post the failing request and the response vs. the working ones?

    You can also use WCF tracing at server side to get more information about the error. Please post the logs if you cannot find the reason.

    http://msdn.microsoft.com/en-us/library/ms733025.aspx

    http://msdn.microsoft.com/en-us/library/ms732023.aspx


    Allen Chen [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.


    Monday, April 02, 2012 2:47 AM
  • Yes I looked at Fiddler, the specific WCF function is a simple GET, but the webrole service isn't supporting anything other than POST

            [OperationContract]
            [WebGet(UriTemplate = "/Users",
            RequestFormat = WebMessageFormat.Xml,
            ResponseFormat = WebMessageFormat.Xml,
            BodyStyle = WebMessageBodyStyle.Bare)]
            List<string> EnumUsers();

    Let me try and get the traces above to see what are in them...

    Thanks, Gavin

    Wednesday, April 04, 2012 8:12 PM
  • Yes I looked at Fiddler, the specific WCF function is a simple GET, but the webrole service isn't supporting anything other than POST

            [OperationContract]
            [WebGet(UriTemplate = "/Users",
            RequestFormat = WebMessageFormat.Xml,
            ResponseFormat = WebMessageFormat.Xml,
            BodyStyle = WebMessageBodyStyle.Bare)]
            List<string> EnumUsers();

    Let me try and get the traces above to see what are in them...

    Thanks, Gavin


    Hi,

    So it turns out to be a pure WCF issue. This behavior is normal as you're using WebGet attribute for the OperationContract. This means the operation only accepts GET requests. For POST, etc. we need to use WebInvoke attribute. I have a sample about RESTful WCF service that can help you quickly get started. Please check out below sample.

    web.config
     <system.serviceModel>
      <bindings>
       <webHttpBinding>
        <binding name="mywebHttpBinding"></binding>
       </webHttpBinding>
      </bindings>
      <behaviors>
          <endpointBehaviors>
            <behavior name="HttpEnableBehavior">
              <webHttp/>
            </behavior>
          </endpointBehaviors>
       <serviceBehaviors>
        <behavior name="WCF_REST.Service1Behavior">
         <serviceMetadata httpGetEnabled="true"/>
        </behavior>
       </serviceBehaviors>
      </behaviors>
      <services>
       <service behaviorConfiguration="WCF_REST.Service1Behavior" name="WCF_REST.Service1">
        <endpoint behaviorConfiguration="HttpEnableBehavior"   bindingConfiguration="mywebHttpBinding" address="" binding="webHttpBinding" contract="WCF_REST.IService1">
         <identity>
          <dns value="localhost"/>
         </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
       </service>
      </services>
     </system.serviceModel>

    IService1.cs:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Web;

    namespace WCF_REST
    {
        // NOTE: If you change the interface name "IService1" here, you must also update the reference to "IService1" in Web.config.

        [ServiceContract]
        public interface IService1
        {   //test http://.../Service1.svc/GetData?q=aaa
            [WebGet(UriTemplate="*")]
            [OperationContract]
            Message GetData();
            //test http://.../Service1.svc/Get/myparameter
            [WebGet(UriTemplate="Get/{param}")]
            [OperationContract]
            string GetData2(string param);

            [WebInvoke(Method="POST",UriTemplate="Post")]
            [OperationContract]
            string PostData();
        }
    }

    Service1.svc.cs:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Web;

    namespace WCF_REST
    {
        // NOTE: If you change the class name "Service1" here, you must also update the reference to "Service1" in Web.config.

        public class Service1 : IService1
        {
            public Message GetData()
            {
                WebOperationContext woc = WebOperationContext.Current;
                woc.OutgoingResponse.Headers["Content-Type"] = "text/xml";
               string querystring="";
               if (woc.IncomingRequest.UriTemplateMatch.QueryParameters.Count > 0)
               {
                   querystring = woc.IncomingRequest.UriTemplateMatch.QueryParameters[0];
               }
              
                Message response = Message.CreateMessage(MessageVersion.None, "*", "Simple response. Query String: " + querystring);
                return response;
            }

            public string GetData2(string param)
            {
              
                string response = "Simple response. Param: " + param;
                return response;
            }

            public string PostData()
            {
             string posteddata=   OperationContext.Current.RequestContext.RequestMessage.ToString();
             return "Posted at: " + DateTime.Now.ToString() + " Posted data: " + posteddata;
            }
        }
    }

    Client test code.
    aspx.cs:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Net;
    using System.IO;
    using System.Threading;

    namespace WCF_REST
    {
        public partial class _Default : System.Web.UI.Page
        {
            byte[] buffer;
            protected void Page_Load(object sender, EventArgs e)
            {//init buffer
             //buffer contains the data send to REST service
                string s = "<test>Hello World!</test>";
                buffer= System.Text.Encoding.Default.GetBytes(s);

                //note to change the url below to test
                HttpWebRequest hwr = (HttpWebRequest)HttpWebRequest.Create("http://localhost:1329/Service1.svc/Post");
                hwr.Method = "POST";

                //Important thing here. If it's not added we'll get 415
                //The Content-Type header is essential when using RESTful
                //services in general, but is especially important when
                //making requests to RESTful services.
                //If you don't have a Content-Type header in your HTTP
                //request to a WCF service, you'll always get a
                //"415 Missing Content Type" status code.
                //You don't need the Content-Type header when making
                //a GET request, since there isn't any entity body when
                //making a GET request.

                hwr.ContentType=@"text/xml";

                //sync
                using (Stream postStream = hwr.GetRequestStream())
                {
                    postStream.Write(buffer, 0, buffer.Length);
                    postStream.Close();
                    HttpWebResponse response = (HttpWebResponse)hwr.GetResponse();
                    using (Stream respStream = response.GetResponseStream())
                    {
                        using (StreamReader streamRead = new StreamReader(respStream))
                        {
                            //render the result retrieved from REST service
                            Response.Write(streamRead.ReadToEnd());
                        }
                    }

                    response.Close();  

              }

                ////async
                //bool flag=true;
                //hwr.BeginGetRequestStream((a) =>
                //{
                //    HttpWebRequest request = (HttpWebRequest)a.AsyncState;
                //    Stream postStream = request.EndGetRequestStream(a);
                //    postStream.Write(buffer, 0, buffer.Length);
                //    postStream.Close();
                //    request.BeginGetResponse((a1) =>
                //    {
                //        HttpWebResponse resp = (HttpWebResponse)request.EndGetResponse(a1);
                //        Stream streamResponse = resp.GetResponseStream();
                //        StreamReader streamRead = new StreamReader(streamResponse);
                //        string responseString = streamRead.ReadToEnd();
                //        //render the result retrieved from REST service
                //        Response.Write(responseString);
                //        // Close the stream object.
                //        streamResponse.Close();
                //        streamRead.Close();
                //        // Release the HttpWebResponse.
                //        resp.Close();
                //        flag=false;
                //    }, request);
                //}, hwr);

                ////helper for asp.net application to force sync otherwise the resource may be GCed.
                //while(flag){Thread.Sleep(1000);}
            }
        }
    }


    Allen Chen [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.






    Thursday, April 05, 2012 1:59 AM
  • Hi Gavin,

    A similar situation is faced when you have a silverlight enabled webrole. The ServiceReference.ClientConfig file has a similar link which denoted the ip address and port number appended with the Service1.svc url parameter. It denotes like this

    http://127.0.0.1:8080/Service1.svc

    but we need to update this with http://appname.cloudapp.net/Service1.svc

    Note: there is no port number in this case.

    So I believe without the port number the communication will succeed.

    Please correct me if I am wrong.

    Thanks

    Nabarun

    Thursday, April 05, 2012 2:57 AM