Trying to add a service reference results in Bad Request (400) in one project, otherwise runs fine

StackOverflow https://stackoverflow.com/questions/23067794

  •  03-07-2023
  •  | 
  •  

Question

I'm in a delicate situation: As the title suggests, I can't seem to connect to a WCF service I wrapped up in a Windows Service. I followed the tutorial http://msdn.microsoft.com/en-us/library/ms733069%28v=vs.110%29.aspx every step and got it to work multiple times in this exact way, just not for one particular project. I really don't know what it is, I have a very simple interface with just one method as contract, my service is installed just fine and also starts just fine. Once I try to add a service reference in another project I get an error 400, Bad Request, and a metadata problem. I even rewrote the prototype project (it's basically the same project with less code in the implementation of the contract; but we're talking still below 300 lines of code) which ran perfectly fine, and came to the same error. I didn't change any of the app.config code when I did that, and I could connect before, but bad request afterwards.

What adds to the problem is that I can't post any code here (I'm working on a VM at work where I don't have internet access, plus the internet access I actually do have on the physical machine is so restricted I can't open any kind of board/forum/blog/whatever, so I can't post exact errors). Since all my minimal examples do work, my "minimal not working example" would be the total code anyway.

I'm at a total loss here. I dug through all the other topics of the bad request error and have some more checking to do tomorrow, but I thought I'd rather just post here and maybe get some advice on what/how to further test my project for errors. Just in case it helps, the app.config is the same as the following except for the service and contract names:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>    <services>
      <!-- This section is optional with the new configuration model
           introduced in .NET Framework 4. -->
      <service name="Microsoft.ServiceModel.Samples.CalculatorService"
               behaviorConfiguration="CalculatorServiceBehavior">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8000/ServiceModelSamples/service"/>
          </baseAddresses>
        </host>
        <!-- this endpoint is exposed at the base address provided by host: http://localhost:8000/ServiceModelSamples/service  -->
        <endpoint address=""
                  binding="wsHttpBinding"
                  contract="Microsoft.ServiceModel.Samples.ICalculator" />
        <!-- the mex endpoint is exposed at http://localhost:8000/ServiceModelSamples/service/mex -->
        <endpoint address="mex"
                  binding="mexHttpBinding"
                  contract="IMetadataExchange" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="CalculatorServiceBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="False"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

What could possibly make three attempts from scratch work, but the 4th not work again where the only difference is the size of the files and 2 added helper classes which are not doing anything else? I mean, the service has to be there, otherwise I'd get a 404 Not Found error instead, but something's wrong with the metadata although I'm using the exact(!) same app.config in another try and there it works.

Any suggestions/hints are greatly appreciated. I'll surely try increasing the buffer sizes in the binding tomorrow, I heard that helped some people, but I don't know about me there since I'm not really sending anything yet, am I? I'm just trying to add the service reference, I don't know if the size matters there. I also already tried TCP and basicHTTP bindings to the same result.

Thanks in advance and sorry for the wall of text; I got very frustrated at work today, with not even being able to research the error properly due to those stupid work conditions, and that frustration came up again writing this... ;)

Was it helpful?

Solution

For the beginning you have to find an issue, I mean you need to know what happens on server side. You need to handler all error and log them.

Logging errors

    public class GlobalErrorHanler: IErrorHandler 
    {
        //to use log4net you have to have a proper configuration in you web/app.config
        private static readonly ILog Logger = LogManager.GetLogger(typeof (GlobalErrorHandler));

        public bool HandleError(Exception error)
        {
            //if you host your app on IIS you have to log using log4net for example
            Logger.Error("Error occurred on the service side", error);

            //Console.WriteLine(error.Message); 
            //Console.WriteLine(error.StackTrace);

            return false;
        }

        public void ProvideFault(Exception error, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message fault)
        {
            //you can provide you fault exception here
        }
    }

Then service behavior (inherits Attribute to add possibility to use it as an attribute on service implementation):

public class MyErrorHandlingBehavior : Attribute, IServiceBehavior
{

    public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
    {
        return;
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
    {
        foreach (ChannelDispatcher disp in serviceHostBase.ChannelDispatchers)
            disp.ErrorHandlers.Add(new GlobalErrorHanler());
    }

    public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
    {        }
}

And extension element to use it in your config:

public class ErrorHandlerExtention: BehaviorExtensionElement
{

    public override Type BehaviorType
    {
        get { return typeof(MyErrorHandlingBehavior); }
    }

    protected override object CreateBehavior()
    {
        return new MyErrorHandlingBehavior();
    }
}

Then add to config file:

<system.serviceModel>
  <extensions>
    <behaviorExtensions>
      <!-- Extension.ErrorHandlerExtention: fully qualified class name, ArrayList: assebmly name-->
      <add name="errorHandler" type="Extension.ErrorHandlerExtention, Extensions"/>
    </behaviorExtensions>
  </extensions>

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

      <!-- the name of this element must be the same as in the section behaviorExtensions -->
      <errorHandler />

    </behavior>
  </serviceBehaviors>
</behaviors>

</system.serviceModel>

This will allow you to get an error. When you have an error get back to the forum.

Some more posts with this technique: Error 1, Error 2

Tracing:

To turn on tracing you have to add such lines to a config:

<system.diagnostics>
  <trace autoflush="true" />
  <sources>
    <source name="System.ServiceModel"
            switchValue="Information, ActivityTracing"
            propagateActivity="true">
      <listeners>
        <add name="log"
            type="System.Diagnostics.XmlWriterTraceListener"
            initializeData= "C:\traces.svclog" />
      </listeners>
    </source>
  </sources>
</system.diagnostics>

Tracing tool: Trace Viewer

OTHER TIPS

Cannot say what the problem is but here is some info to help you find the problem.

When you do "Add Service Reference" there is a http get Call that goes to the service. I am assuming that it is this Call that Returns http 400. You have a mex endpoint and httpgetenabled, missing these are the normal problems.

You can try Reading the wsdl using a browser and see if that Works.

Reasons could be:

  • Port 8000 is blocked
  • The IIS site is on a different port
  • You are Accessing the site using the machine name and the config uses local host
  • the binding is not enabled in iis for that port

For anyone who is encountering similar issues without detailed error, I'll describe what my problem was about. In my Visual Studio project, I used a class similar to the following:

public class Info
{
  public string Key { get; set; }
  public string Value { get; set; }

  public Info(string key, string value)
  {
    this.Key = key;
    this.Value = value;
  }
}

The difference to the other project (which worked) is that this POCO has a constructor that takes parameters, thus doesn't provide a standard constructor without any arguments. But this is needed in order for Info objects to be serialized (I hope I use that term correctly here) and deserialized. So in order to make it work, either just add a standard constructor which may just do nothing, or (perhaps better) use the following instead:

[DataContract]
public class Info
{
  [DataMember]
  public string Key { get; set; }
  [DataMember]
  public string Value { get; set; }

  public Info(string key, string value)
  {
    this.Key = key;
    this.Value = value;
  }
}

I didn't think about the DataContract part before, because I didn't use any custom constructor and just initialized the objects like

Info info = new Info { Key = "a", Value = "b" };
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top