WCF service caching all requests - c#

I'm trying to write WCF service in which one method will be catching all requests. Plan to host it within standalone executable. Here is the contract:
[ServiceContract]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple, AddressFilterMode = AddressFilterMode.Any)]
public class Proxy
{
[WebInvoke(UriTemplate = "*", Method = "*")]
public string Test(Stream input)
{
return "Test";
}
}
Here is the hosting code:
static void Main(string[] args)
{
var uri = new Uri("http://localhost:2535/");
var binding = new WebHttpBinding();
var host = new ServiceHost(new Proxy(), uri);
host.AddServiceEndpoint(typeof(Proxy), binding, uri);
host.Open();
Console.ReadKey();
}
But when I'm pointing my browser to the localhost:2535 i just see information about service and fact that metadata is not enabled. And when I getting something like localhost:2535/bla-bla-bla/ error rises:
The message with Action '' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).
I don't understand what I'm missing, to be frankly... Would be very grateful for helping me to get back on right track.
EDIT: Solved by explicitly adding WebHttpBehavior behavior to the endpoint. The resulting code become:
static void Main(string[] args)
{
var uri = new Uri("http://localhost:2535/");
var binding = new WebHttpBinding();
var host = new ServiceHost(new Proxy(), uri);
host.AddServiceEndpoint(typeof(Proxy), binding, uri).Behaviors.Add(new WebHttpBehavior());
host.Open();
Console.ReadKey();
}
I'm still looking for more detailed explanation why it's working that way...

Try add to your Endpoint's behaviour WebHttpBehavior, like this
host.AddServiceEndpoint(typeof(Proxy), binding, uri).Behaviours.Add(new WebHttpBehavior());

It looks a bit odd that your ServiceContract attribute is defined directly on the class that implements your service. Usually you'd define this on the interface that defines the service. Example here:-
MSDN ServiceContractAttribute

To enable metadata exchange you need to add ServiceMetadataBehavior just like that
ServiceMetadataBehavior serviceBehaviour = new ServiceMetadataBehavior() { HttpGetEnabled = true, HttpGetUrl = new Uri(String.Format("{0}/mex", endpointUrl)) };
Host.Description.Behaviors.Add(serviceBehaviour);
And then use localhost:2535/mex to retrieve the service metadata. If it succeeds have a look if your Test method is included in the metadata. If it fails try to configure WCF tracing to get more detailed and user friendly error messages.
Also make sure you marked you method with OperationContract attribute.
Hope it helps.

Related

Programmatically have Client application list a WCF service's Endpoints created during runtime using a URL (like WCF Test Client does)

