问题
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... ;)
回答1:
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
回答2:
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
回答3:
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" };
来源:https://stackoverflow.com/questions/23067794/trying-to-add-a-service-reference-results-in-bad-request-400-in-one-project-o