WCF REST Self-Hosted 400 Bad Request - wcf

I'm having a problem with a self-host WCF REST service.
When I try to issue a GET via browser or Fiddler, I get a 400 Bad Request. Tracing is reporting an inner exception of XmlException "The body of the message cannot be read because it is empty."
I don't have any configuration in app.config (do I need any?). I have tried changing WebServiceHost to ServiceHost, and WSDL is returned, but the operations still return 400.
What am I missing here?
// Add Reference to System.ServiceModel and System.ServiceModel.Web
using System;
using System.Diagnostics;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Web;
namespace WCFRESTTest
{
class Program
{
static void Main(string[] args)
{
var baseAddress = new Uri("http://localhost:8000/");
var host = new WebServiceHost(typeof(RestService), baseAddress);
try
{
host.AddServiceEndpoint(typeof(IRestService), new WSHttpBinding(), "RestService");
var smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
host.Description.Behaviors.Add(smb);
host.Open();
Console.WriteLine("Service Running. Press any key to stop.");
Console.ReadKey();
}
catch(CommunicationException ce)
{
host.Abort();
throw;
}
}
}
[ServiceContract]
public interface IRestService
{
[OperationContract]
[WebGet(UriTemplate = "Test")]
bool Test();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class RestService : IRestService
{
public bool Test()
{
Debug.WriteLine("Test Called.");
return true;
}
}
}

When you use the WebServiceHost, you typically don't need to add a service endpoint - it will add one with all behaviors required to make it a "Web HTTP" (a.k.a. REST) endpoint (i.e., an endpoint which doesn't use SOAP and you can easily consume with a tool such as Fiddler, which seems to be what you want). Also, Web HTTP endpoints aren't exposed in the WSDL, so you don't need to add the ServiceMetadataBehavior either.
Now for why it doesn't work - sending a GET request to http://localhost:8000/Test should work - and in the code below it does. Try running this code, and sending the request you were sending before with Fiddler, to see the difference. That should point out what the issue you have.
public class StackOverflow_15705744
{
[ServiceContract]
public interface IRestService
{
[OperationContract]
[WebGet(UriTemplate = "Test")]
bool Test();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class RestService : IRestService
{
public bool Test()
{
Debug.WriteLine("Test Called.");
return true;
}
}
public static void Test()
{
var baseAddress = new Uri("http://localhost:8000/");
var host = new WebServiceHost(typeof(RestService), baseAddress);
// host.AddServiceEndpoint(typeof(IRestService), new WSHttpBinding(), "RestService");
// var smb = new ServiceMetadataBehavior();
// smb.HttpGetEnabled = true;
// host.Description.Behaviors.Add(smb);
host.Open();
WebClient c = new WebClient();
Console.WriteLine(c.DownloadString(baseAddress.ToString().TrimEnd('/') + "/Test"));
Console.WriteLine("Service Running. Press any key to stop.");
Console.ReadKey();
}
}

Related

Catching WCF faults when hosting a service and a client in a single application

I am experimenting with a WCF service in a Visual Studio unit test. Both the client and the service are configured programmatically.
Currently my code looks like this:
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Tests
{
public abstract class EntityBase
{
}
public class TestEntity : EntityBase
{
public string Name { get; set; }
}
[ServiceContract]
[ServiceKnownType("GetKnownTypes", typeof(ServiceKnownTypesDiscoveryHelper))]
public interface ITestService
{
[OperationContract]
EntityBase GetEntity(string entityName);
}
public class TestService : ITestService
{
public EntityBase GetEntity(string entityName)
{
Type t = Type.GetType(entityName);
return (EntityBase)Activator.CreateInstance(t);
}
}
[TestClass]
public class ServiceTests
{
private static ServiceHost ServiceHost { get; set; }
[ClassInitialize]
public static void ClassInitialize(TestContext testContext)
{
ServiceHost = new ServiceHost(typeof(TestService));
NetTcpBinding wsBinding = new NetTcpBinding();
ServiceHost.AddServiceEndpoint(typeof(ITestService), wsBinding,
"net.tcp://localhost:8011/TestService");
// trying to turn on debugging here
var behavior = ServiceHost.Description.Behaviors.Find<ServiceDebugBehavior>();
behavior.IncludeExceptionDetailInFaults = true;
ServiceHost.Open();
}
[ClassCleanup]
public static void ClassCleanup()
{
ServiceHost.Close();
}
[TestMethod]
public void TestSomething()
{
var binding = new NetTcpBinding();
var endpoint = new EndpointAddress("net.tcp://localhost:8011/TestService");
using (ChannelFactory<ITestService> testServiceFactory =
new ChannelFactory<ITestService>(binding, endpoint))
{
var proxy = testServiceFactory.CreateChannel();
using (proxy as IDisposable)
{
try
{
var entity = proxy.GetEntity(typeof(TestEntity).FullName);
Assert.IsInstanceOfType(entity, typeof(TestEntity));
}
catch (FaultException ex)
{
// copied this from MSDN example
string msg = "FaultException: " + ex.Message;
MessageFault fault = ex.CreateMessageFault();
if (fault.HasDetail == true)
{
var reader = fault.GetReaderAtDetailContents();
if (reader.Name == "ExceptionDetail")
{
ExceptionDetail detail = fault.GetDetail<ExceptionDetail>();
msg += "\n\nStack Trace: " + detail.StackTrace;
}
}
System.Diagnostics.Trace.WriteLine(msg);
}
}
}
}
}
}
If my ServiceKnownTypesDiscoveryHelper does not return known types, I know that my service and client should throw something serialisation related somewhere deep in .NET servicemodel code (if I modify it to return my TestEntity then of course everything works without any issues).
But currently if the service fails, I get only some vague exception messages like:
The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue.
and at the end of using() I get
The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state.
(which also is weird - why can't I even dispose the ServiceChannel if it's in a faulted state...)
How do I catch the actual fault which caused the service or the client to fail instead of those vague exception messages?

Test WCF service and client

I am trying to write an integration test that runs the service and then connects a client to this service.
ConnectClientToTestService() throws error:
System.ServiceModel.Security.SecurityNegotiationException: Secure
channel cannot be opened because security negotiation with the remote
endpoint has failed. This may be due to absent or incorrectly
specified EndpointIdentity in the EndpointAddress used to create the
channel. Please verify the EndpointIdentity specified or implied by
the EndpointAddress correctly identifies the remote endpoint. --->
System.ServiceModel.FaultException: The request for security token has
invalid or malformed elements.
Can you do this in the same exe? There are certificates involved which have been installed on my machine, but these also might be the issue.
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.ServiceModel;
using ECS.Services;
using ECS.App.Core.ECSDataService;
using System.ServiceModel.Description;
namespace ECS.Test.ClientSide
{
[TestClass]
public class TestValidUserIntegrationTest
{
private static TestContext context;
[ClassInitialize()]
public static void ClassInitialize(TestContext testContext)
{
context = testContext;
ResourcingServiceHost.StartService();
}
/// <summary>
/// Shut down the WCF service once all tests have been run
/// </summary>
[ClassCleanup()]
public static void MyClassCleanup()
{
ResourcingServiceHost.StopService();
}
//Point the client at the test ResourceingServiceHost service
[TestMethod]
public void ConnectClientToTestService()
{
WSHttpBinding myBinding = new WSHttpBinding();
EndpointAddress myEndpoint = new EndpointAddress("http://localhost:8733/ECS.Services/DataService/");
var factory = new ChannelFactory<ECS.App.Core.ECSDataService.IDataService>("debug", new EndpointAddress("http://localhost:8733/ECS.Services/DataService/"));//new ChannelFactory<ECS.App.Core.ECSDataService.IDataService>(myBinding, myEndpoint);//
{
ClientCredentials clientCredentials = new ClientCredentials();
clientCredentials.UserName.UserName = "admin";
clientCredentials.UserName.Password = "a";
factory.Endpoint.Behaviors.RemoveAll<ClientCredentials>();
factory.Endpoint.Behaviors.Add(clientCredentials);
ECS.App.Core.ECSDataService.IDataService client = factory.CreateChannel();
using (Channel.AsDisposable(client))
{
client.GetConnectionStrings();
}
}
}
}
internal class ResourcingServiceHost
{
internal static ServiceHost Instance = null;
internal static void StartService()
{
Instance = new ServiceHost(typeof(DataService));
WSHttpBinding wsBinding = new WSHttpBinding();
wsBinding.Security.Mode = SecurityMode.Message;
wsBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
Instance.AddServiceEndpoint(typeof(ECS.Services.IDataService), wsBinding, "http://localhost:8733/ECS.Services/DataService/");
Instance.Open();
}
internal static void StopService()
{
if (Instance.State != CommunicationState.Closed)
{
Instance.Close();
}
}
}
//This allows us to see the inner exceptions from the WCF service
public class Channel : IDisposable
{
private ICommunicationObject _channel;
private Channel(ICommunicationObject channel)
{
_channel = channel;
}
public static IDisposable AsDisposable(object client)
{
return new Channel((ICommunicationObject)client);
}
public void Dispose()
{
bool success = false;
try
{
if (_channel.State != CommunicationState.Faulted)
{
_channel.Close(); success = true;
}
}
finally
{
if (!success)
{
_channel.Abort();
}
}
}
}
}

WCF & IXmlSerializable: root element gets replaced in response

We have custom XML serialization for our "protocol" here:
[XmlRoot("axf", Namespace = Axf10Namespace)]
public class AxfDocument : IXmlSerializable
{
public const string Axf10Namespace = "http://schemas.***.ru/axf/axf-1.0.0";
// ...
}
and all's fine when using standard .NET XmlSerializer:
<?xml version="1.0" encoding="utf-16"?>
<axf version="1.0.0" createdAt="2011-10-20T13:11:40" xmlns="http://schemas.***.ru/axf/axf-1.0.0">
<itineraries>
<!-- -->
</itineraries>
</axf>
Now that we try to use this class in a bare-bones WCF service:
[OperationContract]
AxfDocument GetItineraries(ItinerariesQuery query);
actual XML document that gets sent to client side is this:
<GetItinerariesResult version="1.0.0" createdAt="2011-10-20T13:17:50" xmlns="http://tempuri.org/">
<itineraries xmlns="http://schemas.***.ru/axf/axf-1.0.0">
<!-- rest is fine, serialization code does work -->
How do I bend WCF to send root element as-is and not to replace it with its own?
By default, the operation responses are wrapped in the operation name. You can, however, use a MessageContract in the operation definition to use an "unwrapped" response, as shown below. If you look at the response body of the request in Fiddler, you'll see that it's exactly as the one from the serialization.
public class StackOverflow_7836645
{
[XmlRoot("axf", Namespace = Axf10Namespace)]
public class AxfDocument : IXmlSerializable
{
public const string Axf10Namespace = "http://schemas.something.ru/axf/axf-1.0.0";
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
}
public void WriteXml(XmlWriter writer)
{
writer.WriteStartElement("intineraries", Axf10Namespace);
writer.WriteElementString("item", Axf10Namespace, "one value");
writer.WriteElementString("item", Axf10Namespace, "another value");
writer.WriteEndElement();
}
}
[MessageContract(IsWrapped = false)]
public class OperationResponse
{
[MessageBodyMember(Name = "axf", Namespace = AxfDocument.Axf10Namespace)]
public AxfDocument axf;
}
[ServiceContract]
public interface ITest
{
[OperationContract]
OperationResponse GetAxf();
}
public class Service : ITest
{
public OperationResponse GetAxf()
{
return new OperationResponse { axf = new AxfDocument() };
}
}
public static void Test()
{
Console.WriteLine("Serialization");
MemoryStream ms = new MemoryStream();
XmlSerializer xs = new XmlSerializer(typeof(AxfDocument));
xs.Serialize(ms, new AxfDocument());
Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));
Console.WriteLine();
Console.WriteLine("Service");
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), "");
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new BasicHttpBinding(), new EndpointAddress(baseAddress));
ITest proxy = factory.CreateChannel();
Console.WriteLine(proxy.GetAxf());
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}

