title: WCF Restful Web Service Endpoint exposed but methods all return http 404 not found c#
body: <p>I have written my first Web Service and when I test run it from visual studio on my local development machine everything works as expected. I went to the clients to deploy the service and found that after deployment I could reach the endpoint but that all my methods return HTTP 404 not found.</p>
  
  <p>The Web Service is written in Visual Studio using WCF and is set to return Json. The Web Services is targeting .Net framework 4.5. The site is configured for HTTPS protocol and has a valid SSL Cert. I have updated the server with the latest version of .Net framework 4.5 and applied an application pool to the site accordingly. </p>
  
  <p>When I go to the Endpoint location on the client’s server from an external browser (I have modified the screenshot and link to remove the real domain):</p>
  
  <p>The link looks like this:</p>
  
  <p><a href="https://www.somedomain.co.uk/WorksWebService/WorksWebService.svc" rel="nofollow noreferrer">https://www.somedomain.co.uk/WorksWebService/WorksWebService.svc</a></p>
  
  <p>I get the page displaying the Web Service links to the WSDL and SingleWSDL pages and they display correctly the endpoint methods and various other configuration information.</p>
  
  <p><a href="https://i.stack.imgur.com/o1WTQ.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/o1WTQ.png" alt="Works Web Service Endpoint"></a></p>
  
  <p>This seems to me like everything is as expected and am a bit unsure where next to look for problems.</p>
  
  <p>Now I am quite unsure what the requirements are for the Web.config file. Below is the current Web.config file for the Web Service. It may be that I am missing important information here regarding the deployment of the Service but wonder why if that is the case that the Service runs successfully from within Visual Studio? The only difference of note is that from visual studio I run the Service using http instead of https.</p>
  
  <pre><code>&lt;?xml version="1.0"?&gt;
  &lt;configuration&gt;
    &lt;appSettings&gt;
      &lt;add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" /&gt;
    &lt;/appSettings&gt;
    &lt;system.web&gt;
      &lt;compilation debug="true" targetFramework="4.5" /&gt;
      &lt;httpRuntime targetFramework="4.5"/&gt;
    &lt;/system.web&gt;
    &lt;connectionStrings&gt;
      &lt;add name="BAXISQL"
           connectionString="Database=SomeDatabase;Server=SomeServer;User ID=user;Password=password"
           providerName="System.Data.SqlClient"
      /&gt;
    &lt;/connectionStrings&gt;
    &lt;system.serviceModel&gt;
      &lt;services&gt;
        &lt;service name="WorksWebService.WorksWebService"&gt;
          &lt;endpoint address="" behaviorConfiguration="restful" binding="webHttpBinding"
            contract="WorksWebService.IWorksWebService" /&gt;
        &lt;/service&gt;
      &lt;/services&gt;
      &lt;behaviors&gt;
        &lt;serviceBehaviors&gt;
          &lt;behavior&gt;
            &lt;!-- To avoid disclosing metadata information, set the values below to false before deployment --&gt;
            &lt;serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/&gt;
            &lt;!-- 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 --&gt;
            &lt;serviceDebug includeExceptionDetailInFaults="false"/&gt;
          &lt;/behavior&gt;
        &lt;/serviceBehaviors&gt;
        &lt;endpointBehaviors&gt;
          &lt;behavior name="restful"&gt;
            &lt;webHttp/&gt;
          &lt;/behavior&gt;
        &lt;/endpointBehaviors&gt;
      &lt;/behaviors&gt;
      &lt;protocolMapping&gt;
          &lt;add binding="basicHttpsBinding" scheme="https" /&gt;
      &lt;/protocolMapping&gt;    
      &lt;serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /&gt;
    &lt;/system.serviceModel&gt;
    &lt;system.webServer&gt;
      &lt;modules runAllManagedModulesForAllRequests="true"/&gt;
      &lt;!--
          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.
        --&gt;
      &lt;directoryBrowse enabled="true"/&gt;
    &lt;/system.webServer&gt;
    &lt;startup&gt;
      &lt;supportedRuntime version="4.0" sku=".NETFramework,Version=v4.0"/&gt;
    &lt;/startup&gt;
  &lt;/configuration&gt;
  </code></pre>
  
  <p>I imagine that my problem is coming from either the Web.config file or from some missing Meta data in my service interface and method declarations.</p>
  
  <p>The following code shows my Interface (modified just to include a single method):</p>
  
  <pre><code>using System.IO;
  using System.ServiceModel;
  using System.Web.Services;
  
  namespace WorksWebService
  {
      [ServiceContract]
      interface IWorksWebService
      {
          [OperationContract]
          [WebMethod]
          string GetTrainingStatus(string urn, string pastdays, string futuredays);   
      }
  }
  </code></pre>
  
  <p>And below is the corresponding method calls in the WebService.svc.cs file (also modified to show the only the single method):</p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using System.Collections;
  using System.Data.SqlClient;
  using System.Net;
  using System.ServiceModel.Web;
  using System.Web;
  using Newtonsoft.Json;
  using System.Runtime.Serialization;
  using System.IO;
  using System.ServiceModel;
  using System.Text;
  using Newtonsoft.Json.Linq;
  
  namespace WorksWebService
  {
      public class WorksWebService: IWorksWebService
      {
          // key that all methods must receive in the header to validate that the request is from a valid source
          private string baxiSMSKey = "C5A75B32-5BC9-4D89-AB78-F8FE0CF58806";
          //BAXI_SMS_KEY: C5A75B32-5BC9-4D89-AB78-F8FE0CF58806
  
          #region TrainingStatus
  
          // test url string
          // WorksWebService.svc/rest/member/C211292/trainingstatus?country=GB
          /// &lt;summary&gt;
          /// Method to find the training status dates for a specific Customer
          /// &lt;/summary&gt;
          /// &lt;param name="urn"&gt;&lt;/param&gt;
          /// &lt;param name="country"&gt;&lt;/param&gt;
          /// &lt;returns&gt;Last valid training status date or future date the customer will be attending training&lt;/returns&gt;
          [WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json
              , UriTemplate = "rest/member/{urn}/trainingstatus?pastdays={pastdays}&amp;futuredays={futuredays}")]
          public string GetTrainingStatus(string urn, string pastdays, string futuredays)
          {
              string dateString = string.Empty;
              WebOperationContext context = WebOperationContext.Current;
              // check the headers for the BAXI_SMS_KEY
              if (CheckAPIKey())
              {
                  // validate the url parameters
                  if (ValidateURLStringParameters_GetTrainingStatus(urn, pastdays, futuredays))
                  {
                      // find the training status
                      dateString = GetTrainingDate(urn, pastdays, futuredays);
  
                      if (dateString.Contains("Error: "))
                          context.OutgoingResponse.StatusCode = HttpStatusCode.InternalServerError; // HTTP Code 500
                      else if (dateString.Equals(string.Empty))
                          context.OutgoingResponse.StatusCode = HttpStatusCode.NotFound; // HTTP Code 404
                      else
                          context.OutgoingResponse.StatusCode = HttpStatusCode.OK; // HTTP Code 200
                  }
                  else
                      context.OutgoingResponse.StatusCode = HttpStatusCode.BadRequest; // HTTP Code 400
              }
              else
                  context.OutgoingResponse.StatusCode = HttpStatusCode.Forbidden; // HTTP Code 403
  
              return dateString;
          }
  
          /// &lt;summary&gt;
          /// method to validate the parameters for GetTrainingStatus Web method
          /// &lt;/summary&gt;
          /// &lt;param name="urn"&gt;&lt;/param&gt;
          /// &lt;param name="pastdays"&gt;&lt;/param&gt;
          /// &lt;param name="futuredays"&gt;&lt;/param&gt;
          /// &lt;returns&gt;True or False&lt;/returns&gt;
          private bool ValidateURLStringParameters_GetTrainingStatus(string urn, string pastdays, string futuredays)
          {
              if (string.IsNullOrWhiteSpace(urn))
                  return false;
  
              if (string.IsNullOrWhiteSpace(pastdays))
                  return false;
  
              if (string.IsNullOrWhiteSpace(futuredays))
                  return false;
  
              try
              {
                  Convert.ToInt32(pastdays);
                  Convert.ToInt32(futuredays);
              }
              catch (Exception e)
              {
                  return false;
              }
  
              return true;
          }
  
          /// &lt;summary&gt;
          /// Method to find the training status of the member
          /// &lt;/summary&gt;
          /// &lt;param name="urn"&gt;&lt;/param&gt;
          /// &lt;returns&gt;the date of the users training past of future&lt;/returns&gt;
          private string GetTrainingDate(string urn, string pastdays, string futuredays)
          {
              string dateSt = string.Empty;
              try
              {
                  // first search against customers
                  var connection = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["BAXISQL"].ConnectionString);
                  connection.Open();
                  var sqlCommand = connection.CreateCommand();
                  sqlCommand.CommandText = "SELECT dbo.WORKS_GetTrainingDate_fn(@URN, @Past, @Future)";
                  sqlCommand.Parameters.AddWithValue("@URN", urn);
                  sqlCommand.Parameters.AddWithValue("@Past", pastdays);
                  sqlCommand.Parameters.AddWithValue("@Future", futuredays);
  
                  string date = sqlCommand.ExecuteScalar().ToString();
                  connection.Close();
  
                  if (!string.IsNullOrWhiteSpace(date))
                  {                    
                      string[] parts = date.ToString().Split('/');
                      dateSt = parts[2] + parts[1] + parts[0];
                  }
              }
              catch (Exception e)
              {
                  dateSt = "Error: " + e.Message + "&lt;br /&gt;Source: " + e.Source + "&lt;br /&gt;Stacktrace: " + e.StackTrace;
              }
  
              return dateSt;
          }
  
          #endregion
  
          #region Private Methods
  
          /// &lt;summary&gt;
          /// Method to determine if the API key has been passed in succesfully
          /// &lt;/summary&gt;
          /// &lt;returns&gt;true or false&lt;/returns&gt;
          private bool CheckAPIKey()
          {
              bool matchedKey = false;
              IncomingWebRequestContext request = WebOperationContext.Current.IncomingRequest;
              WebHeaderCollection headers = request.Headers;
  
              System.Diagnostics.Debug.Write("\r\n-------------------------------------------------------");
              System.Diagnostics.Debug.Write("\r\n" + request.Method + " " + request.UriTemplateMatch.RequestUri.AbsolutePath);
              foreach (string headerName in headers.AllKeys)
              {
                  System.Diagnostics.Debug.Write("\r\n" + headerName + ": " + headers[headerName]);
                  if (headerName.Equals("BAXI_SMS_KEY"))
                  {
                      if (baxiSMSKey.ToString().ToUpper().Equals(headers[headerName]))
                      {
                          matchedKey = true;
                      }
                  }
              }
              System.Diagnostics.Debug.Write("\r\n-------------------------------------------------------");
  
              return matchedKey;
          }
  
          #endregion
  
      }
  }
  </code></pre>
  
  <p>These 404 messages are occurring when I try to reach the Service Mthods using Fiddler and browsers. I wanted to prove that the methods are working correctly before allowing the 3rd party that is building a Web Portal that will act as a client to the Web Service have access.</p>
  
  <p>Any input or suggestions would be greatly appreciated.</p>
  
  <p>Thanks in advance Iain</p>