Can't create RESTful streamed WCF service - c#

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?

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.

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

The message with To '...' cannot be processed at the receiver, due to an AddressFilter mismatch at the EndpointDispatcher

I'm trying to implement a WCF Rest service without modifying the App.config file.
My Service Interface looks like so:
[ServiceContract]
public interface IService
{
[OperationContract]
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "/teststr/?string={aStr}")]
string TestString(string aStr);
}
The Service implementation is very basic:
public class TestService : IService
{
public string TestString(string aStr = null)
{
Console.WriteLine("The Test() method was called at {0}"
+ "\n\rThe given string was {1}\n\r"
, DateTime.Now.ToString("H:mm:ss")
, aStr);
return aStr;
}
}
And my main Program that runs everything:
// Step 1 Create a URI to serve as the base address.
Uri baseAddress = new Uri("http://localhost:8000/ServiceSample/");
// Step 2 Create a ServiceHost instance
ServiceHost selfHost = new ServiceHost(typeof(TestService), baseAddress);
try
{
// Step 3 Add a service endpoint.
selfHost.AddServiceEndpoint(typeof(IService), new WebHttpBinding(),
"TestService");
// Step 4 Enable metadata exchange.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
selfHost.Description.Behaviors.Add(smb);
// Step 5 Start the service.
selfHost.Open();
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.ReadLine();
// Close the ServiceHostBase to shutdown the service.
selfHost.Close();
}
catch (CommunicationException ce)
{
Console.WriteLine("An exception occurred: {0}", ce.Message);
selfHost.Abort();
}
When I run this and enter the following url:
http://localhost:8000/ServiceSample/TestService/teststr/?string=thisisatest
I get this message:
// The message with To '...' cannot be processed at the receiver, due to an
// AddressFilter mismatch at the EndpointDispatcher. Check that the sender
// and receiver's EndpointAddresses agree.
I've looked at similar SO questions suggesting I add the <webHttp/> behavour (but isn't Step 4 doing that already?), others said adding [ServiceBehavior..]. None of these worked and I didnt find related SO questions useful.
Do I need to modify the App.config file? What am I doing wrong here?
I realize that there is an answer in the comments above, but I'll try to state what I learned when trying to find a solution to my problem, since this is the first page that comes up in Google by searching the error message.
I was trying to call a 3rd Party SOAP Web Service from JavaScript (terrible I know), and I was running into this issue.
The SOAP 1.2 service I was calling required that I put a :To SOAP Addressing header in the <soap:Header> of the <soap:Envelope>. After doing this as well as adding the :Action that I got from WSDL, I was golden.
What the SOAP Header eventually looked like:
<soap:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsa:To>(WebService URL)</wsa:To>
<wsa:Action>(Action from WebService WSDL)</wsa:Action>
</soap:Header>
Helpful sites:
http://www.w3.org/TR/soap12-part1/
http://www.w3.org/TR/ws-addr-soap/
http://javiercrespoalvez.com/2010/09/using-soapui-to-consume-ws-addressing.html
I had this issue and tried numerous fixes and eventually solved by using the WebServiceHost instead of the ServiceHost
Example:
var selfHost = new WebServiceHost(typeof(TestService), baseAddress);

WCF service caching all requests

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.

WCF "Content Type was not supported" error

When I am trying to connect to my WCFService the following error occurred.
Content Type text/xml; charset=utf-8 was not supported by service
http://localhost:1978/Service1.svc. The client and service bindings
may be mismatched.
My service code is :
namespace WcfService1
{
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData(string fName, string lName);
}
}
And in the client form I am calling this service as follows:
endPointAddr = "http://localhost:1978/Service1.svc";
BasicHttpBinding httpBinding = new BasicHttpBinding();
httpBinding.TransferMode = TransferMode.Buffered;
EndpointAddress endpointAddress = new EndpointAddress(endPointAddr);
Append("Attempt to connect to: " + endPointAddr);
IService1 proxy = ChannelFactory<IService1>.CreateChannel(httpBinding, endpointAddress);
using (proxy as IDisposable)
{
string strNew=proxy.GetData(textBox2.Text, textBox1.Text) ;
}
I am stuck on that error, if anybody knows please help.
I suspect that your WCF service has a binding of WSHttpBinding or similar - you need to change the client binding (which is currently using BasicHttpBinding) accordingly to make it work...

Categories