How can I programmatically get the binding that my client proxy is using?

I have a WCF proxy generated at runtime with DuplexChannelFactory.
How can I access the binding information given only the service interface returned from DuplexChannelFactory?
I can get most stuff by casting to an IClientChannel, but I can't seem to find binding info on there. The closest I can get is IClientChannel.RemoteAddress which is an endpoint, but that doesn't seem to have binding info either. :-/
You can't (directly). There are a few things which you can get from the channel, such as the message version (channel.GetProperty<MessageVersion>()), and other values. But the binding isn't one of those. The channel is created after the binding is "deconstructed" (i.e., expanded into its binding elements, while each binding element can add one more piece to the channel stack.
If you want to have the binding information in the proxy channel, however, you can add it yourself, using one of the extension properties of the context channel. The code below shows one example of that.
public class StackOverflow_6332575
{
[ServiceContract]
public interface ITest
{
[OperationContract]
int Add(int x, int y);
}
public class Service : ITest
{
public int Add(int x, int y)
{
return x + y;
}
}
static Binding GetBinding()
{
BasicHttpBinding result = new BasicHttpBinding();
return result;
}
class MyExtension : IExtension<IContextChannel>
{
public void Attach(IContextChannel owner)
{
}
public void Detach(IContextChannel owner)
{
}
public Binding Binding { get; set; }
}
static void CallProxy(ITest proxy)
{
Console.WriteLine(proxy.Add(3, 5));
MyExtension extension = ((IClientChannel)proxy).Extensions.Find<MyExtension>();
if (extension != null)
{
Console.WriteLine("Binding: {0}", extension.Binding);
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(ITest), GetBinding(), "");
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<ITest> factory = new ChannelFactory<ITest>(GetBinding(), new EndpointAddress(baseAddress));
ITest proxy = factory.CreateChannel();
((IClientChannel)proxy).Extensions.Add(new MyExtension { Binding = factory.Endpoint.Binding });
CallProxy(proxy);
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}

Generic Service Contract

I need to have a generic Service contract but if I do that I receive this error:
[ServiceContract]
public interface IService<T> where T : MyClass
{
[OperationContract]
void DoWork();
}
The contract name 'x.y' could not be found in the list of contracts implemented by the service 'z.t'.
As long as you use a closed generic for your interface it does work - see below. What you cannot do is to have an open generic as the contract type.
public class StackOverflow_6216858_751090
{
public class MyClass { }
[ServiceContract]
public interface ITest<T> where T : MyClass
{
[OperationContract]
string Echo(string text);
}
public class Service : ITest<MyClass>
{
public string Echo(string text)
{
return text;
}
}
static Binding GetBinding()
{
BasicHttpBinding result = new BasicHttpBinding();
//Change binding settings here
return result;
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(ITest<MyClass>), GetBinding(), "");
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<ITest<MyClass>> factory = new ChannelFactory<ITest<MyClass>>(GetBinding(), new EndpointAddress(baseAddress));
ITest<MyClass> proxy = factory.CreateChannel();
Console.WriteLine(proxy.Echo("Hello"));
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
Your service contract is not interoperable. It's not possible to expose generics like that via WSDL.
Take a look at this article (link) for a possible workaround.
If you use an servicereference on thee client side generic will fail.
Use the following on client side with generic:
var myBinding = new BasicHttpBinding();
var myEndpoint = new EndpointAddress("");
var myChannelFactory = new ChannelFactory<IService>(myBinding, myEndpoint);
IService gks = myChannelFactory.CreateChannel();