Maybe I'm not searching with the right terms because this seems pretty simple. I'm just looking for a way to list the endpoints of a WCF service that has it's endpoints created during runtime the same way WCF Test Client Does.
Specify URL
Get MetaData and Endpoints
This is how I add the endpoints during runtime
string SetInstrumentsURL = serviceUrl + "SetInstruments/";
string SetInstrumentsPipe = "net.pipe://localhost/TestService/SetInstruments/";
ServiceHost SetInstrumentsHost = null;
var SetInstruments = InstrumentLoader.Factory.GetIEnumerableOf<ISetInstrument>();
if (SetInstruments.Count() > 0)
{
Uri SetInstrumentsURI = new Uri(SetInstrumentsURL);
Uri SetInstrumentsPipedURI = new Uri(SetInstrumentsPipe);
NetTcpBinding netTcpBindingSetInstruments = new NetTcpBinding();
NetNamedPipeBinding NamedPipeBindingSetInstruments = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
SetInstrumentsHost = new ServiceHost(typeof(TeraSetInstrumentService), new Uri[] { SetInstrumentsURI, SetInstrumentsPipedURI });
ServiceMetadataBehavior SetInstrumentServiceMetadataBehavior = new ServiceMetadataBehavior();
SetInstrumentsHost.Description.Behaviors.Add(SetInstrumentServiceMetadataBehavior);
SetInstrumentsHost.AddServiceEndpoint(typeof(IMetadataExchange),
MetadataExchangeBindings.CreateMexTcpBinding(), "mex");
SetInstrumentsHost.AddServiceEndpoint(typeof(IMetadataExchange),
MetadataExchangeBindings.CreateMexNamedPipeBinding(), "mex");
foreach (var setter in SetInstruments)
{
SetInstrumentsHost.AddServiceEndpoint(typeof(ISetInstrumentService), netTcpBindingSetInstruments, SetInstrumentsURL + setter.Name).Name = "Set_" + setter.Name.Replace(" ", "_");
SetInstrumentsHost.AddServiceEndpoint(typeof(ISetInstrumentService), NamedPipeBindingSetInstruments, SetInstrumentsPipe + setter.Name).Name = "Set_" + setter.Name.Replace(" ", "_");
}
SetInstrumentsHost.Open();
}
What functions can I use from the client side to access those same endpoints as WCF Test Client? I know how to connect to those endpoints if I already have the Endpoint's URL but I would like to have a list of the endpoints so I can create a drop down list choose from that changes depending on what host you connect to.
Adding a service reference through Visual Studio doesn't list all of the endpoints because the are not created yet. Is the a library I can use to get them at run time like WCF Test Client does.
Provided that we have the service metadata URI, we could use MetadataExchangeClientMode and MetadataResolver class which is provided in the System.ServiceModel.Description namespace to retrieve and process the metadata.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-use-metadataresolver-to-obtain-binding-metadata-dynamically
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-use-metadataexchangeclient-to-retrieve-metadata
I have made a simple example, wish it is useful to you.
class Program
{
static void Main(string[] args)
{
Uri uri = new Uri("http://10.157.13.69:3336/mex");
MetadataExchangeClient client = new MetadataExchangeClient(uri, MetadataExchangeClientMode.MetadataExchange);
MetadataSet metadata = client.GetMetadata();
WsdlImporter importer = new WsdlImporter(metadata);
ServiceEndpointCollection endpoints = importer.ImportAllEndpoints();
//ServiceEndpointCollection endpoints = MetadataResolver.Resolve(typeof(IService), uri, MetadataExchangeClientMode.MetadataExchange);
foreach (var item in endpoints)
{
Console.WriteLine(item.Address.Uri);
}
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
string SayHello();
}
Result
Okay it was pretty easy to do, I just needed to use the MetadataExchangeClient to get the config. In the code all I had to do to get the MetaData Xml was this:
var meta = new System.ServiceModel.Description.MetadataExchangeClient(new Uri("net.tcp://10.0.2.124:9000/TeraService/SetInstruments/mex"), System.ServiceModel.Description.MetadataExchangeClientMode.MetadataExchange);
var data = meta.GetMetadata();
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(data.GetType());
TextWriter writer = new StreamWriter("xmlfile.xml");
x.Serialize(writer, data);
writer.Close();
I posted my answer in the wrong place. But Abraham Qian has a more elegant solution that I will test now.

Can't create RESTful streamed WCF service

I'm trying to create a RESTful WCF service, but it uses Streamed transfer because it is transferring large files.
There are many examples online and they all seem to end up with one approach. I just can't get it to work!
I have created this contract interface:
[ServiceContract(Namespace = "http://services.mydomain/MyService", ConfigurationName = "MyService")]
public interface IMyServiceContract
{
[OperationContract]
[WebGet]
Stream GetFile(string someValue, string filepath);
}
And the following implementation of the contract:
public class MyServiceImpl : IMyServiceContract
{
public Stream GetFile(string someValue, string filepath)
{
DoSomethingWith(someValue);
return File.OpenRead(filepath);
}
}
Finally I create the WCF service like this (I can't put anything in app.config because this is started dynamically, so everything must be started programatically without any dependencies on app.config):
private void StartService()
{
MyServiceImpl svcImplementation = new MyServiceImpl();
ServiceHost host = new ServiceHost(svcImplementation, "http://localhost/MyService");
ServiceBehaviorAttribute behaviour = host.Description.Behaviors.Find<ServiceBehaviorAttribute>();
behaviour.InstanceContextMode = InstanceContextMode.Single;
WebHttpBinding binding = new WebHttpBinding
{
TransferMode = TransferMode.Streamed,
MaxReceivedMessageSize = int.MaxValue,
ReaderQuotas = { MaxArrayLength = int.MaxValue }
};
host.AddServiceEndpoint(typeof(IMyServiceContract), binding, "MyService")
.Behaviors.Add(new WebHttpBehavior());
ServiceMetadataBehavior smb = new ServiceMetadataBehavior
{
HttpGetEnabled = true
};
host.Description.Behaviors.Add(smb);
host.Open();
}
Everything compiles, and everything starts without any errors.
When I, in Postman, try to access http://localhost/MyService/GetFile?someValue=foo&filepath=C%3A%5Ctemp%5Csomefile.txt I seem to get a HTTP 405, which says that only POST is allowed (even though I have decorated the method with [WebGet]
If I get Postman to POST instead of GET, I get a HTTP 404.
If I go to http://localhost/MyService in a browser I get the standard WCF service page, so the service is definitely running.
Adding UriTemplate to the [WebGet] attribute has no effect:
[OperationContract]
[WebGet(UriTemplate = "{someValue}/{filepath}")]
Stream GetFile(string someValue, string filepath);
Based on the examples I've found I feel like this should be easy, but I must be missing something essential as I just can't get it to work.
What am I missing?

WCF - AddressFilter mismatch

I have a self-hosted WCF service, and am getting the following exception when calling it:
The message with To 'net.tcp://localhost:53724/Test1' cannot be
processed at the receiver, due to an AddressFilter mismatch at the
EndpointDispatcher. Check that the sender and receiver's
EndpointAddresses agree.
The solution that works is to add [ServiceBehavior(AddressFilterMode = AddressFilterMode.Prefix)] before the service interface's implementation class. But that shouldn't be the case! So, I'm trying to get to the source of the error in order to remove it.
I've found that
When I do add the ServiceBehavior attribute and the call succeeds - the following: OperationContext.Current.EndpointDispatcher.EndpointAddress
Returns: net.tcp://localhost/Test1 - notice the absence of the port. That is actually what I feed the ServiceHost.Open method. But the port is added because I specify ListenUriMode.Unique .
So: How do I fix the error with AddressFilterMode.Exact ?
Code to reproduce:
[ServiceContract]
public interface IWCF1
{
[OperationContract]
bool SendMessage(string message);
}
[ServiceBehavior(AddressFilterMode = AddressFilterMode.Prefix)]
public class WCF1 : IWCF1
{
public bool SendMessage(string message)
{
Debug.WriteLine("Message: " + message);
Debug.WriteLine(OperationContext.Current.EndpointDispatcher.EndpointAddress, "EndpointAddress");//Does not include the port!
return true;
}
}
public void test()
{
Uri uri2 = Service(typeof(WCF1), typeof(IWCF1), "Test1");
IWCF1 iwcf1 = CreateChannel(uri2.ToString());
new Task(() => iwcf1.SendMessage("abc")).Start();
}
public Uri Service(Type class1, Type interface1, string uri)
{
string serviceUri = "net.tcp://localhost/" + uri;
ServiceHost host = new ServiceHost(class1, new Uri(serviceUri));
ServiceEndpoint ep = host.AddServiceEndpoint(interface1, new NetTcpBinding(SecurityMode.None), serviceUri);
ep.ListenUriMode = ListenUriMode.Unique;
host.Open();
return host.ChannelDispatchers[0].Listener.Uri;
}
public static IWCF1 CreateChannel(string address)
{
EndpointAddress ep = new EndpointAddress(address);
ChannelFactory<IWCF1> channelFactory = new ChannelFactory<IWCF1>(new NetTcpBinding(SecurityMode.None), ep);
return channelFactory.CreateChannel();
}
I suspect that the source of the AddressFilterMode.Exact error involves the difference between the logical endpoint and physical service address.
The EndpointAddress is the logical address of a service, which is the address that SOAP messages are addressed to. The ListenUri is the physical address of the service. It has the port and address information where the service endpoint actually listens for messages on the current machine. The logical endpoint address, not the physical service location, is used for by the dispatcher for matching and filtering, thus the reason why an exact match does not find the service.
The Physical Address is not the same as the Logical Address:
net.tcp://localhost:53724/Test1 != net.tcp://localhost/Test1
A few options to consider:
Continue to use AddressFilterMode.Prefix
Try specifying HostNameComparisonMode
Specify the port in advance
References:
https://msdn.microsoft.com/en-us/library/aa395210%28v=vs.110%29.aspx
https://msdn.microsoft.com/en-us/magazine/cc163412.aspx#S4

WCF Application hosted service Faulted State exception?

No idea what's going on. Simple application hosted service. Ran fine on server A. Copied everything over to server B...and suddenly won't launch.
Any tips? Ideas? I'll happily provide more info. Thanks for any help.
Error message:
The communication object, System.ServiceModel.ServiceHost, cannot be
used for communication because it is in the Faulted state.
Code (FAILS AT HOST.OPEN()_
static void Main(string[] args)
{
try
{
Uri baseAddress = new Uri("http://localhost:8080/Brp");
Uri mexUri = new Uri("http://localhost:8080/Brp/mex");
// Create the ServiceHost.
using (ServiceHost host = new ServiceHost(typeof(BBService), baseAddress))
{
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetUrl = mexUri;
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
host.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
BasicHttpBinding binding = new BasicHttpBinding();
binding.MaxReceivedMessageSize = int.MaxValue;
binding.Security.Mode = BasicHttpSecurityMode.None;
host.AddServiceEndpoint(typeof(IBService), binding, "");
// Enable metadata publishing.
var behavior = host.Description.Behaviors.Find<ServiceDebugBehavior>();
behavior.IncludeExceptionDetailInFaults = true;
host.Open();
Console.ReadLine();
// Close the ServiceHost.
host.Close();
}
} catch (Exception excep)
{
writeMessage("EXCEPTION!!! - " + excep.Message);
}
In case anyone else runs into this do: Right-click -> Run as administrator
You have to follow certain rules while working with Message contract
1. When using Message contract type as parameter, Only one parameter can be used in servicie Operation
[OperationContract]
void SaveEmployeeDetails(EmployeeDetails emp);
2. Service operation either should return Messagecontract type or it should not return any value
[OperationContract]
EmployeeDetails GetEmployeeDetails();
3. Service operation will accept and return only message contract type. Other data types are not allowed.
[OperationContract]
EmployeeDetails ModifyEmployeeDetails(EmployeeDetails emp);
Note: If a type has both Message and Data contract, service operation will accept only message contract.

MessageSecurityException: The security header element 'Timestamp' with the '' id must be signed

I'm asking the same question here that I've already asked on msdn forums http://social.msdn.microsoft.com/Forums/en-US/netfxnetcom/thread/70f40a4c-8399-4629-9bfc-146524334daf
I'm consuming a (most likely Java based) Web Service with I have absolutely no access to modify. It won't be modified even though I would ask them (it's a nation wide system).
I've written the client with WCF. Here's some code:
CustomBinding binding = new CustomBinding();
AsymmetricSecurityBindingElement element = SecurityBindingElement.CreateMutualCertificateDuplexBindingElement(MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10);
element.AllowSerializedSigningTokenOnReply = true;
element.SetKeyDerivation(false);
element.IncludeTimestamp = true;
element.KeyEntropyMode = SecurityKeyEntropyMode.ClientEntropy;
element.MessageProtectionOrder = System.ServiceModel.Security.MessageProtectionOrder.SignBeforeEncrypt;
element.LocalClientSettings.IdentityVerifier = new CustomIdentityVerifier();
element.SecurityHeaderLayout = SecurityHeaderLayout.Lax;
element.IncludeTimestamp = false;
binding.Elements.Add(element);
binding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8));
binding.Elements.Add(new HttpsTransportBindingElement());
EndpointAddress address = new EndpointAddress(new Uri("url"));
ChannelFactory<MyPortTypeChannel> factory = new ChannelFactory<MyPortTypeChannel>(binding, address);
ClientCredentials credentials = factory.Endpoint.Behaviors.Find<ClientCredentials>();
credentials.ClientCertificate.Certificate = myClientCert;
credentials.ServiceCertificate.DefaultCertificate = myServiceCert;
credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
service = factory.CreateChannel();
After this every request done to the service fails in client side (I can confirm my request is accepted by the service and a sane response is being returned)
I always get the following exception
MessageSecurityException: The security
header element 'Timestamp' with the ''
id must be signed.
By looking at trace I can see that in the response there really is a timestamp element, but in the security section there is only a signature for body.
Can I somehow make WCF to ingore the fact Timestamp isn't signed?
You could try using a WCF Message contract. When you have a Message contract you can specify that the items in the header should be signed:
[MessageContract]
public class CustomType
{
[MessageHeader(ProtectionLevel = ProtectionLevel.Sign)]
string name;
[MessageHeader(ProtectionLevel = ProtectionLevel.EncryptAndSign)]
string secret;
I got this sorted out, the answer can be found in here:
http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/371184de-5c05-4c70-8899-13536d8f5d16
Main points: add a custom StrippingChannel to the custombinding to strip off the timestamp from the securityheader and configure WCF to not detect replies.

Categories